[SHELL32]

* Sync CommandLineToArgvW with Wine 1.5.26. Fixes issues spotted by Victor Martinez.
CORE-7125 #resolve

svn path=/trunk/; revision=58860
This commit is contained in:
Amine Khaldi 2013-04-26 12:10:27 +00:00
parent ab9ec0bd8c
commit 86c4670f9f

View file

@ -38,33 +38,31 @@ const char * const SHELL_Authors[] = { "Copyright 1993-"COPYRIGHT_YEAR" WINE tea
* '"a b"' -> 'a b' * '"a b"' -> 'a b'
* - escaped quotes must be converted back to '"' * - escaped quotes must be converted back to '"'
* '\"' -> '"' * '\"' -> '"'
* - an odd number of '\'s followed by '"' correspond to half that number * - consecutive backslashes preceding a quote see their number halved with
* of '\' followed by a '"' (extension of the above) * the remainder escaping the quote:
* '\\\"' -> '\"' * 2n backslashes + quote -> n backslashes + quote as an argument delimiter
* '\\\\\"' -> '\\"' * 2n+1 backslashes + quote -> n backslashes + literal quote
* - an even number of '\'s followed by a '"' correspond to half that number * - backslashes that are not followed by a quote are copied literally:
* 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'
* 'a\\b' -> 'a\\b' * 'a\\b' -> 'a\\b'
* * - in quoted strings, consecutive quotes see their number divided by three
* Note: * with the remainder modulo 3 deciding whether to close the string or not.
* '\t' == 0x0009 * Note that the opening quote must be counted in the consecutive quotes,
* ' ' == 0x0020 * that's the (1+) below:
* '"' == 0x0022 * (1+) 3n quotes -> n quotes
* '\\' == 0x005c * (1+) 3n+1 quotes -> n quotes plus closes the quoted string
* (1+) 3n+2 quotes -> n+1 quotes plus closes the quoted string
* - in unquoted strings, the first quote opens the quoted string and the
* remaining consecutive quotes follow the above rule.
*/ */
LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs) LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
{ {
DWORD argc; DWORD argc;
LPWSTR *argv; LPWSTR *argv;
LPCWSTR cs; LPCWSTR s;
LPWSTR arg,s,d; LPWSTR d;
LPWSTR cmdline; LPWSTR cmdline;
int in_quotes,bcount; int qcount,bcount;
if(!numargs) if(!numargs)
{ {
@ -98,92 +96,155 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
return argv; return argv;
} }
/* to get a writable copy */ /* --- First count the arguments */
argc=0; argc=1;
bcount=0; s=lpCmdline;
in_quotes=0; /* The first argument, the executable path, follows special rules */
cs=lpCmdline; if (*s=='"')
while (1)
{ {
if (*cs==0 || ((*cs==0x0009 || *cs==0x0020) && !in_quotes)) /* The executable path ends at the next quote, no matter what */
{ s++;
/* space */ while (*s)
argc++; if (*s++=='"')
/* skip the remaining spaces */
while (*cs==0x0009 || *cs==0x0020)
{
cs++;
}
if (*cs==0)
break; break;
}
else
{
/* The executable path ends at the next space, no matter what */
while (*s && *s!=' ' && *s!='\t')
s++;
}
/* skip to the first argument, if any */
while (*s==' ' || *s=='\t')
s++;
if (*s)
argc++;
/* Analyze the remaining arguments */
qcount=bcount=0;
while (*s)
{
if ((*s==' ' || *s=='\t') && qcount==0)
{
/* skip to the next argument and count it if any */
while (*s==' ' || *s=='\t')
s++;
if (*s)
argc++;
bcount=0; bcount=0;
continue;
} }
else if (*cs==0x005c) else if (*s=='\\')
{ {
/* '\', count them */ /* '\', count them */
bcount++; bcount++;
s++;
} }
else if ((*cs==0x0022) && ((bcount & 1)==0)) else if (*s=='"')
{ {
/* unescaped '"' */ /* '"' */
in_quotes=!in_quotes; if ((bcount & 1)==0)
qcount++; /* unescaped '"' */
s++;
bcount=0; bcount=0;
/* consecutive quotes, see comment in copying code below */
while (*s=='"')
{
qcount++;
s++;
}
qcount=qcount % 3;
if (qcount==2)
qcount=0;
} }
else else
{ {
/* a regular character */ /* a regular character */
bcount=0; bcount=0;
s++;
} }
cs++;
} }
/* Allocate in a single lump, the string array, and the strings that go with it.
* This way the caller can make a single GlobalFree call to free both, as per MSDN. /* Allocate in a single lump, the string array, and the strings that go
* with it. This way the caller can make a single LocalFree() call to free
* both, as per MSDN.
*/ */
argv=(LPWSTR *)LocalAlloc(LMEM_FIXED, argc*sizeof(LPWSTR)+(wcslen(lpCmdline)+1)*sizeof(WCHAR)); argv=(LPWSTR *)LocalAlloc(LMEM_FIXED, argc*sizeof(LPWSTR)+(strlenW(lpCmdline)+1)*sizeof(WCHAR));
if (!argv) if (!argv)
return NULL; return NULL;
cmdline=(LPWSTR)(argv+argc); cmdline=(LPWSTR)(argv+argc);
wcscpy(cmdline, lpCmdline); strcpyW(cmdline, lpCmdline);
argc=0; /* --- Then split and copy the arguments */
bcount=0; argv[0]=d=cmdline;
in_quotes=0; argc=1;
arg=d=s=cmdline; /* The first argument, the executable path, follows special rules */
if (*d=='"')
{
/* The executable path ends at the next quote, no matter what */
s=d+1;
while (*s)
{
if (*s=='"')
{
s++;
break;
}
*d++=*s++;
}
}
else
{
/* The executable path ends at the next space, no matter what */
while (*d && *d!=' ' && *d!='\t')
d++;
s=d;
if (*s)
s++;
}
/* close the executable path */
*d++=0;
/* skip to the first argument and initialize it if any */
while (*s==' ' || *s=='\t')
s++;
if (!*s)
{
/* There are no parameters so we are all done */
*numargs=argc;
return argv;
}
/* Split and copy the remaining arguments */
argv[argc++]=d;
qcount=bcount=0;
while (*s) while (*s)
{ {
if ((*s==0x0009 || *s==0x0020) && !in_quotes) if ((*s==' ' || *s=='\t') && qcount==0)
{ {
/* Close the argument and copy it */ /* close the argument */
*d=0; *d++=0;
argv[argc++]=arg; bcount=0;
/* skip the remaining spaces */ /* skip to the next one and initialize it if any */
do { do {
s++; s++;
} while (*s==0x0009 || *s==0x0020); } while (*s==' ' || *s=='\t');
if (*s)
/* Start with a new argument */ argv[argc++]=d;
arg=d=s;
bcount=0;
} }
else if (*s==0x005c) else if (*s=='\\')
{ {
/* '\\' */
*d++=*s++; *d++=*s++;
bcount++; bcount++;
} }
else if (*s==0x0022) else if (*s=='"')
{ {
/* '"' */
if ((bcount & 1)==0) if ((bcount & 1)==0)
{ {
/* Preceded by an even number of '\', this is half that /* Preceded by an even number of '\', this is half that
* number of '\', plus a quote which we erase. * number of '\', plus a quote which we erase.
*/ */
d-=bcount/2; d-=bcount/2;
in_quotes=!in_quotes; qcount++;
s++;
} }
else else
{ {
@ -192,9 +253,24 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
*/ */
d=d-bcount/2-1; d=d-bcount/2-1;
*d++='"'; *d++='"';
}
s++;
bcount=0;
/* Now count the number of consecutive quotes. Note that qcount
* already takes into account the opening quote if any, as well as
* the quote that lead us here.
*/
while (*s=='"')
{
if (++qcount==3)
{
*d++='"';
qcount=0;
}
s++; s++;
} }
bcount=0; if (qcount==2)
qcount=0;
} }
else else
{ {
@ -203,11 +279,7 @@ LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
bcount=0; bcount=0;
} }
} }
if (*arg) *d='\0';
{
*d='\0';
argv[argc++]=arg;
}
*numargs=argc; *numargs=argc;
return argv; return argv;