Commit 5ee3879c authored by Francois Gouget's avatar Francois Gouget Committed by Alexandre Julliard

Fix the conversions of a command line to/from an argv array.

parent 45e9cea3
...@@ -34,50 +34,152 @@ DEFAULT_DEBUG_CHANNEL(shell); ...@@ -34,50 +34,152 @@ DEFAULT_DEBUG_CHANNEL(shell);
#define MORE_DEBUG 1 #define MORE_DEBUG 1
/************************************************************************* /*************************************************************************
* CommandLineToArgvW [SHELL32.@] * CommandLineToArgvW [SHELL32.@]
*
* We must interpret the quotes in the command line to rebuild the argv
* array correctly:
* - arguments are separated by spaces or tabs
* - quotes serve as optional argument delimiters
* '"a b"' -> 'a b'
* - escaped quotes must be converted back to '"'
* '\"' -> '"'
* - an odd number of '\'s followed by '"' correspond to half that number
* of '\' followed by a '"' (extension of the above)
* '\\\"' -> '\"'
* '\\\\\"' -> '\\"'
* - an even number of '\'s followed by a '"' correspond to half that number
* of '\', plus a regular quote serving as an argument delimiter (which
* means it does not appear in the result)
* 'a\\"b c"' -> 'a\b c'
* 'a\\\\"b c"' -> 'a\\b c'
* - '\' that are not followed by a '"' are copied literally
* 'a\b' -> 'a\b'
* 'a\\b' -> 'a\\b'
*
* Note:
* '\t' == 0x0009
* ' ' == 0x0020
* '"' == 0x0022
* '\\' == 0x005c
*/ */
LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
{ LPWSTR *argv,s,t; {
LPWSTR cmdline; DWORD argc;
int i; LPWSTR *argv;
TRACE("\n"); LPWSTR arg,s,d;
LPWSTR cmdline;
/* to get writeable copy */ int in_quotes,bcount;
if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpCmdline)+1) * sizeof(WCHAR) )))
return NULL; /* FIXME: same thing if we only have spaces */
strcpyW( cmdline, lpCmdline ); if (*lpCmdline==0) {
s=cmdline; /* Return the path to the executable */
i=0; DWORD size;
while (*s)
{ /* space */ argv=HeapAlloc(GetProcessHeap(), 0, 2*sizeof(LPWSTR));
if (*s==0x0020) argv[0]=NULL;
{ i++; size=16;
s++; do {
while (*s && *s==0x0020) size*=2;
s++; argv[0]=HeapReAlloc(GetProcessHeap(), 0, argv[0], size);
continue; } while (GetModuleFileNameW((HMODULE)0, argv[0], size) == 0);
} argv[1]=NULL;
s++; if (numargs)
} *numargs=2;
argv=(LPWSTR*)HeapAlloc( GetProcessHeap(), 0, sizeof(LPWSTR)*(i+1) );
s=t=cmdline; return argv;
i=0; }
while (*s)
{ /* to get a writeable copy */
if (*s==0x0020) cmdline = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpCmdline)+1) * sizeof(WCHAR));
{ if (!cmdline)
argv[i++]=t; return NULL;
while (*s==0x0020) *s++ = 0; strcpyW(cmdline, lpCmdline);
t=s; argc=0;
continue; bcount=0;
in_quotes=0;
s=cmdline;
while (1) {
if (*s==0 || ((*s==0x0009 || *s==0x0020) && !in_quotes)) {
/* space */
argc++;
/* skip the remaining spaces */
while (*s==0x0009 || *s==0x0020) {
s++;
} }
s++; if (*s==0)
} break;
if (*t) bcount=0;
argv[i++]=t; continue;
} else if (*s==0x005c) {
/* '\', count them */
bcount++;
} else if ((*s==0x0022) && ((bcount & 1)==0)) {
/* unescaped '"' */
in_quotes=!in_quotes;
bcount=0;
} else {
/* a regular character */
bcount=0;
}
s++;
}
argv=HeapAlloc(GetProcessHeap(), 0, (argc+1)*sizeof(LPWSTR));
argc=0;
bcount=0;
in_quotes=0;
arg=d=s=cmdline;
while (*s) {
if ((*s==0x0009 || *s==0x0020) && !in_quotes) {
/* Close the argument and copy it */
*d=0;
argv[argc++]=arg;
/* skip the remaining spaces */
do {
s++;
} while (*s==0x0009 || *s==0x0020);
/* Start with a new argument */
arg=d=s;
bcount=0;
} else if (*s==0x005c) {
/* '\\' */
*d++=*s++;
bcount++;
} else if (*s==0x0022) {
/* '"' */
if ((bcount & 1)==0) {
/* Preceeded by an even number of '\', this is half that
* number of '\', plus a quote which we erase.
*/
d-=bcount/2;
in_quotes=!in_quotes;
s++;
} else {
/* Preceeded by an odd number of '\', this is half that
* number of '\' followed by a '"'
*/
d=d-bcount/2-1;
*d++='"';
s++;
}
bcount=0;
} else {
/* a regular character */
*d++=*s++;
bcount=0;
}
}
if (*arg) {
*d='\0';
argv[argc++]=arg;
}
argv[argc]=NULL;
if (numargs)
*numargs=argc;
argv[i]=NULL; HeapFree(GetProcessHeap(), 0, cmdline);
*numargs=i; return argv;
return argv;
} }
/************************************************************************* /*************************************************************************
......
...@@ -174,34 +174,121 @@ ENVDB *ENV_BuildEnvironment(void) ...@@ -174,34 +174,121 @@ ENVDB *ENV_BuildEnvironment(void)
* *
* Note that it does NOT necessarily include the file name. * Note that it does NOT necessarily include the file name.
* Sometimes we don't even have any command line options at all. * Sometimes we don't even have any command line options at all.
*
* We must quote and escape characters so that the argv array can be rebuilt
* from the command line:
* - spaces and tabs must be quoted
* 'a b' -> '"a b"'
* - quotes must be escaped
* '"' -> '\"'
* - if '\'s are followed by a '"', they must be doubled and followed by '\"',
* resulting in an odd number of '\' followed by a '"'
* '\"' -> '\\\"'
* '\\"' -> '\\\\\"'
* - '\'s that are not followed by a '"' can be left as is
* 'a\b' == 'a\b'
* 'a\\b' == 'a\\b'
*/ */
BOOL ENV_BuildCommandLine( char **argv ) BOOL ENV_BuildCommandLine( char **argv )
{ {
int len, quote = 0; int len;
char *p, **arg; char *p, **arg;
for (arg = argv, len = 0; *arg; arg++) len += strlen(*arg) + 1; len = 0;
if ((argv[0]) && (quote = (strchr( argv[0], ' ' ) != NULL))) len += 2; for (arg = argv; *arg; arg++)
if (!(p = current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
arg = argv;
if (quote)
{ {
*p++ = '\"'; int has_space,bcount;
strcpy( p, *arg ); char* a;
p += strlen(p);
*p++ = '\"'; has_space=0;
*p++ = ' '; bcount=0;
arg++; a=*arg;
while (*a!='\0') {
if (*a=='\\') {
bcount++;
} else {
if (*a==' ' || *a=='\t') {
has_space=1;
} else if (*a=='"') {
/* doubling of '\' preceeding a '"',
* plus escaping of said '"'
*/
len+=2*bcount+1;
}
bcount=0;
}
a++;
}
len+=(a-*arg)+1 /* for the separating space */;
if (has_space)
len+=2; /* for the quotes */
} }
while (*arg)
if (!(current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len )))
return FALSE;
p = current_envdb.cmd_line;
for (arg = argv; *arg; arg++)
{ {
strcpy( p, *arg ); int has_space,has_quote;
p += strlen(p); char* a;
*p++ = ' ';
arg++; /* Check for quotes and spaces in this argument */
has_space=has_quote=0;
a=*arg;
while (*a!='\0') {
if (*a==' ' || *a=='\t') {
has_space=1;
if (has_quote)
break;
} else if (*a=='"') {
has_quote=1;
if (has_space)
break;
}
a++;
}
/* Now transfer it to the command line */
if (has_space)
*p++='"';
if (has_quote) {
int bcount;
char* a;
bcount=0;
a=*arg;
while (*a!='\0') {
if (*a=='\\') {
*p++=*a;
bcount++;
} else {
if (*a=='"') {
int i;
/* Double all the '\\' preceeding this '"', plus one */
for (i=0;i<=bcount;i++)
*p++='\\';
*p++='"';
} else {
*p++=*a;
}
bcount=0;
}
a++;
}
} else {
strcpy(p,*arg);
p+=strlen(*arg);
}
if (has_space)
*p++='"';
*p++=' ';
} }
if (p > current_envdb.cmd_line) p--; /* remove last space */ if (p > current_envdb.cmd_line)
*p = 0; p--; /* remove last space */
*p = '\0';
/* now allocate the Unicode version */ /* now allocate the Unicode version */
len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 ); len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 );
if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
......
...@@ -531,50 +531,96 @@ void PROCESS_InitWine( int argc, char *argv[], LPSTR win16_exe_name, HANDLE *win ...@@ -531,50 +531,96 @@ void PROCESS_InitWine( int argc, char *argv[], LPSTR win16_exe_name, HANDLE *win
*/ */
static char **build_argv( char *cmdline, int reserved ) static char **build_argv( char *cmdline, int reserved )
{ {
char **argv; int argc;
int count = reserved + 1; char** argv;
char *p = cmdline; char *arg,*s,*d;
int in_quotes,bcount;
/* if first word is quoted store it as a single arg */
if (*cmdline == '\"') argc=reserved+1;
{ bcount=0;
if ((p = strchr( cmdline + 1, '\"' ))) in_quotes=0;
{ s=cmdline;
p++; while (1) {
count++; if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) {
/* space */
argc++;
/* skip the remaining spaces */
while (*s==' ' || *s=='\t') {
s++;
}
if (*s=='\0')
break;
bcount=0;
continue;
} else if (*s=='\\') {
/* '\', count them */
bcount++;
} else if ((*s=='"') && ((bcount & 1)==0)) {
/* unescaped '"' */
in_quotes=!in_quotes;
bcount=0;
} else {
/* a regular character */
bcount=0;
} }
else p = cmdline; s++;
}
while (*p)
{
while (*p && isspace(*p)) p++;
if (!*p) break;
count++;
while (*p && !isspace(*p)) p++;
} }
argv=malloc(argc*sizeof(*argv));
if (!argv)
return NULL;
if ((argv = malloc( count * sizeof(*argv) ))) arg=d=s=cmdline;
{ bcount=0;
char **argvptr = argv + reserved; in_quotes=0;
p = cmdline; argc=reserved;
if (*cmdline == '\"') while (*s) {
{ if ((*s==' ' || *s=='\t') && !in_quotes) {
if ((p = strchr( cmdline + 1, '\"' ))) /* Close the argument and copy it */
{ *d=0;
*argvptr++ = cmdline + 1; argv[argc++]=arg;
*p++ = 0;
/* skip the remaining spaces */
do {
s++;
} while (*s==' ' || *s=='\t');
/* Start with a new argument */
arg=d=s;
bcount=0;
} else if (*s=='\\') {
/* '\\' */
*d++=*s++;
bcount++;
} else if (*s=='"') {
/* '"' */
if ((bcount & 1)==0) {
/* Preceeded by an even number of '\', this is half that
* number of '\', plus a '"' which we discard.
*/
d-=bcount/2;
s++;
in_quotes=!in_quotes;
} else {
/* Preceeded by an odd number of '\', this is half that
* number of '\' followed by a '"'
*/
d=d-bcount/2-1;
*d++='"';
s++;
} }
else p = cmdline; bcount=0;
} else {
/* a regular character */
*d++=*s++;
bcount=0;
} }
while (*p)
{
while (*p && isspace(*p)) *p++ = 0;
if (!*p) break;
*argvptr++ = p;
while (*p && !isspace(*p)) p++;
}
*argvptr = 0;
} }
if (*arg) {
*d='\0';
argv[argc++]=arg;
}
argv[argc]=NULL;
return argv; return argv;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment