1/* 2 * winMain.c -- 3 * 4 * Main entry point for wish and other Tk-based applications. 5 * 6 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 7 * Copyright (c) 1998-1999 by Scriptics Corporation. 8 * 9 * See the file "license.terms" for information on usage and redistribution 10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: winMain.c,v 1.15.2.2 2003/12/12 00:42:10 davygrvy Exp $ 13 */ 14 15#include <tk.h> 16#define WIN32_LEAN_AND_MEAN 17#include <windows.h> 18#undef WIN32_LEAN_AND_MEAN 19#include <malloc.h> 20#include <locale.h> 21 22#include "tkInt.h" 23 24/* 25 * The following declarations refer to internal Tk routines. These 26 * interfaces are available for use, but are not supported. 27 */ 28 29 30/* 31 * Forward declarations for procedures defined later in this file: 32 */ 33 34static void setargv _ANSI_ARGS_((int *argcPtr, char ***argvPtr)); 35static Tcl_PanicProc WishPanic; 36 37#ifdef TK_TEST 38extern int Tktest_Init(Tcl_Interp *interp); 39#endif /* TK_TEST */ 40 41static BOOL consoleRequired = TRUE; 42 43/* 44 * The following #if block allows you to change the AppInit 45 * function by using a #define of TCL_LOCAL_APPINIT instead 46 * of rewriting this entire file. The #if checks for that 47 * #define and uses Tcl_AppInit if it doesn't exist. 48 */ 49 50#ifndef TK_LOCAL_APPINIT 51#define TK_LOCAL_APPINIT Tcl_AppInit 52#endif 53extern int TK_LOCAL_APPINIT _ANSI_ARGS_((Tcl_Interp *interp)); 54 55/* 56 * The following #if block allows you to change how Tcl finds the startup 57 * script, prime the library or encoding paths, fiddle with the argv, 58 * etc., without needing to rewrite Tk_Main() 59 */ 60 61#ifdef TK_LOCAL_MAIN_HOOK 62extern int TK_LOCAL_MAIN_HOOK _ANSI_ARGS_((int *argc, char ***argv)); 63#endif 64 65 66/* 67 *---------------------------------------------------------------------- 68 * 69 * WinMain -- 70 * 71 * Main entry point from Windows. 72 * 73 * Results: 74 * Returns false if initialization fails, otherwise it never 75 * returns. 76 * 77 * Side effects: 78 * Just about anything, since from here we call arbitrary Tcl code. 79 * 80 *---------------------------------------------------------------------- 81 */ 82 83int APIENTRY 84WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow) 85 HINSTANCE hInstance; 86 HINSTANCE hPrevInstance; 87 LPSTR lpszCmdLine; 88 int nCmdShow; 89{ 90 char **argv; 91 int argc; 92 char buffer[MAX_PATH+1]; 93 char *p; 94 95 Tcl_SetPanicProc(WishPanic); 96 97 /* 98 * Create the console channels and install them as the standard 99 * channels. All I/O will be discarded until Tk_CreateConsoleWindow is 100 * called to attach the console to a text widget. 101 */ 102 103 consoleRequired = TRUE; 104 105 /* 106 * Set up the default locale to be standard "C" locale so parsing 107 * is performed correctly. 108 */ 109 110 setlocale(LC_ALL, "C"); 111 setargv(&argc, &argv); 112 113 /* 114 * Replace argv[0] with full pathname of executable, and forward 115 * slashes substituted for backslashes. 116 */ 117 118 GetModuleFileName(NULL, buffer, sizeof(buffer)); 119 argv[0] = buffer; 120 for (p = buffer; *p != '\0'; p++) { 121 if (*p == '\\') { 122 *p = '/'; 123 } 124 } 125 126#ifdef TK_LOCAL_MAIN_HOOK 127 TK_LOCAL_MAIN_HOOK(&argc, &argv); 128#endif 129 130 Tk_Main(argc, argv, TK_LOCAL_APPINIT); 131 return 1; 132} 133 134 135/* 136 *---------------------------------------------------------------------- 137 * 138 * Tcl_AppInit -- 139 * 140 * This procedure performs application-specific initialization. 141 * Most applications, especially those that incorporate additional 142 * packages, will have their own version of this procedure. 143 * 144 * Results: 145 * Returns a standard Tcl completion code, and leaves an error 146 * message in the interp's result if an error occurs. 147 * 148 * Side effects: 149 * Depends on the startup script. 150 * 151 *---------------------------------------------------------------------- 152 */ 153 154int 155Tcl_AppInit(interp) 156 Tcl_Interp *interp; /* Interpreter for application. */ 157{ 158 if (Tcl_Init(interp) == TCL_ERROR) { 159 goto error; 160 } 161 if (Tk_Init(interp) == TCL_ERROR) { 162 goto error; 163 } 164 Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit); 165 166 /* 167 * Initialize the console only if we are running as an interactive 168 * application. 169 */ 170 171 if (consoleRequired) { 172 if (Tk_CreateConsoleWindow(interp) == TCL_ERROR) { 173 goto error; 174 } 175 } 176#if defined(STATIC_BUILD) && defined(TCL_USE_STATIC_PACKAGES) 177 { 178 extern Tcl_PackageInitProc Registry_Init; 179 extern Tcl_PackageInitProc Dde_Init; 180 181 if (Registry_Init(interp) == TCL_ERROR) { 182 return TCL_ERROR; 183 } 184 Tcl_StaticPackage(interp, "registry", Registry_Init, NULL); 185 186 if (Dde_Init(interp) == TCL_ERROR) { 187 return TCL_ERROR; 188 } 189 Tcl_StaticPackage(interp, "dde", Dde_Init, NULL); 190 } 191#endif 192 193#ifdef TK_TEST 194 if (Tktest_Init(interp) == TCL_ERROR) { 195 goto error; 196 } 197 Tcl_StaticPackage(interp, "Tktest", Tktest_Init, 198 (Tcl_PackageInitProc *) NULL); 199#endif /* TK_TEST */ 200 201 Tcl_SetVar(interp, "tcl_rcFileName", "~/wishrc.tcl", TCL_GLOBAL_ONLY); 202 return TCL_OK; 203 204error: 205 MessageBeep(MB_ICONEXCLAMATION); 206 MessageBox(NULL, Tcl_GetStringResult(interp), "Error in Wish", 207 MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); 208 ExitProcess(1); 209 /* we won't reach this, but we need the return */ 210 return TCL_ERROR; 211} 212 213/* 214 *---------------------------------------------------------------------- 215 * 216 * WishPanic -- 217 * 218 * Display a message and exit. 219 * 220 * Results: 221 * None. 222 * 223 * Side effects: 224 * Exits the program. 225 * 226 *---------------------------------------------------------------------- 227 */ 228 229void 230WishPanic TCL_VARARGS_DEF(CONST char *,arg1) 231{ 232 va_list argList; 233 char buf[1024]; 234 CONST char *format; 235 236 format = TCL_VARARGS_START(CONST char *,arg1,argList); 237 vsprintf(buf, format, argList); 238 239 MessageBeep(MB_ICONEXCLAMATION); 240 MessageBox(NULL, buf, "Fatal Error in Wish", 241 MB_ICONSTOP | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); 242#ifdef _MSC_VER 243 DebugBreak(); 244#endif 245 ExitProcess(1); 246} 247/* 248 *------------------------------------------------------------------------- 249 * 250 * setargv -- 251 * 252 * Parse the Windows command line string into argc/argv. Done here 253 * because we don't trust the builtin argument parser in crt0. 254 * Windows applications are responsible for breaking their command 255 * line into arguments. 256 * 257 * 2N backslashes + quote -> N backslashes + begin quoted string 258 * 2N + 1 backslashes + quote -> literal 259 * N backslashes + non-quote -> literal 260 * quote + quote in a quoted string -> single quote 261 * quote + quote not in quoted string -> empty string 262 * quote -> begin quoted string 263 * 264 * Results: 265 * Fills argcPtr with the number of arguments and argvPtr with the 266 * array of arguments. 267 * 268 * Side effects: 269 * Memory allocated. 270 * 271 *-------------------------------------------------------------------------- 272 */ 273 274static void 275setargv(argcPtr, argvPtr) 276 int *argcPtr; /* Filled with number of argument strings. */ 277 char ***argvPtr; /* Filled with argument strings (malloc'd). */ 278{ 279 char *cmdLine, *p, *arg, *argSpace; 280 char **argv; 281 int argc, size, inquote, copy, slashes; 282 283 cmdLine = GetCommandLine(); /* INTL: BUG */ 284 285 /* 286 * Precompute an overly pessimistic guess at the number of arguments 287 * in the command line by counting non-space spans. 288 */ 289 290 size = 2; 291 for (p = cmdLine; *p != '\0'; p++) { 292 if ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */ 293 size++; 294 while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */ 295 p++; 296 } 297 if (*p == '\0') { 298 break; 299 } 300 } 301 } 302 argSpace = (char *) Tcl_Alloc( 303 (unsigned) (size * sizeof(char *) + strlen(cmdLine) + 1)); 304 argv = (char **) argSpace; 305 argSpace += size * sizeof(char *); 306 size--; 307 308 p = cmdLine; 309 for (argc = 0; argc < size; argc++) { 310 argv[argc] = arg = argSpace; 311 while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */ 312 p++; 313 } 314 if (*p == '\0') { 315 break; 316 } 317 318 inquote = 0; 319 slashes = 0; 320 while (1) { 321 copy = 1; 322 while (*p == '\\') { 323 slashes++; 324 p++; 325 } 326 if (*p == '"') { 327 if ((slashes & 1) == 0) { 328 copy = 0; 329 if ((inquote) && (p[1] == '"')) { 330 p++; 331 copy = 1; 332 } else { 333 inquote = !inquote; 334 } 335 } 336 slashes >>= 1; 337 } 338 339 while (slashes) { 340 *arg = '\\'; 341 arg++; 342 slashes--; 343 } 344 345 if ((*p == '\0') 346 || (!inquote && ((*p == ' ') || (*p == '\t')))) { /* INTL: ISO space. */ 347 break; 348 } 349 if (copy != 0) { 350 *arg = *p; 351 arg++; 352 } 353 p++; 354 } 355 *arg = '\0'; 356 argSpace = arg + 1; 357 } 358 argv[argc] = NULL; 359 360 *argcPtr = argc; 361 *argvPtr = argv; 362} 363 364#if !defined(__GNUC__) || defined(TK_TEST) 365/* 366 *---------------------------------------------------------------------- 367 * 368 * main -- 369 * 370 * Main entry point from the console. 371 * 372 * Results: 373 * None: Tk_Main never returns here, so this procedure never 374 * returns either. 375 * 376 * Side effects: 377 * Whatever the applications does. 378 * 379 *---------------------------------------------------------------------- 380 */ 381 382int main(int argc, char **argv) 383{ 384 Tcl_SetPanicProc(WishPanic); 385 386 /* 387 * Set up the default locale to be standard "C" locale so parsing 388 * is performed correctly. 389 */ 390 391 setlocale(LC_ALL, "C"); 392 393 /* 394 * Create the console channels and install them as the standard 395 * channels. All I/O will be discarded until Tk_CreateConsoleWindow is 396 * called to attach the console to a text widget. 397 */ 398 399 consoleRequired = FALSE; 400 401 Tk_Main(argc, argv, Tcl_AppInit); 402 return 0; 403} 404#endif /* !__GNUC__ || TK_TEST */ 405