OSDN Git Service

Suppress glob-brace expansion within quoted arguments.
[mingw/mingw-org-wsl.git] / mingwrt / setargv.c
1 /*
2  * setargv.c
3  *
4  * Implements runtime initialization code to populate the argument
5  * vector, which will subsequently be passed to the main() function;
6  * provides a _setargv() hook, similar to that described on MSDN.
7  *
8  * $Id$
9  *
10  * Written by Keith Marshall <keithmarshall@users.sourceforge.net>
11  * Copyright (C) 2014, 2017, 2018, MinGW.org Project
12  *
13  * ---------------------------------------------------------------------------
14  *
15  * Permission is hereby granted, free of charge, to any person obtaining a
16  * copy of this software and associated documentation files (the "Software"),
17  * to deal in the Software without restriction, including without limitation
18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19  * and/or sell copies of the Software, and to permit persons to whom the
20  * Software is furnished to do so, subject to the following conditions:
21  *
22  * The above copyright notice and this permission notice shall be included
23  * in all copies or substantial portions of the Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
26  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  *
33  * ---------------------------------------------------------------------------
34  *
35  */
36 #define _ISOC99_SOURCE
37
38 #include <glob.h>
39 #include <string.h>
40 #include <ctype.h>
41
42 #define WIN32_LEAN_AND_MEAN
43 #include <windows.h>
44
45 /* Access to a standard 'main'-like argument count and list.
46  */
47 extern int     _argc;
48 extern char ** _argv;
49 extern int     _CRT_glob;
50
51 #define ARGV_INLINE  static __inline__ __attribute__((__always_inline__))
52
53 #define ARGV_ESCAPE     __CRT_GLOB_ESCAPE_CHAR__
54 #define ARGV_SQUOTE     __CRT_GLOB_USE_SINGLE_QUOTE__
55 #define ARGV_NOGROUP    __CRT_GLOB_BRACKET_GROUPS__
56
57 ARGV_INLINE
58 char *backslash( int count, char *buf )
59 {
60   /* Helper used by the MinGW replacement command line globbing handler,
61    * to provide appropriate handling of backslashes while preparing the
62    * command line arguments for globbing.
63    */
64   while( count-- )
65     *buf++ = '\\';
66   return buf;
67 }
68
69 ARGV_INLINE
70 char *unquote( int quote, int altquote, int escape, int *state, char *buf )
71 {
72   /* Helper used by the MinGW replacement command line globbing handler,
73    * to provide a single level of reduction for balanced quotation marks,
74    * while preparing the command line arguments for globbing.
75    */
76   buf = backslash( escape >> 1, buf );
77   if( (escape & 1) || (*state == altquote) )
78     /*
79      * In this case, the quotation mark is to be interpreted as a literal,
80      * and is NOT a candidate for reduction...
81      */
82     *buf++ = quote;
83   else
84     /* ...while this is the more usual case, of a quotation mark used to
85      * delimit a single argument; it must be reduced.
86      */
87     *state ^= quote;
88   return buf;
89 }
90
91 ARGV_INLINE
92 void __mingw32_setargv( const char *cmdline )
93 {
94   /* Implementation of the MinGW replacement command line interpreter.
95    */
96   char cmdbuf[1 + strlen( cmdline ) << 1];
97   int c, gotarg = 0, quoted = 0, bracket = 0, bslash = 0;
98   char *argptr = cmdbuf; const char *cmdptr = cmdline;
99   glob_t gl_argv;
100
101   /* Capture any non-default globbing options, which the user may have
102    * specified via a custom setting for _CRT_glob.
103    */
104   int gl_opts = GLOB_NOCHECK | (_CRT_glob & (GLOB_CASEMATCH | GLOB_BRACE));
105
106   /* We explicitly DO NOT use the GLOB_DOOFFS capability; ensure that
107    * the associated field, in the glob_t structure, is initialized to
108    * correctly reflect this.
109    */
110   gl_argv.gl_offs = 0;
111
112   /* Scan the command line, and prepare it for globbing.
113    */
114   while( c = *cmdptr++ )
115   {
116     /* Got a character to process...
117      */
118     switch( c )
119     {
120       /* Specific characters, which serve as globbing tokens,
121        * need special handling.
122        */
123       case '\\':
124         if( quoted == '\'' )
125           /* Backslashes within single quotes are always literal.
126            */
127           *argptr++ = '\\';
128
129         else
130           /* We don't (yet) know if this is a literal backslash,
131            * (directory separator), or an escape for a following
132            * quote character; just note its presence, until we
133            * have looked far enough ahead to decide.
134            */
135           ++bslash;
136         break;
137
138       case '[':
139         /* POSIX defines this as a globbing token, (introducing
140          * a character group); we don't support this by default,
141          * so defeat it, unless the extended behaviour has been
142          * requested by the user.
143          */
144         bracket = (_CRT_glob & ARGV_NOGROUP) ? 0 : ARGV_NOGROUP;
145
146       case '*': case '?':
147         /* These standard globbing tokens,...
148          */
149       case '{': case ',': case '}':
150         /* ...this additional triplet, non-standard, but required
151          * to support GNU's GLOB_BRACE extension; (strictly we need
152          * to consider these only if GLOB_BRACE is enabled, but it
153          * should do no harm to consider them regardless),...
154          */
155       case ARGV_ESCAPE:
156         /* ...and the escape character itself, need to be escaped
157          * when they appear in any context in which they should be
158          * interpreted literally, rather than globbed.
159          */
160         argptr = backslash( bslash, argptr );
161         if( quoted || (bracket == ARGV_NOGROUP) || (c == ARGV_ESCAPE) )
162           *argptr++ = ARGV_ESCAPE;
163         bracket = bslash = 0;
164         *argptr++ = c;
165         break;
166
167       case '"':
168         /* The double quote always acts as an argument quoting
169          * character, (unless escaped); handle it accordingly.
170          */
171         argptr = unquote( c, '\'', bslash, &quoted, argptr );
172         gotarg = 1; bslash = 0;
173         break;
174
175       case '\'':
176         /* POSIX also defines the single quote as a quoting
177          * character, but MS-Windows does not; we offer this
178          * extended handling...
179          */
180         if( _CRT_glob & ARGV_SQUOTE )
181         {
182           /* ...only when the user has explicitly enabled the
183            * POSIX compatible extended quoting option.
184            */
185           argptr = unquote( c, '"', bslash, &quoted, argptr );
186           gotarg = 1; bslash = 0;
187           break;
188         }
189
190       default:
191         /* With one exception, any other character is handled
192          * literally, after flushing out any pending backslashes.
193          */
194         argptr = backslash( bslash, argptr );
195         if( (quoted == 0) && isblank( c ) )
196         {
197           /* The one exception is any blank or tab character,
198            * when it is not contained within quotes; this acts
199            * as an argument separator, (or is simply discarded
200            * if there is no argument already collected)...
201            */
202           if( gotarg || (argptr > cmdbuf) )
203           {
204             /* ...so, when there is a argument pending, we may
205              * now add it to the globbed argument vector.
206              */
207             *argptr = '\0';
208             __mingw_glob( argptr = cmdbuf, gl_opts, NULL, &gl_argv );
209             gl_opts |= GLOB_APPEND;
210             gotarg = 0;
211           }
212         }
213         else
214           /* In every other case, we simply collect the current
215            * literal character into the next pending argument.
216            */
217           *argptr++ = c;
218
219         /* Irrespective of how we handled the current character,
220          * we can be certain that there are no pending backslashes
221          * by the time we get to here.
222          */
223         bslash = 0;
224     }
225   }
226   /* Finally, when we've run out of command line characters to process,
227    * flush out any final pending backslashes, ...
228    */
229   argptr = backslash( bslash, argptr );
230   if( gotarg || (argptr > cmdbuf) )
231   {
232     /* ...and add any final pending argument to the globbed vector.
233      */
234     *argptr = '\0';
235     __mingw_glob( argptr = cmdbuf, gl_opts, NULL, &gl_argv );
236   }
237   /* ...and store the resultant globbed vector into the "argc" and "argv"
238    * variables to be passed to main(); note that this allows us to safely
239    * discard our working glob_t structure, but we MUST NOT globfree() it,
240    * as that would destroy the content of "argv".
241    */
242   _argc = gl_argv.gl_pathc;
243   _argv = gl_argv.gl_pathv;
244 }
245
246 extern void _mingw32_init_mainargs( void );
247
248 void _setargv()
249 {
250   /* Initialize the _argc, _argv and environ variables.
251    */
252   if( (_CRT_glob & __CRT_GLOB_USE_MINGW__) == 0 )
253   {
254     /* This is the old start-up mechanism, implemented via a callback
255      * into the CRT initialization module, in which we use a start-up
256      * hook provided by Microsoft's runtime library to initialize the
257      * argument and environment vectors.
258      */
259     _mingw32_init_mainargs();
260   }
261   else
262   { /* Here, we implement a new, more POSIX compatible mechanism,
263      * for initializing the argument vector; note that we delegate
264      * to the previously defined inline function, which avoids the
265      * broken globbing behaviour of some more recent versions of
266      * MSVCRT.DLL
267      */
268     __mingw32_setargv( GetCommandLine() );
269   }
270 }
271
272 /* $RCSfile$: end of file */