Reimplemented "relay=" and "snoop=" suboptions.
[wine] / misc / options.c
1 /*
2  * Option parsing
3  *
4  * Copyright 2000 Alexandre Julliard
5  */
6
7 #include "config.h"
8 #include <string.h>
9 #include <stdlib.h>
10
11 #include "winbase.h"
12 #include "winnls.h"
13 #include "ntddk.h"
14 #include "wine/library.h"
15 #include "options.h"
16 #include "version.h"
17 #include "debugtools.h"
18
19 struct option
20 {
21     const char *longname;
22     char        shortname;
23     int         has_arg;
24     int         inherit;
25     void      (*func)( const char *arg );
26     const char *usage;
27 };
28
29 /* default options */
30 struct options Options =
31 {
32     NULL,           /* desktopGeometry */
33     NULL,           /* display */
34     NULL,           /* dllFlags */
35     FALSE,          /* synchronous */
36     FALSE,          /* Managed windows */
37     NULL            /* Alternate config file name */
38 };
39
40 const char *argv0;       /* the original argv[0] */
41 const char *full_argv0;  /* the full path of argv[0] (if known) */
42
43 static char *inherit_str;  /* options to pass to child processes */
44
45 static int app_argc;       /* argc/argv to pass to application */
46 static char **app_argv;
47 static WCHAR **app_wargv;
48
49 static void out_of_memory(void) WINE_NORETURN;
50 static void out_of_memory(void)
51 {
52     MESSAGE( "Virtual memory exhausted\n" );
53     ExitProcess(1);
54 }
55
56 static char *xstrdup( const char *str )
57 {
58     char *ret = strdup( str );
59     if (!ret) out_of_memory();
60     return ret;
61 }
62
63 static void do_config( const char *arg );
64 static void do_debugmsg( const char *arg );
65 static void do_desktop( const char *arg );
66 static void do_display( const char *arg );
67 static void do_dll( const char *arg );
68 static void do_help( const char *arg );
69 static void do_language( const char *arg );
70 static void do_managed( const char *arg );
71 static void do_synchronous( const char *arg );
72 static void do_version( const char *arg );
73
74 static const struct option option_table[] =
75 {
76     { "config",       0, 1, 0, do_config,
77       "--config name    Specify config file to use" },
78     { "debugmsg",     0, 1, 1, do_debugmsg,
79       "--debugmsg name  Turn debugging-messages on or off" },
80     { "desktop",      0, 1, 1, do_desktop,
81       "--desktop geom   Use a desktop window of the given geometry" },
82     { "display",      0, 1, 0, do_display,
83       "--display name   Use the specified display" },
84     { "dll",          0, 1, 1, do_dll,
85       "--dll name       Enable or disable built-in DLLs" },
86     { "dosver",       0, 1, 1, VERSION_ParseDosVersion,
87       "--dosver x.xx    DOS version to imitate (e.g. 6.22)\n"
88       "                    Only valid with --winver win31" },
89     { "help",       'h', 0, 0, do_help,
90       "--help,-h        Show this help message" },
91     { "language",     0, 1, 1, do_language,
92       "--language xx    Set the language (one of Br,Ca,Cs,Cy,Da,De,En,Eo,Es,Fi,Fr,Ga,Gd,Gv,\n"
93       "                    He,Hr,Hu,It,Ja,Ko,Kw,Nl,No,Pl,Pt,Sk,Sv,Ru,Wa)" },
94     { "managed",      0, 0, 0, do_managed,
95       "--managed        Allow the window manager to manage created windows" },
96     { "synchronous",  0, 0, 1, do_synchronous,
97       "--synchronous    Turn on synchronous display mode" },
98     { "version",    'v', 0, 0, do_version,
99       "--version,-v     Display the Wine version" },
100     { "winver",       0, 1, 1, VERSION_ParseWinVersion,
101       "--winver         Version to imitate (win95,nt40,win31,nt2k,win98,nt351,win30,win20)" },
102     { NULL,           0, 0, 0, NULL, NULL }  /* terminator */
103 };
104
105
106 static void do_help( const char *arg )
107 {
108     OPTIONS_Usage();
109 }
110
111 static void do_version( const char *arg )
112 {
113     MESSAGE( "%s\n", WINE_RELEASE_INFO );
114     ExitProcess(0);
115 }
116
117 static void do_synchronous( const char *arg )
118 {
119     Options.synchronous = TRUE;
120 }
121
122 static void do_desktop( const char *arg )
123 {
124     Options.desktopGeometry = xstrdup( arg );
125 }
126
127 static void do_display( const char *arg )
128 {
129     Options.display = xstrdup( arg );
130 }
131
132 static void do_dll( const char *arg )
133 {
134     if (Options.dllFlags)
135     {
136         Options.dllFlags = (char *) realloc ( Options.dllFlags, 
137                                             strlen ( Options.dllFlags ) + strlen ( arg ) + 2 );
138         if ( !Options.dllFlags ) out_of_memory(); 
139         strcat ( Options.dllFlags, "+" );
140         strcat ( Options.dllFlags, arg );
141     }
142     else 
143     {
144         Options.dllFlags = xstrdup( arg );
145     }
146 }
147
148 static void do_language( const char *arg )
149 {
150     SetEnvironmentVariableA( "LANGUAGE", arg );
151 }
152
153 static void do_managed( const char *arg )
154 {
155     Options.managed = TRUE;
156 }
157
158 static void do_config( const char *arg )
159 {
160     Options.configFileName = xstrdup( arg );
161 }
162
163 static void do_debugmsg( const char *arg )
164 {
165     static const char * const debug_class_names[__DBCL_COUNT] = { "fixme", "err", "warn", "trace" };
166
167     char *opt, *options = strdup(arg);
168     int i;
169     /* defined in relay32/relay386.c */
170     extern char **debug_relay_includelist;
171     extern char **debug_relay_excludelist;
172     /* defined in relay32/snoop.c */
173     extern char **debug_snoop_includelist;
174     extern char **debug_snoop_excludelist;
175
176     if (!(opt = strtok( options, "," ))) goto error;
177     do
178     {
179         unsigned char set = 0, clear = 0;
180         char *p = strchr( opt, '+' );
181         if (!p) p = strchr( opt, '-' );
182         if (!p || !p[1]) goto error;
183         if (p > opt)
184         {
185             for (i = 0; i < __DBCL_COUNT; i++)
186             {
187                 int len = strlen(debug_class_names[i]);
188                 if (len != (p - opt)) continue;
189                 if (!memcmp( opt, debug_class_names[i], len ))  /* found it */
190                 {
191                     if (*p == '+') set |= 1 << i;
192                     else clear |= 1 << i;
193                     break;
194                 }
195             }
196             if (i == __DBCL_COUNT) goto error;  /* class name not found */
197         }
198         else
199         {
200             if (*p == '+') set = ~0;
201             else clear = ~0;
202             if (!strncasecmp(p+1, "relay=", 6) ||
203                 !strncasecmp(p+1, "snoop=", 6))
204                 {
205                     int i, l;
206                     char *s, *s2, ***output, c;
207
208                     if (strchr(p,','))
209                         l=strchr(p,',')-p;
210                     else
211                         l=strlen(p);
212                     set = ~0;
213                     clear = 0;
214                     output = (*p == '+') ?
215                         ((*(p+1) == 'r') ?
216                          &debug_relay_includelist :
217                          &debug_snoop_includelist) :
218                         ((*(p+1) == 'r') ?
219                          &debug_relay_excludelist :
220                          &debug_snoop_excludelist);
221                     s = p + 7;
222                     /* if there are n ':', there are n+1 modules, and we need
223                        n+2 slots, last one being for the sentinel (NULL) */
224                     i = 2;      
225                     while((s = strchr(s, ':'))) i++, s++;
226                     *output = malloc(sizeof(char **) * i);
227                     i = 0;
228                     s = p + 7;
229                     while((s2 = strchr(s, ':'))) {
230                         c = *s2;
231                         *s2 = '\0';
232                         *((*output)+i) = _strupr(strdup(s));
233                         *s2 = c;
234                         s = s2 + 1;
235                         i++;
236                     }
237                     c = *(p + l);
238                     *(p + l) = '\0';
239                     *((*output)+i) = _strupr(strdup(s));
240                     *(p + l) = c;
241                     *((*output)+i+1) = NULL;
242                     *(p + 6) = '\0';
243                 }
244         }
245         p++;
246         if (!strcmp( p, "all" )) p = "";  /* empty string means all */
247         wine_dbg_add_option( p, set, clear );
248         opt = strtok( NULL, "," );
249     } while(opt);
250
251     free( options );
252     return;
253
254  error:
255     MESSAGE("wine: Syntax: --debugmsg [class]+xxx,...  or "
256             "-debugmsg [class]-xxx,...\n");
257     MESSAGE("Example: --debugmsg +all,warn-heap\n"
258             "  turn on all messages except warning heap messages\n");
259     MESSAGE("Available message classes:\n");
260     for( i = 0; i < __DBCL_COUNT; i++) MESSAGE( "%-9s", debug_class_names[i] );
261     MESSAGE("\n\n");
262     ExitProcess(1);
263 }
264
265
266 static void remove_options( char *argv[], int pos, int count, int inherit )
267 {
268     if (inherit)
269     {
270         int i, len = 0;
271         for (i = 0; i < count; i++) len += strlen(argv[pos+i]) + 1;
272         if (inherit_str)
273         {
274             if (!(inherit_str = realloc( inherit_str, strlen(inherit_str) + 1 + len )))
275                 out_of_memory();
276             strcat( inherit_str, " " );
277         }
278         else
279         {
280             if (!(inherit_str = malloc( len ))) out_of_memory();
281             inherit_str[0] = 0;
282         }
283         for (i = 0; i < count; i++)
284         {
285             strcat( inherit_str, argv[pos+i] );
286             if (i < count-1) strcat( inherit_str, " " );
287         }
288     }
289     while ((argv[pos] = argv[pos+count])) pos++;
290 }
291
292 /* parse options from the argv array and remove all the recognized ones */
293 static void parse_options( char *argv[] )
294 {
295     const struct option *opt;
296     int i;
297
298     for (i = 0; argv[i]; i++)
299     {
300         const char *equalarg = NULL;
301         char *p = argv[i];
302         if (*p++ != '-') continue;  /* not an option */
303         if (*p && !p[1]) /* short name */
304         {
305             if (*p == '-') break; /* "--" option */
306             for (opt = option_table; opt->longname; opt++) if (opt->shortname == *p) break;
307         }
308         else  /* long name */
309         {
310             const char *equal = strchr  (p, '=');
311             if (*p == '-') p++;
312             /* check for the long name */
313             for (opt = option_table; opt->longname; opt++) {
314                 /* Plain --option */
315                 if (!strcmp( p, opt->longname )) break;
316
317                 /* --option=value */
318                 if (opt->has_arg &&
319                     equal &&
320                     strlen (opt->longname) == equal - p &&
321                     !strncmp (p, opt->longname, equal - p)) {
322                         equalarg = equal + 1;
323                         break;
324                     }
325             }
326         }
327         if (!opt->longname) continue;
328
329         if (equalarg)
330         {
331             opt->func( equalarg );
332             remove_options( argv, i, 1, opt->inherit );
333         }
334         else if (opt->has_arg && argv[i+1])
335         {
336             opt->func( argv[i+1] );
337             remove_options( argv, i, 2, opt->inherit );
338         }
339         else
340         {
341             opt->func( "" );
342             remove_options( argv, i, 1, opt->inherit );
343         }
344         i--;
345     }
346 }
347
348 /* inherit options from WINEOPTIONS variable */
349 static void inherit_options( char *buffer )
350 {
351     char *argv[256];
352     unsigned int n;
353
354     char *p = strtok( buffer, " \t" );
355     for (n = 0; n < sizeof(argv)/sizeof(argv[0])-1 && p; n++)
356     {
357         argv[n] = p;
358         p = strtok( NULL, " \t" );
359     }
360     argv[n] = NULL;
361     parse_options( argv );
362     if (argv[0])  /* an option remains */
363     {
364         MESSAGE( "Unknown option '%s' in WINEOPTIONS variable\n\n", argv[0] );
365         OPTIONS_Usage();
366     }
367 }
368
369 /***********************************************************************
370  *              OPTIONS_Usage
371  */
372 void OPTIONS_Usage(void)
373 {
374     const struct option *opt;
375     MESSAGE( "Usage: %s [options] [--] program_name [arguments]\n", argv0 );
376     MESSAGE("The -- has to be used if you specify arguments (of the program)\n\n");
377     MESSAGE( "Options:\n" );
378     for (opt = option_table; opt->longname; opt++) MESSAGE( "   %s\n", opt->usage );
379     ExitProcess(0);
380 }
381
382 /***********************************************************************
383  *              OPTIONS_ParseOptions
384  */
385 void OPTIONS_ParseOptions( char *argv[] )
386 {
387     char buffer[1024];
388     int i;
389
390     if (GetEnvironmentVariableA( "WINEOPTIONS", buffer, sizeof(buffer) ) && buffer[0])
391         inherit_options( buffer );
392
393     parse_options( argv + 1 );
394
395     SetEnvironmentVariableA( "WINEOPTIONS", inherit_str );
396
397     /* check if any option remains */
398     for (i = 1; argv[i]; i++)
399     {
400         if (!strcmp( argv[i], "--" ))
401         {
402             remove_options( argv, i, 1, 0 );
403             break;
404         }
405         if (argv[i][0] == '-')
406         {
407             MESSAGE( "Unknown option '%s'\n\n", argv[i] );
408             OPTIONS_Usage();
409         }
410     }
411
412     /* count the resulting arguments */
413     app_argv = argv;
414     app_argc = 0;
415     while (argv[app_argc]) app_argc++;
416 }
417
418
419 /***********************************************************************
420  *              __wine_get_main_args
421  *
422  * Return the argc/argv that the application should see.
423  * Used by the startup code generated in the .spec.c file.
424  */
425 int __wine_get_main_args( char ***argv )
426 {
427     *argv = app_argv;
428     return app_argc;
429 }
430
431
432 /***********************************************************************
433  *              __wine_get_wmain_args
434  *
435  * Same as __wine_get_main_args but for Unicode.
436  */
437 int __wine_get_wmain_args( WCHAR ***argv )
438 {
439     if (!app_wargv)
440     {
441         int i;
442         WCHAR *p;
443         DWORD total = 0;
444
445         for (i = 0; i < app_argc; i++)
446             total += MultiByteToWideChar( CP_ACP, 0, app_argv[i], -1, NULL, 0 );
447
448         app_wargv = HeapAlloc( GetProcessHeap(), 0,
449                                total * sizeof(WCHAR) + (app_argc + 1) * sizeof(*app_wargv) );
450         p = (WCHAR *)(app_wargv + app_argc + 1);
451         for (i = 0; i < app_argc; i++)
452         {
453             DWORD len = MultiByteToWideChar( CP_ACP, 0, app_argv[i], -1, p, total );
454             app_wargv[i] = p;
455             p += len;
456             total -= len;
457         }
458         app_wargv[app_argc] = NULL;
459     }
460     *argv = app_wargv;
461     return app_argc;
462 }