Added CommandLineParseCommaSeparatedValuesEx single quote support

This commit is contained in:
Armin Novak 2022-02-24 08:46:18 +01:00 committed by akallabeth
parent 34c5b65064
commit 6e2c5f2273
2 changed files with 150 additions and 36 deletions

View File

@ -483,10 +483,23 @@ const COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(const COMMAND_LINE_A
return nextArgument;
}
static size_t get_element_count(const char* list, BOOL* failed)
static int is_quoted(char c)
{
switch (c)
{
case '"':
return 1;
case '\'':
return -1;
default:
return 0;
}
}
static size_t get_element_count(const char* list, BOOL* failed, BOOL fullquoted)
{
size_t count = 0;
BOOL quoted = FALSE;
int quoted = 0;
BOOL finished = FALSE;
BOOL first = TRUE;
const char* it = list;
@ -502,7 +515,7 @@ static size_t get_element_count(const char* list, BOOL* failed)
switch (*it)
{
case '\0':
if (quoted)
if (quoted != 0)
{
WLog_ERR(TAG, "Invalid argument (missing closing quote) '%s'", list);
*failed = TRUE;
@ -510,14 +523,23 @@ static size_t get_element_count(const char* list, BOOL* failed)
}
finished = TRUE;
break;
case '\'':
case '"':
if (!quoted && !first)
if (!fullquoted)
{
WLog_ERR(TAG, "Invalid argument (misplaced quote) '%s'", list);
*failed = TRUE;
return 0;
int now = is_quoted(*it);
if ((quoted == 0) && !first)
{
WLog_ERR(TAG, "Invalid argument (misplaced quote) '%s'", list);
*failed = TRUE;
return 0;
}
if (now == quoted)
quoted = 0;
else if (quoted == 0)
quoted = now;
}
quoted = !quoted;
break;
case ',':
if (first)
@ -526,7 +548,7 @@ static size_t get_element_count(const char* list, BOOL* failed)
*failed = TRUE;
return 0;
}
if (!quoted)
if (quoted == 0)
{
nextFirst = TRUE;
count++;
@ -542,10 +564,10 @@ static size_t get_element_count(const char* list, BOOL* failed)
return count + 1;
}
static char* get_next_comma(char* string)
static char* get_next_comma(char* string, BOOL fullquoted)
{
const char* log = string;
BOOL quoted = FALSE;
int quoted = 0;
BOOL first = TRUE;
WINPR_ASSERT(string);
@ -555,17 +577,25 @@ static char* get_next_comma(char* string)
switch (*string)
{
case '\0':
if (quoted)
if (quoted != 0)
WLog_ERR(TAG, "Invalid quoted argument '%s'", log);
return NULL;
case '\'':
case '"':
if (!quoted && !first)
if (!fullquoted)
{
WLog_ERR(TAG, "Invalid quoted argument '%s'", log);
return NULL;
int now = is_quoted(*string);
if ((quoted == 0) && !first)
{
WLog_ERR(TAG, "Invalid quoted argument '%s'", log);
return NULL;
}
if (now == quoted)
quoted = 0;
else if (quoted == 0)
quoted = now;
}
quoted = !quoted;
break;
case ',':
@ -574,7 +604,7 @@ static char* get_next_comma(char* string)
WLog_ERR(TAG, "Invalid argument (empty list elements) '%s'", log);
return NULL;
}
if (!quoted)
if (quoted == 0)
return string;
break;
@ -588,6 +618,41 @@ static char* get_next_comma(char* string)
return NULL;
}
static BOOL is_valid_fullquoted(const char* string)
{
char cur = '\0';
char last = '\0';
const char quote = *string++;
/* We did not start with a quote. */
if (is_quoted(quote) == 0)
return FALSE;
while ((cur = *string++) != '\0')
{
/* A quote is found. */
if (cur == quote)
{
/* If the quote was escaped, it is valid. */
if (last != '\\')
{
/* Only allow unescaped quote as last character in string. */
if (*string != '\0')
return FALSE;
}
/* If the last quote in the string is escaped, it is wrong. */
else if (*string != '\0')
return FALSE;
}
last = cur;
}
/* The string did not terminate with the same quote as it started. */
if (last != quote)
return FALSE;
return TRUE;
}
char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list, size_t* count)
{
char** p = NULL;
@ -600,6 +665,7 @@ char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list
BOOL failed = FALSE;
char* copy = NULL;
char* unquoted = NULL;
BOOL fullquoted = FALSE;
if (count == NULL)
goto fail;
@ -607,20 +673,35 @@ char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list
*count = 0;
if (list)
{
int start, end;
unquoted = copy = _strdup(list);
if (!copy)
goto fail;
len = strlen(unquoted);
if ((unquoted[0] == '"') && (unquoted[len - 1] == '"'))
if (len > 0)
{
unquoted[len - 1] = '\0';
unquoted++;
len -= 2;
start = is_quoted(unquoted[0]);
end = is_quoted(unquoted[len - 1]);
if ((start != 0) && (end != 0))
{
if (start != end)
{
WLog_ERR(TAG, "invalid argument (quote mismatch) '%s'", list);
goto fail;
}
if (!is_valid_fullquoted(unquoted))
goto fail;
unquoted[len - 1] = '\0';
unquoted++;
len -= 2;
fullquoted = TRUE;
}
}
}
*count = get_element_count(unquoted, &failed);
*count = get_element_count(unquoted, &failed, fullquoted);
if (failed)
goto fail;
@ -671,9 +752,10 @@ char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list
for (index = name ? 1 : 0; index < nArgs; index++)
{
char* ptr = str;
char* comma = get_next_comma(str);
const int quote = is_quoted(*ptr);
char* comma = get_next_comma(str, fullquoted);
if (*ptr == '"')
if ((quote != 0) && !fullquoted)
ptr++;
p[index] = ptr;
@ -681,8 +763,20 @@ char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list
if (comma)
{
char* last = comma - 1;
if (*last == '"')
*last = '\0';
const int lastQuote = is_quoted(*last);
if (!fullquoted)
{
if (lastQuote != quote)
{
WLog_ERR(TAG, "invalid argument (quote mismatch) '%s'", list);
free(p);
p = NULL;
goto fail;
}
else if (lastQuote != 0)
*last = '\0';
}
*comma = '\0';
str = comma + 1;

View File

@ -20,9 +20,10 @@ static const char* testArgv[] = { "mstsc.exe",
0 };
static const char testListAppName[] = "test app name";
static const char* testListArgs[] = { "a,b,c,d", "a:,\"b:xxx, yyy\",c", "a:,,,b",
"a:,\",b", "\"a,b,c,d d d,fff\"", "",
NULL };
static const char* testListArgs[] = {
"a,b,c,d", "a:,\"b:xxx, yyy\",c", "a:,,,b", "a:,\",b", "\"a,b,c,d d d,fff\"", "",
NULL, "'a,b,\",c'", "\"a,b,',c\"", "', a, ', b,c'", "\"a,b,\",c\""
};
static const char* testListArgs1[] = { testListAppName, "a", "b", "c", "d" };
static const char* testListArgs2[] = { testListAppName, "a:", "b:xxx, yyy", "c" };
@ -31,15 +32,34 @@ static const char* testListArgs2[] = { testListAppName, "a:", "b:xxx, yyy", "c"
static const char* testListArgs5[] = { testListAppName, "a", "b", "c", "d d d", "fff" };
static const char* testListArgs6[] = { testListAppName };
static const char* testListArgs7[] = { testListAppName };
static const char* testListArgs8[] = { testListAppName, "a", "b", "\"", "c" };
static const char* testListArgs9[] = { testListAppName, "a", "b", "'", "c" };
// static const char* testListArgs10[] = {};
// static const char* testListArgs11[] = {};
static const char** testListArgsResult[] = {
testListArgs1, testListArgs2, NULL /* testListArgs3 */, NULL /* testListArgs4 */, testListArgs5,
testListArgs6, testListArgs7
};
static const char** testListArgsResult[] = { testListArgs1,
testListArgs2,
NULL /* testListArgs3 */,
NULL /* testListArgs4 */,
testListArgs5,
testListArgs6,
testListArgs7,
testListArgs8,
testListArgs9,
NULL /* testListArgs10 */,
NULL /* testListArgs11 */ };
static const size_t testListArgsCount[] = {
ARRAYSIZE(testListArgs1), ARRAYSIZE(testListArgs2), 0 /* ARRAYSIZE(testListArgs3) */,
0 /* ARRAYSIZE(testListArgs4) */, ARRAYSIZE(testListArgs5), ARRAYSIZE(testListArgs6),
ARRAYSIZE(testListArgs7)
ARRAYSIZE(testListArgs1),
ARRAYSIZE(testListArgs2),
0 /* ARRAYSIZE(testListArgs3) */,
0 /* ARRAYSIZE(testListArgs4) */,
ARRAYSIZE(testListArgs5),
ARRAYSIZE(testListArgs6),
ARRAYSIZE(testListArgs7),
ARRAYSIZE(testListArgs8),
ARRAYSIZE(testListArgs9),
0 /* ARRAYSIZE(testListArgs10) */,
0 /* ARRAYSIZE(testListArgs11) */
};
static BOOL checkResult(size_t index, char** actual, size_t actualCount)