[CRT][CRT_APITEST] Fix __getmainargs and __wgetmainargs parsing, verified with apitests. Patch by Yaroslav Veremenko. CORE-11673 #resolve #comment Thanks!

svn path=/trunk/; revision=72214
This commit is contained in:
Mark Jansen
2016-08-12 21:31:32 +00:00
parent f7cb5ca5c0
commit caa5c5b12d
4 changed files with 253 additions and 74 deletions
+150 -73
View File
@@ -181,16 +181,18 @@ int aexpand(char* name, int expand_wildcards)
*/
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 @@ void __getmainargs(int* argc, char*** argv, char*** env, int expand_wildcards, i
__argc = 0;
len = strlen(_acmdln);
buffer = malloc(sizeof(char) * len);
/* Allocate a temporary buffer to be used instead of the original _acmdln parameter. */
aNewCmdln = strndup(_acmdln, len);
while (aNewCmdln[i])
// Reference: https://msdn.microsoft.com/en-us/library/a1y7w461(v=vs.71).aspx
while (TRUE)
{
if (aNewCmdln[i] == '"')
// Arguments are delimited by white space, which is either a space or a tab.
if (i >= len || ((_acmdln[i] == ' ' || _acmdln[i] == '\t') && !inQuotes))
{
if(ignorespace)
aexpand(strndup(buffer, bufferIndex), doexpand);
// Copy the last element from buffer and quit the loop
if (i >= len)
{
ignorespace = 0;
break;
}
else
{
ignorespace = 1;
doexpand = 0;
}
memmove(aNewCmdln + i, aNewCmdln + i + 1, len - i);
len--;
while (_acmdln[i] == ' ' || _acmdln[i] == '\t')
++i;
bufferIndex = 0;
slashesAdded = 0;
escapedQuote = FALSE;
continue;
}
if (aNewCmdln[i] == ' ' && !ignorespace)
if (_acmdln[i] == '\\')
{
aexpand(strndup(aNewCmdln + afterlastspace, i - afterlastspace), doexpand);
i++;
while (aNewCmdln[i] == ' ')
i++;
afterlastspace=i;
doexpand = expand_wildcards;
buffer[bufferIndex++] = _acmdln[i];
++slashesAdded;
++i;
escapedQuote = FALSE;
continue;
}
else
{
i++;
}
}
if (aNewCmdln[afterlastspace] != 0)
{
aexpand(strndup(aNewCmdln + afterlastspace, i - afterlastspace), doexpand);
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 __getmainargs(int* argc, char*** argv, char*** env, int expand_wildcards, i
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 @@ void __wgetmainargs(int* argc, wchar_t*** wargv, wchar_t*** wenv,
__argc = 0;
len = wcslen(_wcmdln);
buffer = malloc(sizeof(wchar_t) * len);
/* Allocate a temporary buffer to be used instead of the original _wcmdln parameter. */
wNewCmdln = wcsndup(_wcmdln, len);
while (wNewCmdln[i])
// Reference: https://msdn.microsoft.com/en-us/library/a1y7w461(v=vs.71).aspx
while (TRUE)
{
if (wNewCmdln[i] == L'"')
// Arguments are delimited by white space, which is either a space or a tab.
if (i >= len || ((_wcmdln[i] == ' ' || _wcmdln[i] == '\t') && !inQuotes))
{
if(ignorespace)
wexpand(wcsndup(buffer, bufferIndex), doexpand);
// Copy the last element from buffer and quit the loop
if (i >= len)
{
ignorespace = 0;
break;
}
else
{
ignorespace = 1;
doexpand = 0;
}
memmove(wNewCmdln + i, wNewCmdln + i + 1, (len - i) * sizeof(wchar_t));
len--;
while (_wcmdln[i] == ' ' || _wcmdln[i] == '\t')
++i;
bufferIndex = 0;
slashesAdded = 0;
escapedQuote = FALSE;
continue;
}
if (wNewCmdln[i] == L' ' && !ignorespace)
if (_wcmdln[i] == '\\')
{
wexpand(wcsndup(wNewCmdln + afterlastspace, i - afterlastspace), doexpand);
i++;
while (wNewCmdln[i] == L' ')
i++;
afterlastspace=i;
doexpand = expand_wildcards;
buffer[bufferIndex++] = _wcmdln[i];
++slashesAdded;
++i;
escapedQuote = FALSE;
continue;
}
else
{
i++;
}
}
if (wNewCmdln[afterlastspace] != 0)
{
wexpand(wcsndup(wNewCmdln + afterlastspace, i - afterlastspace), doexpand);
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;
+100
View File
@@ -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 <[email protected]>
*/
#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");
}
@@ -66,7 +66,7 @@ list(APPEND SOURCE_MSVCRT
# __doserrno.c
# __fpecode.c
# __get_app_type.c
# __getmainargs.c
__getmainargs.c
# __initenv
# __iob_func.c
# __isascii.c
+2
View File
@@ -25,6 +25,7 @@ extern void func_strtoul(void);
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 @@ const struct test winetest_testlist[] =
#endif
#if defined(TEST_STATIC_CRT)
#elif defined(TEST_MSVCRT)
{ "__getmainargs", func___getmainargs },
{ "_vscprintf", func__vscprintf },
{ "_vscwprintf", func__vscwprintf },