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…
==============================================================================
--- 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/__getmainarg…
==============================================================================
--- 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 <yaroslav(a)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_a…
==============================================================================
--- 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?r…
==============================================================================
--- 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 },