diff --git a/dlls/shell32/shell32_main.c b/dlls/shell32/shell32_main.c index 2778fc07c8f..e95b7073f93 100644 --- a/dlls/shell32/shell32_main.c +++ b/dlls/shell32/shell32_main.c @@ -34,50 +34,152 @@ DEFAULT_DEBUG_CHANNEL(shell); #define MORE_DEBUG 1 /************************************************************************* * 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 *argv,s,t; - LPWSTR cmdline; - int i; - TRACE("\n"); +{ + DWORD argc; + LPWSTR *argv; + LPWSTR arg,s,d; + LPWSTR cmdline; + int in_quotes,bcount; - /* to get writeable copy */ - if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpCmdline)+1) * sizeof(WCHAR) ))) - return NULL; - strcpyW( cmdline, lpCmdline ); - s=cmdline; - i=0; - while (*s) - { /* space */ - if (*s==0x0020) - { i++; - s++; - while (*s && *s==0x0020) - s++; - continue; - } - s++; - } - argv=(LPWSTR*)HeapAlloc( GetProcessHeap(), 0, sizeof(LPWSTR)*(i+1) ); - s=t=cmdline; - i=0; - while (*s) - { - if (*s==0x0020) - { - argv[i++]=t; - while (*s==0x0020) *s++ = 0; - t=s; - continue; + /* FIXME: same thing if we only have spaces */ + if (*lpCmdline==0) { + /* Return the path to the executable */ + DWORD size; + + argv=HeapAlloc(GetProcessHeap(), 0, 2*sizeof(LPWSTR)); + argv[0]=NULL; + size=16; + do { + size*=2; + argv[0]=HeapReAlloc(GetProcessHeap(), 0, argv[0], size); + } while (GetModuleFileNameW((HMODULE)0, argv[0], size) == 0); + argv[1]=NULL; + if (numargs) + *numargs=2; + + return argv; + } + + /* to get a writeable copy */ + cmdline = HeapAlloc(GetProcessHeap(), 0, (strlenW(lpCmdline)+1) * sizeof(WCHAR)); + if (!cmdline) + return NULL; + strcpyW(cmdline, lpCmdline); + argc=0; + 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 (*t) - argv[i++]=t; + if (*s==0) + break; + bcount=0; + 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)); - argv[i]=NULL; - *numargs=i; - return argv; + 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; + + HeapFree(GetProcessHeap(), 0, cmdline); + return argv; } /************************************************************************* diff --git a/memory/environ.c b/memory/environ.c index 2bf27850cba..98fadeceb1d 100644 --- a/memory/environ.c +++ b/memory/environ.c @@ -174,34 +174,121 @@ ENVDB *ENV_BuildEnvironment(void) * * Note that it does NOT necessarily include the file name. * 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 ) { - int len, quote = 0; + int len; char *p, **arg; - for (arg = argv, len = 0; *arg; arg++) len += strlen(*arg) + 1; - if ((argv[0]) && (quote = (strchr( argv[0], ' ' ) != NULL))) len += 2; - if (!(p = current_envdb.cmd_line = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; - arg = argv; - if (quote) + len = 0; + for (arg = argv; *arg; arg++) { - *p++ = '\"'; - strcpy( p, *arg ); - p += strlen(p); - *p++ = '\"'; - *p++ = ' '; - arg++; + int has_space,bcount; + char* a; + + has_space=0; + bcount=0; + 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 ); - p += strlen(p); - *p++ = ' '; - arg++; + int has_space,has_quote; + char* a; + + /* 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 */ - *p = 0; + if (p > current_envdb.cmd_line) + p--; /* remove last space */ + *p = '\0'; + /* now allocate the Unicode version */ len = MultiByteToWideChar( CP_ACP, 0, current_envdb.cmd_line, -1, NULL, 0 ); if (!(cmdlineW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) diff --git a/scheduler/process.c b/scheduler/process.c index 4255428b67e..084561cb3e9 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -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 ) { - char **argv; - int count = reserved + 1; - char *p = cmdline; + int argc; + char** argv; + char *arg,*s,*d; + int in_quotes,bcount; - /* if first word is quoted store it as a single arg */ - if (*cmdline == '\"') - { - if ((p = strchr( cmdline + 1, '\"' ))) - { - p++; - count++; - } - else p = cmdline; - } - while (*p) - { - while (*p && isspace(*p)) p++; - if (!*p) break; - count++; - while (*p && !isspace(*p)) p++; - } - - if ((argv = malloc( count * sizeof(*argv) ))) - { - char **argvptr = argv + reserved; - p = cmdline; - if (*cmdline == '\"') - { - if ((p = strchr( cmdline + 1, '\"' ))) - { - *argvptr++ = cmdline + 1; - *p++ = 0; + argc=reserved+1; + bcount=0; + in_quotes=0; + s=cmdline; + while (1) { + if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) { + /* space */ + argc++; + /* skip the remaining spaces */ + while (*s==' ' || *s=='\t') { + s++; } - else p = cmdline; + 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; } - while (*p) - { - while (*p && isspace(*p)) *p++ = 0; - if (!*p) break; - *argvptr++ = p; - while (*p && !isspace(*p)) p++; - } - *argvptr = 0; + s++; } + argv=malloc(argc*sizeof(*argv)); + if (!argv) + return NULL; + + arg=d=s=cmdline; + bcount=0; + in_quotes=0; + argc=reserved; + while (*s) { + if ((*s==' ' || *s=='\t') && !in_quotes) { + /* Close the argument and copy it */ + *d=0; + argv[argc++]=arg; + + /* 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++; + } + bcount=0; + } else { + /* a regular character */ + *d++=*s++; + bcount=0; + } + } + if (*arg) { + *d='\0'; + argv[argc++]=arg; + } + argv[argc]=NULL; + return argv; }