mirror of
https://github.com/reactos/reactos.git
synced 2024-07-05 12:15:46 +00:00
[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:
parent
ab9ec0bd8c
commit
86c4670f9f
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue