Author: akhaldi Date: Fri Apr 26 12:10:27 2013 New Revision: 58860
URL: http://svn.reactos.org/svn/reactos?rev=58860&view=rev Log: [SHELL32] * Sync CommandLineToArgvW with Wine 1.5.26. Fixes issues spotted by Victor Martinez. CORE-7125 #resolve
Modified: trunk/reactos/dll/win32/shell32/shell32_main.cpp
Modified: trunk/reactos/dll/win32/shell32/shell32_main.cpp URL: http://svn.reactos.org/svn/reactos/trunk/reactos/dll/win32/shell32/shell32_m... ============================================================================== --- trunk/reactos/dll/win32/shell32/shell32_main.cpp [iso-8859-1] (original) +++ trunk/reactos/dll/win32/shell32/shell32_main.cpp [iso-8859-1] Fri Apr 26 12:10:27 2013 @@ -38,33 +38,31 @@ * '"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 + * - consecutive backslashes preceding a quote see their number halved with + * the remainder escaping the quote: + * 2n backslashes + quote -> n backslashes + quote as an argument delimiter + * 2n+1 backslashes + quote -> n backslashes + literal quote + * - backslashes that are not followed by a quote are copied literally: * 'a\b' -> 'a\b' * 'a\b' -> 'a\b' - * - * Note: - * '\t' == 0x0009 - * ' ' == 0x0020 - * '"' == 0x0022 - * '\' == 0x005c + * - in quoted strings, consecutive quotes see their number divided by three + * with the remainder modulo 3 deciding whether to close the string or not. + * Note that the opening quote must be counted in the consecutive quotes, + * that's the (1+) below: + * (1+) 3n quotes -> n quotes + * (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) { DWORD argc; LPWSTR *argv; - LPCWSTR cs; - LPWSTR arg,s,d; + LPCWSTR s; + LPWSTR d; LPWSTR cmdline; - int in_quotes,bcount; + int qcount,bcount;
if(!numargs) { @@ -98,92 +96,155 @@ return argv; }
- /* to get a writable copy */ - argc=0; - bcount=0; - in_quotes=0; - cs=lpCmdline; - while (1) - { - if (*cs==0 || ((*cs==0x0009 || *cs==0x0020) && !in_quotes)) - { - /* space */ - argc++; - /* skip the remaining spaces */ - while (*cs==0x0009 || *cs==0x0020) - { - cs++; - } - if (*cs==0) + /* --- First count the arguments */ + argc=1; + s=lpCmdline; + /* The first argument, the executable path, follows special rules */ + if (*s=='"') + { + /* The executable path ends at the next quote, no matter what */ + s++; + while (*s) + if (*s++=='"') 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; - continue; - } - else if (*cs==0x005c) + } + else if (*s=='\') { /* '', count them */ bcount++; - } - else if ((*cs==0x0022) && ((bcount & 1)==0)) - { - /* unescaped '"' */ - in_quotes=!in_quotes; + s++; + } + else if (*s=='"') + { + /* '"' */ + if ((bcount & 1)==0) + qcount++; /* unescaped '"' */ + s++; bcount=0; + /* consecutive quotes, see comment in copying code below */ + while (*s=='"') + { + qcount++; + s++; + } + qcount=qcount % 3; + if (qcount==2) + qcount=0; } else { /* a regular character */ bcount=0; - } - 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. + s++; + } + } + + /* 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) return NULL; cmdline=(LPWSTR)(argv+argc); - wcscpy(cmdline, lpCmdline); - - argc=0; - bcount=0; - in_quotes=0; - arg=d=s=cmdline; + strcpyW(cmdline, lpCmdline); + + /* --- Then split and copy the arguments */ + argv[0]=d=cmdline; + argc=1; + /* 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) { - if ((*s==0x0009 || *s==0x0020) && !in_quotes) - { - /* Close the argument and copy it */ - *d=0; - argv[argc++]=arg; - - /* skip the remaining spaces */ + if ((*s==' ' || *s=='\t') && qcount==0) + { + /* close the argument */ + *d++=0; + bcount=0; + + /* skip to the next one and initialize it if any */ do { s++; - } while (*s==0x0009 || *s==0x0020); - - /* Start with a new argument */ - arg=d=s; - bcount=0; - } - else if (*s==0x005c) - { - /* '\' */ + } while (*s==' ' || *s=='\t'); + if (*s) + argv[argc++]=d; + } + else if (*s=='\') + { *d++=*s++; bcount++; } - else if (*s==0x0022) - { - /* '"' */ + else if (*s=='"') + { if ((bcount & 1)==0) { /* Preceded 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++; + qcount++; } else { @@ -192,9 +253,24 @@ */ d=d-bcount/2-1; *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++; } - bcount=0; + if (qcount==2) + qcount=0; } else { @@ -203,11 +279,7 @@ bcount=0; } } - if (*arg) - { - *d='\0'; - argv[argc++]=arg; - } + *d='\0'; *numargs=argc;
return argv;