1 /*
2  * winMain.c --
3  *
4  *	Provides a default version of the main program and Tcl_AppInit
5  *	procedure for wish and other Tk-based applications.
6  *
7  * Copyright (c) 1993 The Regents of the University of California.
8  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
9  * Copyright (c) 1998-1999 Scriptics Corporation.
10  *
11  * See the file "license.terms" for information on usage and redistribution of
12  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13  */
14 
15 #include "tk.h"
16 #define WIN32_LEAN_AND_MEAN
17 #include <windows.h>
18 #undef WIN32_LEAN_AND_MEAN
19 #include <locale.h>
20 #include <stdlib.h>
21 #include <tchar.h>
22 #if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7
23 #   define Tcl_LibraryInitProc Tcl_PackageInitProc
24 #   define Tcl_StaticLibrary Tcl_StaticPackage
25 #endif
26 
27 #if defined(__GNUC__)
28 int _CRT_glob = 0;
29 #endif /* __GNUC__ */
30 
31 #ifdef TK_TEST
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35 extern Tcl_LibraryInitProc Tktest_Init;
36 #endif /* TK_TEST */
37 
38 #if !defined(TCL_USE_STATIC_PACKAGES)
39 #   if TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 6
40 #	define TCL_USE_STATIC_PACKAGES 1
41 #   else
42 #	define TCL_USE_STATIC_PACKAGES 0
43 #   endif
44 #endif
45 
46 #if defined(STATIC_BUILD) && TCL_USE_STATIC_PACKAGES
47 extern Tcl_LibraryInitProc Registry_Init;
48 extern Tcl_LibraryInitProc Dde_Init;
49 extern Tcl_LibraryInitProc Dde_SafeInit;
50 #endif
51 
52 #ifdef __cplusplus
53 }
54 #endif
55 #ifdef TCL_BROKEN_MAINARGS
56 static void setargv(int *argcPtr, TCHAR ***argvPtr);
57 #endif
58 
59 /*
60  * Forward declarations for procedures defined later in this file:
61  */
62 
63 static BOOL consoleRequired = TRUE;
64 
65 /*
66  * The following #if block allows you to change the AppInit function by using
67  * a #define of TCL_LOCAL_APPINIT instead of rewriting this entire file. The
68  * #if checks for that #define and uses Tcl_AppInit if it doesn't exist.
69  */
70 
71 #ifndef TK_LOCAL_APPINIT
72 #define TK_LOCAL_APPINIT Tcl_AppInit
73 #endif
74 #ifndef MODULE_SCOPE
75 #   ifdef __cplusplus
76 #	define MODULE_SCOPE extern "C"
77 #   else
78 #	define MODULE_SCOPE extern
79 #   endif
80 #endif
81 MODULE_SCOPE int TK_LOCAL_APPINIT(Tcl_Interp *interp);
82 
83 /*
84  * The following #if block allows you to change how Tcl finds the startup
85  * script, prime the library or encoding paths, fiddle with the argv, etc.,
86  * without needing to rewrite Tk_Main()
87  */
88 
89 #ifdef TK_LOCAL_MAIN_HOOK
90 MODULE_SCOPE int TK_LOCAL_MAIN_HOOK(int *argc, TCHAR ***argv);
91 #endif
92 
93 /* Make sure the stubbed variants of those are never used. */
94 #undef Tcl_ObjSetVar2
95 #undef Tcl_NewStringObj
96 
97 /*
98  *----------------------------------------------------------------------
99  *
100  * _tWinMain --
101  *
102  *	Main entry point from Windows.
103  *
104  * Results:
105  *	Returns false if initialization fails, otherwise it never returns.
106  *
107  * Side effects:
108  *	Just about anything, since from here we call arbitrary Tcl code.
109  *
110  *----------------------------------------------------------------------
111  */
112 
113 int APIENTRY
114 #ifdef TCL_BROKEN_MAINARGS
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpszCmdLine,int nCmdShow)115 WinMain(
116     HINSTANCE hInstance,
117     HINSTANCE hPrevInstance,
118     LPSTR lpszCmdLine,
119     int nCmdShow)
120 #else
121 _tWinMain(
122     HINSTANCE hInstance,
123     HINSTANCE hPrevInstance,
124     LPTSTR lpszCmdLine,
125     int nCmdShow)
126 #endif
127 {
128     TCHAR **argv;
129     int argc;
130     TCHAR *p;
131     (void)hInstance;
132     (void)hPrevInstance;
133     (void)lpszCmdLine;
134     (void)nCmdShow;
135 
136     /*
137      * Create the console channels and install them as the standard channels.
138      * All I/O will be discarded until Tk_CreateConsoleWindow is called to
139      * attach the console to a text widget.
140      */
141 
142     consoleRequired = TRUE;
143 
144     /*
145      * Set up the default locale to be standard "C" locale so parsing is
146      * performed correctly.
147      */
148 
149     setlocale(LC_ALL, "C");
150 
151     /*
152      * Get our args from the c-runtime. Ignore lpszCmdLine.
153      */
154 
155 #if defined(TCL_BROKEN_MAINARGS)
156     setargv(&argc, &argv);
157 #else
158     argc = __argc;
159     argv = __targv;
160 #endif
161 
162     /*
163      * Forward slashes substituted for backslashes.
164      */
165 
166     for (p = argv[0]; *p != '\0'; p++) {
167 	if (*p == '\\') {
168 	    *p = '/';
169 	}
170     }
171 
172 #ifdef TK_LOCAL_MAIN_HOOK
173     TK_LOCAL_MAIN_HOOK(&argc, &argv);
174 #endif
175 
176     Tk_Main(argc, argv, TK_LOCAL_APPINIT);
177     return 0;			/* Needed only to prevent compiler warning. */
178 }
179 
180 /*
181  *----------------------------------------------------------------------
182  *
183  * Tcl_AppInit --
184  *
185  *	This procedure performs application-specific initialization. Most
186  *	applications, especially those that incorporate additional packages,
187  *	will have their own version of this procedure.
188  *
189  * Results:
190  *	Returns a standard Tcl completion code, and leaves an error message in
191  *	the interp's result if an error occurs.
192  *
193  * Side effects:
194  *	Depends on the startup script.
195  *
196  *----------------------------------------------------------------------
197  */
198 
199 int
Tcl_AppInit(Tcl_Interp * interp)200 Tcl_AppInit(
201     Tcl_Interp *interp)		/* Interpreter for application. */
202 {
203     if ((Tcl_Init)(interp) == TCL_ERROR) {
204 	return TCL_ERROR;
205     }
206 #if defined(STATIC_BUILD) && TCL_USE_STATIC_PACKAGES
207     if (Registry_Init(interp) == TCL_ERROR) {
208 	return TCL_ERROR;
209     }
210     Tcl_StaticLibrary(interp, "Registry", Registry_Init, 0);
211 
212     if (Dde_Init(interp) == TCL_ERROR) {
213 	return TCL_ERROR;
214     }
215     Tcl_StaticLibrary(interp, "Dde", Dde_Init, Dde_SafeInit);
216 #endif
217     if (Tk_Init(interp) == TCL_ERROR) {
218 	return TCL_ERROR;
219     }
220     Tcl_StaticLibrary(interp, "Tk", Tk_Init, Tk_SafeInit);
221 
222     /*
223      * Initialize the console only if we are running as an interactive
224      * application.
225      */
226 
227     if (consoleRequired) {
228 	if (Tk_CreateConsoleWindow(interp) == TCL_ERROR) {
229 	    return TCL_ERROR;
230 	}
231     }
232 #ifdef TK_TEST
233     if (Tktest_Init(interp) == TCL_ERROR) {
234 	return TCL_ERROR;
235     }
236     Tcl_StaticLibrary(interp, "Tktest", Tktest_Init, 0);
237 #endif /* TK_TEST */
238 
239     /*
240      * Call the init procedures for included packages. Each call should look
241      * like this:
242      *
243      * if (Mod_Init(interp) == TCL_ERROR) {
244      *     return TCL_ERROR;
245      * }
246      *
247      * where "Mod" is the name of the module. (Dynamically-loadable packages
248      * should have the same entry-point name.)
249      */
250 
251     /*
252      * Call Tcl_CreateObjCommand for application-specific commands, if they
253      * weren't already created by the init procedures called above.
254      */
255 
256     /*
257      * Specify a user-specific startup file to invoke if the application is
258      * run interactively. Typically the startup file is "~/.apprc" where "app"
259      * is the name of the application. If this line is deleted then no user-
260      * specific startup file will be run under any conditions.
261      */
262 
263     Tcl_ObjSetVar2(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL,
264 	    Tcl_NewStringObj("~/wishrc.tcl", -1), TCL_GLOBAL_ONLY);
265     return TCL_OK;
266 }
267 
268 #if defined(TK_TEST)
269 /*
270  *----------------------------------------------------------------------
271  *
272  * _tmain --
273  *
274  *	Main entry point from the console.
275  *
276  * Results:
277  *	None: Tk_Main never returns here, so this procedure never returns
278  *	either.
279  *
280  * Side effects:
281  *	Whatever the applications does.
282  *
283  *----------------------------------------------------------------------
284  */
285 
286 #ifdef TCL_BROKEN_MAINARGS
287 int
main(int argc,char ** dummy)288 main(
289     int argc,
290     char **dummy)
291 {
292     TCHAR **argv;
293     (void)dummy;
294 #else
295 int
296 _tmain(
297     int argc,
298     TCHAR **argv)
299 {
300 #endif
301     /*
302      * Set up the default locale to be standard "C" locale so parsing is
303      * performed correctly.
304      */
305 
306     setlocale(LC_ALL, "C");
307 
308 #ifdef TCL_BROKEN_MAINARGS
309     /*
310      * Get our args from the c-runtime. Ignore argc/argv.
311      */
312 
313     setargv(&argc, &argv);
314 #endif
315     /*
316      * Console emulation widget not required as this entry is from the
317      * console subsystem, thus stdin,out,err already have end-points.
318      */
319 
320     consoleRequired = FALSE;
321 
322 #ifdef TK_LOCAL_MAIN_HOOK
323     TK_LOCAL_MAIN_HOOK(&argc, &argv);
324 #endif
325 
326     Tk_Main(argc, argv, Tcl_AppInit);
327     return 0;
328 }
329 #endif /* !__GNUC__ || TK_TEST */
330 
331 
332 /*
333  *-------------------------------------------------------------------------
334  *
335  * setargv --
336  *
337  *	Parse the Windows command line string into argc/argv. Done here
338  *	because we don't trust the builtin argument parser in crt0. Windows
339  *	applications are responsible for breaking their command line into
340  *	arguments.
341  *
342  *	2N backslashes + quote -> N backslashes + begin quoted string
343  *	2N + 1 backslashes + quote -> literal
344  *	N backslashes + non-quote -> literal
345  *	quote + quote in a quoted string -> single quote
346  *	quote + quote not in quoted string -> empty string
347  *	quote -> begin quoted string
348  *
349  * Results:
350  *	Fills argcPtr with the number of arguments and argvPtr with the array
351  *	of arguments.
352  *
353  * Side effects:
354  *	Memory allocated.
355  *
356  *--------------------------------------------------------------------------
357  */
358 
359 #ifdef TCL_BROKEN_MAINARGS
360 static void
361 setargv(
362     int *argcPtr,		/* Filled with number of argument strings. */
363     TCHAR ***argvPtr)		/* Filled with argument strings (malloc'd). */
364 {
365     TCHAR *cmdLine, *p, *arg, *argSpace;
366     TCHAR **argv;
367     int argc, size, inquote, copy, slashes;
368 
369     cmdLine = GetCommandLine();
370 
371     /*
372      * Precompute an overly pessimistic guess at the number of arguments in
373      * the command line by counting non-space spans.
374      */
375 
376     size = 2;
377     for (p = cmdLine; *p != '\0'; p++) {
378 	if ((*p == ' ') || (*p == '\t')) {	/* INTL: ISO space. */
379 	    size++;
380 	    while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */
381 		p++;
382 	    }
383 	    if (*p == '\0') {
384 		break;
385 	    }
386 	}
387     }
388 
389     /* Make sure we don't call ckalloc through the (not yet initialized) stub table */
390     #undef Tcl_Alloc
391     #undef Tcl_DbCkalloc
392 
393     argSpace = (TCHAR *)ckalloc(size * sizeof(char *)
394 	    + (_tcslen(cmdLine) * sizeof(TCHAR)) + sizeof(TCHAR));
395     argv = (TCHAR **) argSpace;
396     argSpace += size * (sizeof(char *)/sizeof(TCHAR));
397     size--;
398 
399     p = cmdLine;
400     for (argc = 0; argc < size; argc++) {
401 	argv[argc] = arg = argSpace;
402 	while ((*p == ' ') || (*p == '\t')) {	/* INTL: ISO space. */
403 	    p++;
404 	}
405 	if (*p == '\0') {
406 	    break;
407 	}
408 
409 	inquote = 0;
410 	slashes = 0;
411 	while (1) {
412 	    copy = 1;
413 	    while (*p == '\\') {
414 		slashes++;
415 		p++;
416 	    }
417 	    if (*p == '"') {
418 		if ((slashes & 1) == 0) {
419 		    copy = 0;
420 		    if ((inquote) && (p[1] == '"')) {
421 			p++;
422 			copy = 1;
423 		    } else {
424 			inquote = !inquote;
425 		    }
426 		}
427 		slashes >>= 1;
428 	    }
429 
430 	    while (slashes) {
431 		*arg = '\\';
432 		arg++;
433 		slashes--;
434 	    }
435 
436 	    if ((*p == '\0') || (!inquote &&
437 		    ((*p == ' ') || (*p == '\t')))) {	/* INTL: ISO space. */
438 		break;
439 	    }
440 	    if (copy != 0) {
441 		*arg = *p;
442 		arg++;
443 	    }
444 	    p++;
445 	}
446 	*arg = '\0';
447 	argSpace = arg + 1;
448     }
449     argv[argc] = NULL;
450 
451     *argcPtr = argc;
452     *argvPtr = argv;
453 }
454 #endif /* TCL_BROKEN_MAINARGS */
455 
456 /*
457  * Local Variables:
458  * mode: c
459  * c-basic-offset: 4
460  * fill-column: 78
461  * End:
462  */
463