Author: mjansen
Date: Fri Aug 12 21:31:32 2016
New Revision: 72214

URL: http://svn.reactos.org/svn/reactos?rev=72214&view=rev
Log:
[CRT][CRT_APITEST] Fix __getmainargs and __wgetmainargs parsing, verified with 
apitests. Patch by Yaroslav Veremenko. CORE-11673 #resolve #comment Thanks!

Added:
    trunk/rostests/apitests/crt/__getmainargs.c   (with props)
Modified:
    trunk/reactos/sdk/lib/crt/misc/getargs.c
    trunk/rostests/apitests/crt/msvcrt_crt_apitest.cmake
    trunk/rostests/apitests/crt/testlist.c

Modified: trunk/reactos/sdk/lib/crt/misc/getargs.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/reactos/sdk/lib/crt/misc/getargs.c?rev=72214&r1=72213&r2=72214&view=diff
==============================================================================
--- trunk/reactos/sdk/lib/crt/misc/getargs.c    [iso-8859-1] (original)
+++ trunk/reactos/sdk/lib/crt/misc/getargs.c    [iso-8859-1] Fri Aug 12 
21:31:32 2016
@@ -181,16 +181,18 @@
  */
 void __getmainargs(int* argc, char*** argv, char*** env, int expand_wildcards, 
int* new_mode)
 {
-   int i, afterlastspace, ignorespace, doexpand;
+   int i, doexpand, slashesAdded, escapedQuote, inQuotes, bufferIndex;
    size_t len;
-   char* aNewCmdln;
+   char* buffer;
 
    /* missing threading init */
 
    i = 0;
-   afterlastspace = 0;
-   ignorespace = 0;
    doexpand = expand_wildcards;
+   escapedQuote = FALSE;
+   slashesAdded = 0;
+   inQuotes = 0;
+   bufferIndex = 0;
 
    if (__argv && _environ)
    {
@@ -203,58 +205,94 @@
    __argc = 0;
 
    len = strlen(_acmdln);
-
-   /* Allocate a temporary buffer to be used instead of the original _acmdln 
parameter. */
-   aNewCmdln = strndup(_acmdln, len);
-
-   while (aNewCmdln[i])
-   {
-      if (aNewCmdln[i] == '"')
-      {
-         if(ignorespace)
-         {
-            ignorespace = 0;
-         }
-         else
-         {
-            ignorespace = 1;
-            doexpand = 0;
-         }
-         memmove(aNewCmdln + i, aNewCmdln + i + 1, len - i);
-         len--;
-         continue;
-      }
-
-      if (aNewCmdln[i] == ' ' && !ignorespace)
-      {
-         aexpand(strndup(aNewCmdln + afterlastspace, i - afterlastspace), 
doexpand);
-         i++;
-         while (aNewCmdln[i] == ' ')
-            i++;
-         afterlastspace=i;
-         doexpand = expand_wildcards;
-      }
-      else
-      {
-         i++;
-      }
-   }
-
-   if (aNewCmdln[afterlastspace] != 0)
-   {
-      aexpand(strndup(aNewCmdln + afterlastspace, i - afterlastspace), 
doexpand);
+   buffer = malloc(sizeof(char) * len);
+
+   // Reference: 
https://msdn.microsoft.com/en-us/library/a1y7w461(v=vs.71).aspx
+   while (TRUE)
+   {
+      // Arguments are delimited by white space, which is either a space or a 
tab.
+      if (i >= len || ((_acmdln[i] == ' ' || _acmdln[i] == '\t') && !inQuotes))
+      {
+         aexpand(strndup(buffer, bufferIndex), doexpand);
+         // Copy the last element from buffer and quit the loop
+         if (i >= len)
+         {
+            break;
+         }
+
+         while (_acmdln[i] == ' ' || _acmdln[i] == '\t')
+            ++i;
+         bufferIndex = 0;
+         slashesAdded = 0;
+         escapedQuote = FALSE;
+         continue;
+      }
+
+      if (_acmdln[i] == '\\')
+      {
+         buffer[bufferIndex++] = _acmdln[i];
+         ++slashesAdded;
+         ++i;
+         escapedQuote = FALSE;
+         continue;
+      }
+
+      if (_acmdln[i] == '\"')
+      {
+         if (slashesAdded > 0)
+         {
+            if (slashesAdded % 2 == 0)
+            {
+               // If an even number of backslashes is followed by a double 
quotation mark, then one backslash (\)
+               // is placed in the argv array for every pair of backslashes 
(\\), and the double quotation mark (")
+               // is interpreted as a string delimiter.
+               bufferIndex -= slashesAdded / 2;
+            }
+            else
+            {
+               // If an odd number of backslashes is followed by a double 
quotation mark, then one backslash (\)
+               // is placed in the argv array for every pair of backslashes 
(\\) and the double quotation mark is
+               // interpreted as an escape sequence by the remaining 
backslash, causing a literal double quotation mark (")
+               // to be placed in argv.
+               bufferIndex -= slashesAdded / 2 + 1;
+               buffer[bufferIndex++] = '\"';
+               slashesAdded = 0;
+               escapedQuote = TRUE;
+               ++i;
+               continue;
+            }
+            slashesAdded = 0;
+         }
+         else if (!inQuotes && i > 0 && _acmdln[i - 1] == '\"' && 
!escapedQuote)
+         {
+            buffer[bufferIndex++] = '\"';
+            ++i;
+            escapedQuote = TRUE;
+            continue;
+         }
+         slashesAdded = 0;
+         escapedQuote = FALSE;
+         inQuotes = !inQuotes;
+         doexpand = inQuotes ? FALSE : expand_wildcards;
+         ++i;
+         continue;
+      }
+
+      buffer[bufferIndex++] = _acmdln[i];
+      slashesAdded = 0;
+      escapedQuote = FALSE;
+      ++i;
    }
 
    /* Free the temporary buffer. */
-   free(aNewCmdln);
-
+   free(buffer);
    HeapValidate(GetProcessHeap(), 0, NULL);
 
    *argc = __argc;
    if (__argv == NULL)
    {
-       __argv = (char**)malloc(sizeof(char*));
-       __argv[0] = 0;
+      __argv = (char**)malloc(sizeof(char*));
+      __argv[0] = 0;
    }
    *argv = __argv;
    *env  = _environ;
@@ -269,16 +307,18 @@
 void __wgetmainargs(int* argc, wchar_t*** wargv, wchar_t*** wenv,
                     int expand_wildcards, int* new_mode)
 {
-   int i, afterlastspace, ignorespace, doexpand;
+   int i, doexpand, slashesAdded, escapedQuote, inQuotes, bufferIndex;
    size_t len;
-   wchar_t* wNewCmdln;
+   wchar_t* buffer;
 
    /* missing threading init */
 
    i = 0;
-   afterlastspace = 0;
-   ignorespace = 0;
    doexpand = expand_wildcards;
+   escapedQuote = FALSE;
+   slashesAdded = 0;
+   inQuotes = 0;
+   bufferIndex = 0;
 
    if (__wargv && __winitenv)
    {
@@ -291,58 +331,95 @@
    __argc = 0;
 
    len = wcslen(_wcmdln);
-
-   /* Allocate a temporary buffer to be used instead of the original _wcmdln 
parameter. */
-   wNewCmdln = wcsndup(_wcmdln, len);
-
-   while (wNewCmdln[i])
-   {
-      if (wNewCmdln[i] == L'"')
-      {
-         if(ignorespace)
-         {
-            ignorespace = 0;
-         }
-         else
-         {
-            ignorespace = 1;
-            doexpand = 0;
-         }
-         memmove(wNewCmdln + i, wNewCmdln + i + 1, (len - i) * 
sizeof(wchar_t));
-         len--;
-         continue;
-      }
-
-      if (wNewCmdln[i] == L' ' && !ignorespace)
-      {
-         wexpand(wcsndup(wNewCmdln + afterlastspace, i - afterlastspace), 
doexpand);
-         i++;
-         while (wNewCmdln[i] == L' ')
-            i++;
-         afterlastspace=i;
-         doexpand = expand_wildcards;
-      }
-      else
-      {
-         i++;
-      }
-   }
-
-   if (wNewCmdln[afterlastspace] != 0)
-   {
-      wexpand(wcsndup(wNewCmdln + afterlastspace, i - afterlastspace), 
doexpand);
+   buffer = malloc(sizeof(wchar_t) * len);
+
+   // Reference: 
https://msdn.microsoft.com/en-us/library/a1y7w461(v=vs.71).aspx
+   while (TRUE)
+   {
+      // Arguments are delimited by white space, which is either a space or a 
tab.
+      if (i >= len || ((_wcmdln[i] == ' ' || _wcmdln[i] == '\t') && !inQuotes))
+      {
+         wexpand(wcsndup(buffer, bufferIndex), doexpand);
+         // Copy the last element from buffer and quit the loop
+         if (i >= len)
+         {
+            break;
+         }
+
+         while (_wcmdln[i] == ' ' || _wcmdln[i] == '\t')
+            ++i;
+         bufferIndex = 0;
+         slashesAdded = 0;
+         escapedQuote = FALSE;
+         continue;
+      }
+
+      if (_wcmdln[i] == '\\')
+      {
+         buffer[bufferIndex++] = _wcmdln[i];
+         ++slashesAdded;
+         ++i;
+         escapedQuote = FALSE;
+         continue;
+      }
+
+      if (_wcmdln[i] == '\"')
+      {
+         if (slashesAdded > 0)
+         {
+            if (slashesAdded % 2 == 0)
+            {
+               // If an even number of backslashes is followed by a double 
quotation mark, then one backslash (\)
+               // is placed in the argv array for every pair of backslashes 
(\\), and the double quotation mark (")
+               // is interpreted as a string delimiter.
+               bufferIndex -= slashesAdded / 2;
+            }
+            else
+            {
+               // If an odd number of backslashes is followed by a double 
quotation mark, then one backslash (\)
+               // is placed in the argv array for every pair of backslashes 
(\\) and the double quotation mark is
+               // interpreted as an escape sequence by the remaining 
backslash, causing a literal double quotation mark (")
+               // to be placed in argv.
+               bufferIndex -= slashesAdded / 2 + 1;
+               buffer[bufferIndex++] = '\"';
+               slashesAdded = 0;
+               escapedQuote = TRUE;
+               ++i;
+               continue;
+            }
+            slashesAdded = 0;
+         }
+         else if (!inQuotes && i > 0 && _wcmdln[i - 1] == '\"' && 
!escapedQuote)
+         {
+            buffer[bufferIndex++] = '\"';
+            ++i;
+            escapedQuote = TRUE;
+            continue;
+         }
+         slashesAdded = 0;
+         escapedQuote = FALSE;
+         inQuotes = !inQuotes;
+         doexpand = inQuotes ? FALSE : expand_wildcards;
+         ++i;
+         continue;
+      }
+
+      buffer[bufferIndex++] = _wcmdln[i];
+      slashesAdded = 0;
+      escapedQuote = FALSE;
+      ++i;
    }
 
    /* Free the temporary buffer. */
-   free(wNewCmdln);
+   free(buffer);
 
    HeapValidate(GetProcessHeap(), 0, NULL);
 
    *argc = __argc;
    if (__wargv == NULL)
    {
-       __wargv = (wchar_t**)malloc(sizeof(wchar_t*));
-       __wargv[0] = 0;
+      __wargv = (wchar_t**)malloc(sizeof(wchar_t*));
+      __wargv[0] = 0;
    }
    *wargv = __wargv;
    *wenv = __winitenv;

Added: trunk/rostests/apitests/crt/__getmainargs.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/crt/__getmainargs.c?rev=72214
==============================================================================
--- trunk/rostests/apitests/crt/__getmainargs.c (added)
+++ trunk/rostests/apitests/crt/__getmainargs.c [iso-8859-1] Fri Aug 12 
21:31:32 2016
@@ -0,0 +1,100 @@
+/*
+ * PROJECT:         ReactOS api tests
+ * LICENSE:         GPLv2+ - See COPYING in the top level directory
+ * PURPOSE:         Test for __getmainargs and __wgetmainargs
+ * PROGRAMMER:      Yaroslav Veremenko <yaros...@veremenko.info>
+ */
+
+#include <apitest.h>
+#include <stdio.h>
+#include <string.h>
+
+
+const char **__p__acmdln(void);
+void __getmainargs(int* argc, char*** argv, char*** env, int expand_wildcards, 
int* new_mode);
+const wchar_t **__p__wcmdln(void);
+void __wgetmainargs(int* argc, wchar_t*** wargv, wchar_t*** wenv, int 
expand_wildcards, int* new_mode);
+
+
+#define winetest_ok_str(x, y) \
+    winetest_ok(strcmp(x, y) == 0, "Wrong string. Expected '%s', got '%s'\n", 
y, x)
+#define winetest_ok_wstr(x, y) \
+    winetest_ok(wcscmp(x, y) == 0, "Wrong string. Expected '%s', got '%s'\n", 
wine_dbgstr_w(y), wine_dbgstr_w(x))
+#define ok_argsA  (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : 
ok_argsA_imp
+#define ok_argsW  (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : 
ok_argsW_imp
+
+
+void
+ok_argsA_imp(const char* input_args, const char* arg1, const char* arg2, const 
char* arg3)
+{
+    int argc = 0, mode = 0;
+    char** argv, **env;
+
+    /* Remove cached argv, setup our input as program argument. */
+    *__p___argv() = NULL;
+    *__p__acmdln() = input_args;
+
+    /* Process the commandline stored in _acmdln */
+    __getmainargs(&argc, &argv, &env, 0, &mode);
+
+    winetest_ok(argc == 4, "Wrong value for argc, expected: 4, got: %d\n", 
argc);
+    if(argc != 4)
+        return;
+
+    winetest_ok_str(argv[0], "test.exe");
+    winetest_ok_str(argv[1], arg1);
+    winetest_ok_str(argv[2], arg2);
+    winetest_ok_str(argv[3], arg3);
+}
+
+void
+ok_argsW_imp(const wchar_t* input_args, const wchar_t* arg1, const wchar_t* 
arg2, const wchar_t* arg3)
+{
+    int argc = 0, mode = 0;
+    wchar_t** argv, **env;
+
+    /* Remove cached wargv, setup our input as program argument. */
+    *__p___wargv() = NULL;
+    *__p__wcmdln() = input_args;
+
+    /* Process the commandline stored in _wcmdln */
+    __wgetmainargs(&argc, &argv, &env, 0, &mode);
+
+    winetest_ok(argc == 4, "Wrong value for argc, expected: 4, got: %d\n", 
argc);
+    if(argc != 4)
+        return;
+
+    winetest_ok_wstr(argv[0], L"test.exe");
+    winetest_ok_wstr(argv[1], arg1);
+    winetest_ok_wstr(argv[2], arg2);
+    winetest_ok_wstr(argv[3], arg3);
+}
+
+START_TEST(__getmainargs)
+{
+    ok_argsA("test.exe \"a b c\" d e", "a b c", "d", "e");
+    ok_argsA("test.exe \"ab\\\"c\" \"\\\\\" d", "ab\"c", "\\", "d");
+    ok_argsA("test.exe a\\\\\\b d\"e f\"g h", "a\\\\\\b", "de fg", "h");
+    ok_argsA("test.exe a\\\\\\\"b c d", "a\\\"b", "c", "d");
+    ok_argsA("test.exe a\\\\\\\\\"b c\" d e", "a\\\\b c", "d", "e");
+    ok_argsA("test.exe a b \"\"", "a", "b", "");
+    ok_argsA("test.exe a \"\" b", "a", "", "b");
+    ok_argsA("test.exe a \"b\"\" c", "a", "b\"", "c");
+    ok_argsA("test.exe a \"b\\\"\" c", "a", "b\"", "c");
+    ok_argsA("test.exe a  \"  b\\  \"\"  c", "a", "  b\\  \"", "c");
+    ok_argsA("test.exe a  \"b\\  \"\"\"  c\" d", "a", "b\\  \"  c", "d");
+    ok_argsA("test.exe a  \"b\\  \"\"\"  \"c  \"\"\"\" d", "a", "b\\  \"  c", 
"\" d");
+
+    ok_argsW(L"test.exe \"a b c\" d e", L"a b c", L"d", L"e");
+    ok_argsW(L"test.exe \"ab\\\"c\" \"\\\\\" d", L"ab\"c", L"\\", L"d");
+    ok_argsW(L"test.exe a\\\\\\b d\"e f\"g h", L"a\\\\\\b", L"de fg", L"h");
+    ok_argsW(L"test.exe a\\\\\\\"b c d", L"a\\\"b", L"c", L"d");
+    ok_argsW(L"test.exe a\\\\\\\\\"b c\" d e", L"a\\\\b c", L"d", L"e");
+    ok_argsW(L"test.exe a b \"\"", L"a", L"b", L"");
+    ok_argsW(L"test.exe a \"\" b", L"a", L"", L"b");
+    ok_argsW(L"test.exe a \"b\"\" c", L"a", L"b\"", L"c");
+    ok_argsW(L"test.exe a \"b\\\"\" c", L"a", L"b\"", L"c");
+    ok_argsW(L"test.exe a  \"  b\\  \"\"  c", L"a", L"  b\\  \"", L"c");
+    ok_argsW(L"test.exe a  \"b\\  \"\"\"  c\" d", L"a", L"b\\  \"  c", L"d");
+    ok_argsW(L"test.exe a  \"b\\  \"\"\"  \"c  \"\"\"\" d", L"a", L"b\\  \"  
c", L"\" d");
+}

Propchange: trunk/rostests/apitests/crt/__getmainargs.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: trunk/rostests/apitests/crt/msvcrt_crt_apitest.cmake
URL: 
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/crt/msvcrt_crt_apitest.cmake?rev=72214&r1=72213&r2=72214&view=diff
==============================================================================
--- trunk/rostests/apitests/crt/msvcrt_crt_apitest.cmake        [iso-8859-1] 
(original)
+++ trunk/rostests/apitests/crt/msvcrt_crt_apitest.cmake        [iso-8859-1] 
Fri Aug 12 21:31:32 2016
@@ -66,7 +66,7 @@
 #    __doserrno.c
 #    __fpecode.c
 #    __get_app_type.c
-#    __getmainargs.c
+    __getmainargs.c
 #    __initenv
 #    __iob_func.c
 #    __isascii.c

Modified: trunk/rostests/apitests/crt/testlist.c
URL: 
http://svn.reactos.org/svn/reactos/trunk/rostests/apitests/crt/testlist.c?rev=72214&r1=72213&r2=72214&view=diff
==============================================================================
--- trunk/rostests/apitests/crt/testlist.c      [iso-8859-1] (original)
+++ trunk/rostests/apitests/crt/testlist.c      [iso-8859-1] Fri Aug 12 
21:31:32 2016
@@ -25,6 +25,7 @@
 extern void func_wcsnlen(void);
 extern void func_wcstombs(void);
 extern void func_wcstoul(void);
+extern void func___getmainargs(void);
 
 extern void func_static_construct(void);
 extern void func_static_init(void);
@@ -50,6 +51,7 @@
 #endif
 #if defined(TEST_STATIC_CRT)
 #elif defined(TEST_MSVCRT)
+    { "__getmainargs", func___getmainargs },
     { "_vscprintf", func__vscprintf },
     { "_vscwprintf", func__vscwprintf },
 


Reply via email to