#include "utils.h" #include /* * Copyright 2002 Jon Griffiths * Copyright 2016 Sebastian Lackner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define MAX_PATH 1024 /************************************************************************* * CommandLineToArgvW [SHCORE.@] * * 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 '"' * '\"' -> '"' * - 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' * - 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. */ wchar_t** wine_parse_command_line(const wchar_t *cmdline, int *numargs, uint32_t* error) { int qcount, bcount; const wchar_t *s; wchar_t **argv; uint32_t argc; wchar_t *d; *error = 0; if (!numargs) { *error = ERROR_INVALID_PARAMETER; return NULL; } // This functionality is not used and requires passing in an extra argument // if (*cmdline == 0) // { // /* Return the path to the executable */ // uint32_t len, deslen = MAX_PATH, size; // size = sizeof(wchar_t *) * 2 + deslen * sizeof(wchar_t); // for (;;) // { // if (!(argv = malloc(size))) return NULL; // len = GetModuleFileNameW(0, (wchar_t *)(argv + 2), deslen); // if (!len) // { // free(argv); // return NULL; // } // if (len < deslen) break; // deslen *= 2; // size = sizeof(wchar_t *) * 2 + deslen * sizeof(wchar_t); // free(argv); // } // argv[0] = (wchar_t *)(argv + 2); // argv[1] = NULL; // *numargs = 1; // return argv; // } /* --- First count the arguments */ argc = 1; s = cmdline; /* 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; } else if (*s == '\\') { /* '\', count them */ bcount++; 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; 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 = malloc((argc + 1) * sizeof(wchar_t *) + (wcslen(cmdline) + 1) * sizeof(wchar_t)); if (!argv) return NULL; /* --- Then split and copy the arguments */ argv[0] = d = wcscpy((wchar_t *)(argv + argc + 1), 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 */ argv[argc] = NULL; *numargs = argc; return argv; } /* Split and copy the remaining arguments */ argv[argc++] = d; qcount = bcount = 0; while (*s) { 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 == ' ' || *s == '\t'); if (*s) argv[argc++] = d; } else if (*s=='\\') { *d++ = *s++; bcount++; } 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; qcount++; } else { /* Preceded by an odd number of '\', this is half that * number of '\' followed by a '"' */ 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++; } if (qcount == 2) qcount = 0; } else { /* a regular character */ *d++ = *s++; bcount = 0; } } *d = '\0'; argv[argc] = NULL; *numargs = argc; return argv; }