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