[PATCH 3/4] shcore: Add CommandLineToArgvW().
Nikolay Sivov
nsivov at codeweavers.com
Mon Nov 26 02:53:12 CST 2018
Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
dlls/shcore/main.c | 258 ++++++++++++++++++++++++++++++++++++++++
dlls/shcore/shcore.spec | 2 +-
2 files changed, 259 insertions(+), 1 deletion(-)
diff --git a/dlls/shcore/main.c b/dlls/shcore/main.c
index ccea518f88..8f674a2edc 100644
--- a/dlls/shcore/main.c
+++ b/dlls/shcore/main.c
@@ -29,6 +29,7 @@
#include "ocidl.h"
#include "shellscalingapi.h"
#include "wine/debug.h"
+#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(shcore);
@@ -234,3 +235,260 @@ HRESULT WINAPI GetCurrentProcessExplicitAppUserModelID(const WCHAR **appid)
*appid = NULL;
return E_NOTIMPL;
}
+
+/*************************************************************************
+ * 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** WINAPI CommandLineToArgvW(const WCHAR *cmdline, int *numargs)
+{
+ int qcount, bcount;
+ const WCHAR *s;
+ WCHAR **argv;
+ DWORD argc;
+ WCHAR *d;
+
+ if (!numargs)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ if (*cmdline == 0)
+ {
+ /* Return the path to the executable */
+ DWORD len, deslen = MAX_PATH, size;
+
+ size = sizeof(WCHAR *) * 2 + deslen * sizeof(WCHAR);
+ for (;;)
+ {
+ if (!(argv = LocalAlloc(LMEM_FIXED, size))) return NULL;
+ len = GetModuleFileNameW(0, (WCHAR *)(argv + 2), deslen);
+ if (!len)
+ {
+ LocalFree(argv);
+ return NULL;
+ }
+ if (len < deslen) break;
+ deslen *= 2;
+ size = sizeof(WCHAR *) * 2 + deslen * sizeof(WCHAR);
+ LocalFree(argv);
+ }
+ argv[0] = (WCHAR *)(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 = LocalAlloc(LMEM_FIXED, (argc + 1) * sizeof(WCHAR *) + (strlenW(cmdline) + 1) * sizeof(WCHAR));
+ if (!argv)
+ return NULL;
+
+ /* --- Then split and copy the arguments */
+ argv[0] = d = strcpyW((WCHAR *)(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;
+}
diff --git a/dlls/shcore/shcore.spec b/dlls/shcore/shcore.spec
index 2f3ca9925b..1266a02f69 100644
--- a/dlls/shcore/shcore.spec
+++ b/dlls/shcore/shcore.spec
@@ -1,5 +1,5 @@
1 stub @
-@ stdcall CommandLineToArgvW(wstr ptr) shell32.CommandLineToArgvW
+@ stdcall CommandLineToArgvW(wstr ptr)
@ stub CreateRandomAccessStreamOnFile
@ stub CreateRandomAccessStreamOverStream
@ stub CreateStreamOverRandomAccessStream
--
2.19.2
More information about the wine-devel
mailing list