Fix subclassing to support nested messages.
[wine] / misc / options.c
1 /*
2  * Option parsing
3  *
4  * Copyright 2000 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdlib.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "wine/library.h"
32 #include "options.h"
33 #include "wine/debug.h"
34
35 struct option_descr
36 {
37     const char *longname;
38     char        shortname;
39     int         has_arg;
40     int         inherit;
41     void      (*func)( const char *arg );
42     const char *usage;
43 };
44
45 static char *inherit_str;  /* options to pass to child processes */
46
47 static void DECLSPEC_NORETURN out_of_memory(void);
48 static void out_of_memory(void)
49 {
50     MESSAGE( "Virtual memory exhausted\n" );
51     ExitProcess(1);
52 }
53
54 static void do_debugmsg( const char *arg );
55 static void do_dll( const char *arg );
56 static void do_help( const char *arg );
57 static void do_version( const char *arg );
58
59 static const struct option_descr option_table[] =
60 {
61     { "debugmsg",     0, 1, 1, do_debugmsg,
62       "--debugmsg name  Turn debugging-messages on or off" },
63     { "dll",          0, 1, 1, do_dll,
64       "--dll name       This option is no longer supported" },
65     { "help",       'h', 0, 0, do_help,
66       "--help,-h        Show this help message" },
67     { "version",    'v', 0, 0, do_version,
68       "--version,-v     Display the Wine version" },
69     { NULL,           0, 0, 0, NULL, NULL }  /* terminator */
70 };
71
72
73 static void do_help( const char *arg )
74 {
75     OPTIONS_Usage();
76 }
77
78 static void do_version( const char *arg )
79 {
80     MESSAGE( "%s\n", PACKAGE_STRING );
81     ExitProcess(0);
82 }
83
84 static void do_debugmsg( const char *arg )
85 {
86     if (wine_dbg_parse_options( arg ))
87     {
88         MESSAGE("wine: Syntax: --debugmsg [class]+xxx,...  or -debugmsg [class]-xxx,...\n");
89         MESSAGE("Example: --debugmsg +all,warn-heap\n"
90                 "  turn on all messages except warning heap messages\n");
91         MESSAGE("Available message classes: err, warn, fixme, trace\n\n");
92         ExitProcess(1);
93     }
94 }
95
96 static void do_dll( const char *arg )
97 {
98     MESSAGE("The --dll option has been removed, you should use\n"
99             "the WINEDLLOVERRIDES environment variable instead.\n"
100             "To see a help message, run:\n"
101             "    WINEDLLOVERRIDES=help wine <program.exe>\n");
102     ExitProcess(1);
103 }
104
105 static void remove_options( char *argv[], int pos, int count, int inherit )
106 {
107     if (inherit)
108     {
109         int i, len = 0;
110         for (i = 0; i < count; i++) len += strlen(argv[pos+i]) + 1;
111         if (inherit_str)
112         {
113             if (!(inherit_str = realloc( inherit_str, strlen(inherit_str) + 1 + len )))
114                 out_of_memory();
115             strcat( inherit_str, " " );
116         }
117         else
118         {
119             if (!(inherit_str = malloc( len ))) out_of_memory();
120             inherit_str[0] = 0;
121         }
122         for (i = 0; i < count; i++)
123         {
124             strcat( inherit_str, argv[pos+i] );
125             if (i < count-1) strcat( inherit_str, " " );
126         }
127     }
128     while ((argv[pos] = argv[pos+count])) pos++;
129 }
130
131 /* parse options from the argv array and remove all the recognized ones */
132 static void parse_options( char *argv[] )
133 {
134     const struct option_descr *opt;
135     int i;
136
137     for (i = 0; argv[i]; i++)
138     {
139         const char *equalarg = NULL;
140         char *p = argv[i];
141         if (*p++ != '-') continue;  /* not an option */
142         if (*p && !p[1]) /* short name */
143         {
144             if (*p == '-') break; /* "--" option */
145             for (opt = option_table; opt->longname; opt++) if (opt->shortname == *p) break;
146         }
147         else  /* long name */
148         {
149             const char *equal = strchr  (p, '=');
150             if (*p == '-') p++;
151             /* check for the long name */
152             for (opt = option_table; opt->longname; opt++) {
153                 /* Plain --option */
154                 if (!strcmp( p, opt->longname )) break;
155
156                 /* --option=value */
157                 if (opt->has_arg &&
158                     equal &&
159                     strlen (opt->longname) == equal - p &&
160                     !strncmp (p, opt->longname, equal - p)) {
161                         equalarg = equal + 1;
162                         break;
163                     }
164             }
165         }
166         if (!opt->longname) continue;
167
168         if (equalarg)
169         {
170             opt->func( equalarg );
171             remove_options( argv, i, 1, opt->inherit );
172         }
173         else if (opt->has_arg && argv[i+1])
174         {
175             opt->func( argv[i+1] );
176             remove_options( argv, i, 2, opt->inherit );
177         }
178         else
179         {
180             opt->func( "" );
181             remove_options( argv, i, 1, opt->inherit );
182         }
183         i--;
184     }
185 }
186
187 /* inherit options from WINEOPTIONS variable */
188 static void inherit_options( char *buffer )
189 {
190     char *argv[256];
191     unsigned int n;
192
193     char *p = strtok( buffer, " \t" );
194     for (n = 0; n < sizeof(argv)/sizeof(argv[0])-1 && p; n++)
195     {
196         argv[n] = p;
197         p = strtok( NULL, " \t" );
198     }
199     argv[n] = NULL;
200     parse_options( argv );
201     if (argv[0])  /* an option remains */
202     {
203         MESSAGE( "Unknown option '%s' in WINEOPTIONS variable\n\n", argv[0] );
204         OPTIONS_Usage();
205     }
206 }
207
208 /***********************************************************************
209  *              OPTIONS_Usage
210  */
211 void OPTIONS_Usage(void)
212 {
213     const struct option_descr *opt;
214     MESSAGE( "%s\n\n", PACKAGE_STRING );
215     MESSAGE( "Usage: wine [options] [--] program_name [arguments]\n" );
216     MESSAGE("The -- has to be used if you specify arguments (of the program)\n\n");
217     MESSAGE( "Options:\n" );
218     for (opt = option_table; opt->longname; opt++) MESSAGE( "   %s\n", opt->usage );
219     ExitProcess(0);
220 }
221
222 /***********************************************************************
223  *              OPTIONS_ParseOptions
224  */
225 void OPTIONS_ParseOptions( char *argv[] )
226 {
227     char buffer[1024];
228     int i;
229
230     if (GetEnvironmentVariableA( "WINEOPTIONS", buffer, sizeof(buffer) ) && buffer[0])
231         inherit_options( buffer );
232     if (!argv) return;
233
234     parse_options( argv + 1 );
235
236     SetEnvironmentVariableA( "WINEOPTIONS", inherit_str );
237
238     /* check if any option remains */
239     for (i = 1; argv[i]; i++)
240     {
241         if (!strcmp( argv[i], "--" ))
242         {
243             remove_options( argv, i, 1, 0 );
244             break;
245         }
246         if (argv[i][0] == '-')
247         {
248             MESSAGE( "Unknown option '%s'\n\n", argv[i] );
249             OPTIONS_Usage();
250         }
251     }
252 }