20#include <winpr/config.h>
23#include <winpr/assert.h>
24#include <winpr/cmdline.h>
28#define TAG WINPR_TAG("commandline")
50static void log_error(DWORD flags, LPCSTR message,
int index, LPCSTR argv)
52 if ((flags & COMMAND_LINE_SILENCE_PARSER) == 0)
53 WLog_ERR(TAG, message, index, argv);
57 void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter,
58 COMMAND_LINE_POST_FILTER_FN_A postFilter)
62 BOOL notescaped = FALSE;
63 const char* sigil = NULL;
64 size_t sigil_length = 0;
66 size_t keyword_index = 0;
67 char* separator = NULL;
76 if (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD)
79 status = COMMAND_LINE_STATUS_PRINT_HELP;
84 for (
int i = 1; i < argc; i++)
86 size_t keyword_length = 0;
92 count = preFilter(context, i, argc, argv);
96 log_error(flags,
"Failed for index %d [%s]: PreFilter rule could not be applied", i,
98 status = COMMAND_LINE_ERROR;
110 size_t length = strlen(argv[i]);
112 if ((sigil[0] ==
'/') && (flags & COMMAND_LINE_SIGIL_SLASH))
116 else if ((sigil[0] ==
'-') && (flags & COMMAND_LINE_SIGIL_DASH))
122 if ((sigil[1] ==
'-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH))
126 else if ((sigil[0] ==
'+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
130 else if ((sigil[0] ==
'-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
134 else if (flags & COMMAND_LINE_SIGIL_NONE)
138 else if (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED)
142 log_error(flags,
"Failed at index %d [%s]: Unescaped sigil", i, argv[i]);
143 return COMMAND_LINE_ERROR;
152 log_error(flags,
"Failed at index %d [%s]: Invalid sigil", i, argv[i]);
153 return COMMAND_LINE_ERROR;
156 if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE) ||
157 (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED))
159 if (length < (sigil_length + 1))
161 if ((flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD))
164 return COMMAND_LINE_ERROR_NO_KEYWORD;
167 keyword_index = sigil_length;
168 keyword = &argv[i][keyword_index];
171 if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
173 if (strncmp(keyword,
"enable-", 7) == 0)
177 keyword = &argv[i][keyword_index];
179 else if (strncmp(keyword,
"disable-", 8) == 0)
183 keyword = &argv[i][keyword_index];
189 if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator))
190 separator = strchr(keyword,
':');
192 if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator))
193 separator = strchr(keyword,
'=');
197 SSIZE_T separator_index = (separator - argv[i]);
198 SSIZE_T value_index = separator_index + 1;
199 keyword_length = WINPR_ASSERTING_INT_CAST(
size_t, (separator - keyword));
200 value = &argv[i][value_index];
204 if (length < keyword_index)
206 log_error(flags,
"Failed at index %d [%s]: Argument required", i, argv[i]);
207 return COMMAND_LINE_ERROR;
210 keyword_length = length - keyword_index;
217 for (
size_t j = 0; options[j].Name != NULL; j++)
222 if (strncmp(cur->Name, keyword, keyword_length) == 0)
224 if (strlen(cur->Name) == keyword_length)
228 if ((!match) && (cur->Alias != NULL))
230 if (strncmp(cur->Alias, keyword, keyword_length) == 0)
232 if (strlen(cur->Alias) == keyword_length)
243 if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc))
246 int value_present = 1;
248 if (flags & COMMAND_LINE_SIGIL_DASH)
250 if (strncmp(argv[i + 1],
"-", 1) == 0)
254 if (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH)
256 if (strncmp(argv[i + 1],
"--", 2) == 0)
260 if (flags & COMMAND_LINE_SIGIL_SLASH)
262 if (strncmp(argv[i + 1],
"/", 1) == 0)
266 if ((cur->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
267 (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL))
272 if (value_present && argument)
277 else if (!value_present && (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL))
281 else if (!value_present && argument)
283 log_error(flags,
"Failed at index %d [%s]: Argument required", i, argv[i]);
284 return COMMAND_LINE_ERROR;
288 if (!(flags & COMMAND_LINE_SEPARATOR_SPACE))
290 if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG))
292 log_error(flags,
"Failed at index %d [%s]: Unexpected value", i, argv[i]);
293 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
298 if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG))
305 if (!value && (cur->Flags & COMMAND_LINE_VALUE_REQUIRED))
307 log_error(flags,
"Failed at index %d [%s]: Missing value", i, argv[i]);
308 status = COMMAND_LINE_ERROR_MISSING_VALUE;
312 cur->Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
316 if (!(cur->Flags & (COMMAND_LINE_VALUE_OPTIONAL | COMMAND_LINE_VALUE_REQUIRED)))
318 log_error(flags,
"Failed at index %d [%s]: Unexpected value", i, argv[i]);
319 return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
323 cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
327 if (cur->Flags & COMMAND_LINE_VALUE_FLAG)
329 cur->Value = (LPSTR)1;
330 cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
332 else if (cur->Flags & COMMAND_LINE_VALUE_BOOL)
334 if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
337 cur->Value = BoolValueTrue;
339 cur->Value = BoolValueFalse;
341 cur->Value = BoolValueTrue;
346 cur->Value = BoolValueTrue;
347 else if (sigil[0] ==
'-')
348 cur->Value = BoolValueFalse;
350 cur->Value = BoolValueTrue;
353 cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
359 count = postFilter(context, &options[j]);
364 "Failed at index %d [%s]: PostFilter rule could not be applied",
366 status = COMMAND_LINE_ERROR;
371 if (cur->Flags & COMMAND_LINE_PRINT)
372 return COMMAND_LINE_STATUS_PRINT;
373 else if (cur->Flags & COMMAND_LINE_PRINT_HELP)
374 return COMMAND_LINE_STATUS_PRINT_HELP;
375 else if (cur->Flags & COMMAND_LINE_PRINT_VERSION)
376 return COMMAND_LINE_STATUS_PRINT_VERSION;
377 else if (cur->Flags & COMMAND_LINE_PRINT_BUILDCONFIG)
378 return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
381 if (!found && (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD) == 0)
383 log_error(flags,
"Failed at index %d [%s]: Unexpected keyword", i, argv[i]);
384 return COMMAND_LINE_ERROR_NO_KEYWORD;
392int CommandLineParseArgumentsW(WINPR_ATTR_UNUSED
int argc, WINPR_ATTR_UNUSED LPWSTR* argv,
394 WINPR_ATTR_UNUSED DWORD flags, WINPR_ATTR_UNUSED
void* context,
395 WINPR_ATTR_UNUSED COMMAND_LINE_PRE_FILTER_FN_W preFilter,
396 WINPR_ATTR_UNUSED COMMAND_LINE_POST_FILTER_FN_W postFilter)
398 WLog_ERR(
"TODO",
"TODO: implement");
404 for (
size_t i = 0; options[i].Name != NULL; i++)
406 options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
407 options[i].Value = NULL;
415 for (
int i = 0; options[i].Name != NULL; i++)
417 options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
418 options[i].Value = NULL;
427 WINPR_ASSERT(options);
430 for (
size_t i = 0; options[i].Name != NULL; i++)
432 if (strcmp(options[i].Name, Name) == 0)
435 if (options[i].Alias != NULL)
437 if (strcmp(options[i].Alias, Name) == 0)
448 WINPR_ASSERT(options);
451 for (
size_t i = 0; options[i].Name != NULL; i++)
453 if (_wcscmp(options[i].Name, Name) == 0)
456 if (options[i].Alias != NULL)
458 if (_wcscmp(options[i].Alias, Name) == 0)
470 if (!argument || !argument->Name)
473 nextArgument = &argument[1];
475 if (nextArgument->Name == NULL)
481static int is_quoted(
char c)
494static size_t get_element_count(
const char* list, BOOL* failed, BOOL fullquoted)
498 BOOL finished = FALSE;
500 const char* it = list;
504 if (strlen(list) == 0)
509 BOOL nextFirst = FALSE;
515 WLog_ERR(TAG,
"Invalid argument (missing closing quote) '%s'", list);
525 int now = is_quoted(*it);
528 else if (quoted == 0)
535 WLog_ERR(TAG,
"Invalid argument (empty list elements) '%s'", list);
555static char* get_next_comma(
char*
string, BOOL fullquoted)
557 const char* log = string;
561 WINPR_ASSERT(
string);
569 WLog_ERR(TAG,
"Invalid quoted argument '%s'", log);
576 int now = is_quoted(*
string);
577 if ((quoted == 0) && !first)
579 WLog_ERR(TAG,
"Invalid quoted argument '%s'", log);
584 else if (quoted == 0)
592 WLog_ERR(TAG,
"Invalid argument (empty list elements) '%s'", log);
609static BOOL is_valid_fullquoted(
const char*
string)
613 const char quote = *
string++;
616 if (is_quoted(quote) == 0)
619 while ((cur = *
string++) !=
'\0')
632 else if (*
string !=
'\0')
644char** CommandLineParseCommaSeparatedValuesEx(
const char* name,
const char* list,
size_t* count)
654 char* unquoted = NULL;
655 BOOL fullquoted = FALSE;
657 BOOL success = FALSE;
666 unquoted = copy = _strdup(list);
670 len = strlen(unquoted);
673 start = is_quoted(unquoted[0]);
674 end = is_quoted(unquoted[len - 1]);
676 if ((start != 0) && (end != 0))
680 WLog_ERR(TAG,
"invalid argument (quote mismatch) '%s'", list);
683 if (!is_valid_fullquoted(unquoted))
685 unquoted[len - 1] =
'\0';
693 *count = get_element_count(unquoted, &failed, fullquoted);
703 size_t clen = strlen(name);
704 p = (
char**)calloc(2UL + clen,
sizeof(
char*));
708 char* dst = (
char*)&p[1];
710 (void)sprintf_s(dst, clen + 1,
"%s", name);
723 prefix = (nArgs + 1UL) *
sizeof(
char*);
725 namelen = strlen(name);
726 p = (
char**)calloc(len + prefix + 1 + namelen + 1,
sizeof(
char*));
731 str = &((
char*)p)[prefix];
732 memcpy(str, unquoted, len);
736 char* namestr = &((
char*)p)[prefix + len + 1];
737 memcpy(namestr, name, namelen);
742 for (
size_t index = name ? 1 : 0; index < nArgs; index++)
745 const int quote = is_quoted(*ptr);
746 char* comma = get_next_comma(str, fullquoted);
748 if ((quote != 0) && !fullquoted)
755 char* last = comma - 1;
756 const int lastQuote = is_quoted(*last);
760 if (lastQuote != quote)
762 WLog_ERR(TAG,
"invalid argument (quote mismatch) '%s'", list);
765 else if (lastQuote != 0)
774 char* end = strrchr(ptr,
'"');
795char** CommandLineParseCommaSeparatedValues(
const char* list,
size_t* count)
797 return CommandLineParseCommaSeparatedValuesEx(NULL, list, count);
800char* CommandLineToCommaSeparatedValues(
int argc,
char* argv[])
802 return CommandLineToCommaSeparatedValuesEx(argc, argv, NULL, 0);
805static const char* filtered(
const char* arg,
const char* filters[],
size_t number)
809 for (
size_t x = 0; x < number; x++)
811 const char* filter = filters[x];
812 size_t len = strlen(filter);
813 if (_strnicmp(arg, filter, len) == 0)
819char* CommandLineToCommaSeparatedValuesEx(
int argc,
char* argv[],
const char* filters[],
824 size_t size = WINPR_ASSERTING_INT_CAST(
size_t, argc) + 1;
825 if ((argc <= 0) || !argv)
828 for (
int x = 0; x < argc; x++)
829 size += strlen(argv[x]);
831 str = calloc(size,
sizeof(
char));
834 for (
int x = 0; x < argc; x++)
837 const char* arg = filtered(argv[x], filters, number);
840 rc = _snprintf(&str[offset], size - offset,
"%s,", arg);
846 offset += (size_t)rc;
849 str[offset - 1] =
'\0';
853void CommandLineParserFree(
char** ptr)