123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- /**
- * WinPR: Windows Portable Runtime
- * Command-Line Utils
- *
- * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <winpr/crt.h>
- #include <winpr/cmdline.h>
- #include "../log.h"
- #define TAG WINPR_TAG("commandline")
- /**
- * Command-line syntax: some basic concepts:
- * https://pythonconquerstheuniverse.wordpress.com/2010/07/25/command-line-syntax-some-basic-concepts/
- */
- /**
- * Command-Line Syntax:
- *
- * <sigil><keyword><separator><value>
- *
- * <sigil>: '/' or '-' or ('+' | '-')
- *
- * <keyword>: option, named argument, flag
- *
- * <separator>: ':' or '='
- *
- * <value>: argument value
- *
- */
- static void log_error(DWORD flags, LPCSTR message, int index, LPCSTR argv)
- {
- if ((flags & COMMAND_LINE_SILENCE_PARSER) == 0)
- WLog_ERR(TAG, message, index, argv);
- }
- int CommandLineParseArgumentsA(int argc, LPSTR* argv, COMMAND_LINE_ARGUMENT_A* options, DWORD flags,
- void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter,
- COMMAND_LINE_POST_FILTER_FN_A postFilter)
- {
- int i, j;
- int status;
- int count;
- size_t length;
- BOOL notescaped;
- const char* sigil;
- size_t sigil_length;
- char* keyword;
- size_t keyword_length;
- SSIZE_T keyword_index;
- char* separator;
- char* value;
- int toggle;
- status = 0;
- notescaped = FALSE;
- if (!argv)
- return status;
- if (argc == 1)
- {
- if (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD)
- status = 0;
- else
- status = COMMAND_LINE_STATUS_PRINT_HELP;
- return status;
- }
- for (i = 1; i < argc; i++)
- {
- BOOL found = FALSE;
- BOOL escaped = TRUE;
- if (preFilter)
- {
- count = preFilter(context, i, argc, argv);
- if (count < 0)
- {
- log_error(flags, "Failed for index %d [%s]: PreFilter rule could not be applied", i,
- argv[i]);
- status = COMMAND_LINE_ERROR;
- return status;
- }
- if (count > 0)
- {
- i += (count - 1);
- continue;
- }
- }
- sigil = argv[i];
- length = strlen(argv[i]);
- if ((sigil[0] == '/') && (flags & COMMAND_LINE_SIGIL_SLASH))
- {
- sigil_length = 1;
- }
- else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_DASH))
- {
- sigil_length = 1;
- if (length > 2)
- {
- if ((sigil[1] == '-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH))
- sigil_length = 2;
- }
- }
- else if ((sigil[0] == '+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
- {
- sigil_length = 1;
- }
- else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
- {
- sigil_length = 1;
- }
- else if (flags & COMMAND_LINE_SIGIL_NONE)
- {
- sigil_length = 0;
- }
- else if (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED)
- {
- if (notescaped)
- {
- log_error(flags, "Failed at index %d [%s]: Unescaped sigil", i, argv[i]);
- return COMMAND_LINE_ERROR;
- }
- sigil_length = 0;
- escaped = FALSE;
- notescaped = TRUE;
- }
- else
- {
- log_error(flags, "Failed at index %d [%s]: Invalid sigil", i, argv[i]);
- return COMMAND_LINE_ERROR;
- }
- if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE) ||
- (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED))
- {
- if (length < (sigil_length + 1))
- {
- if ((flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD))
- continue;
- return COMMAND_LINE_ERROR_NO_KEYWORD;
- }
- keyword_index = sigil_length;
- keyword = &argv[i][keyword_index];
- toggle = -1;
- if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
- {
- if (strncmp(keyword, "enable-", 7) == 0)
- {
- toggle = TRUE;
- keyword_index += 7;
- keyword = &argv[i][keyword_index];
- }
- else if (strncmp(keyword, "disable-", 8) == 0)
- {
- toggle = FALSE;
- keyword_index += 8;
- keyword = &argv[i][keyword_index];
- }
- }
- separator = NULL;
- if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator))
- separator = strchr(keyword, ':');
- if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator))
- separator = strchr(keyword, '=');
- if (separator)
- {
- SSIZE_T separator_index = (separator - argv[i]);
- SSIZE_T value_index = separator_index + 1;
- keyword_length = (separator - keyword);
- value = &argv[i][value_index];
- }
- else
- {
- keyword_length = (length - keyword_index);
- value = NULL;
- }
- if (!escaped)
- continue;
- for (j = 0; options[j].Name != NULL; j++)
- {
- BOOL match = FALSE;
- if (strncmp(options[j].Name, keyword, keyword_length) == 0)
- {
- if (strlen(options[j].Name) == keyword_length)
- match = TRUE;
- }
- if ((!match) && (options[j].Alias != NULL))
- {
- if (strncmp(options[j].Alias, keyword, keyword_length) == 0)
- {
- if (strlen(options[j].Alias) == keyword_length)
- match = TRUE;
- }
- }
- if (!match)
- continue;
- found = match;
- options[j].Index = i;
- if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc))
- {
- BOOL argument;
- int value_present = 1;
- if (flags & COMMAND_LINE_SIGIL_DASH)
- {
- if (strncmp(argv[i + 1], "-", 1) == 0)
- value_present = 0;
- }
- if (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH)
- {
- if (strncmp(argv[i + 1], "--", 2) == 0)
- value_present = 0;
- }
- if (flags & COMMAND_LINE_SIGIL_SLASH)
- {
- if (strncmp(argv[i + 1], "/", 1) == 0)
- value_present = 0;
- }
- if ((options[j].Flags & COMMAND_LINE_VALUE_REQUIRED) ||
- (options[j].Flags & COMMAND_LINE_VALUE_OPTIONAL))
- argument = TRUE;
- else
- argument = FALSE;
- if (value_present && argument)
- {
- i++;
- value = argv[i];
- }
- else if (!value_present && (options[j].Flags & COMMAND_LINE_VALUE_OPTIONAL))
- {
- value = NULL;
- }
- else if (!value_present && argument)
- {
- log_error(flags, "Failed at index %d [%s]: Argument required", i, argv[i]);
- return COMMAND_LINE_ERROR;
- }
- }
- if (!(flags & COMMAND_LINE_SEPARATOR_SPACE))
- {
- if (value && (options[j].Flags & COMMAND_LINE_VALUE_FLAG))
- {
- log_error(flags, "Failed at index %d [%s]: Unexpected value", i, argv[i]);
- return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
- }
- }
- else
- {
- if (value && (options[j].Flags & COMMAND_LINE_VALUE_FLAG))
- {
- i--;
- value = NULL;
- }
- }
- if (!value && (options[j].Flags & COMMAND_LINE_VALUE_REQUIRED))
- {
- log_error(flags, "Failed at index %d [%s]: Missing value", i, argv[i]);
- status = COMMAND_LINE_ERROR_MISSING_VALUE;
- return status;
- }
- options[j].Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
- if (value)
- {
- if (options[j].Flags & (COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_VALUE_BOOL))
- {
- log_error(flags, "Failed at index %d [%s]: Unexpected value", i, argv[i]);
- return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
- }
- options[j].Value = value;
- options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
- }
- else
- {
- if (options[j].Flags & COMMAND_LINE_VALUE_FLAG)
- {
- options[j].Value = (LPSTR)1;
- options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
- }
- else if (options[j].Flags & COMMAND_LINE_VALUE_BOOL)
- {
- if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
- {
- if (toggle == -1)
- options[j].Value = BoolValueTrue;
- else if (!toggle)
- options[j].Value = BoolValueFalse;
- else
- options[j].Value = BoolValueTrue;
- }
- else
- {
- if (sigil[0] == '+')
- options[j].Value = BoolValueTrue;
- else if (sigil[0] == '-')
- options[j].Value = BoolValueFalse;
- else
- options[j].Value = BoolValueTrue;
- }
- options[j].Flags |= COMMAND_LINE_VALUE_PRESENT;
- }
- }
- if (postFilter)
- {
- count = postFilter(context, &options[j]);
- if (count < 0)
- {
- log_error(flags,
- "Failed at index %d [%s]: PostFilter rule could not be applied",
- i, argv[i]);
- status = COMMAND_LINE_ERROR;
- return status;
- }
- }
- if (options[j].Flags & COMMAND_LINE_PRINT)
- return COMMAND_LINE_STATUS_PRINT;
- else if (options[j].Flags & COMMAND_LINE_PRINT_HELP)
- return COMMAND_LINE_STATUS_PRINT_HELP;
- else if (options[j].Flags & COMMAND_LINE_PRINT_VERSION)
- return COMMAND_LINE_STATUS_PRINT_VERSION;
- else if (options[j].Flags & COMMAND_LINE_PRINT_BUILDCONFIG)
- return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
- }
- if (!found && (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD) == 0)
- {
- log_error(flags, "Failed at index %d [%s]: Unexpected keyword", i, argv[i]);
- return COMMAND_LINE_ERROR_NO_KEYWORD;
- }
- }
- }
- return status;
- }
- int CommandLineParseArgumentsW(int argc, LPWSTR* argv, COMMAND_LINE_ARGUMENT_W* options,
- DWORD flags, void* context, COMMAND_LINE_PRE_FILTER_FN_W preFilter,
- COMMAND_LINE_POST_FILTER_FN_W postFilter)
- {
- return 0;
- }
- int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options)
- {
- size_t i;
- for (i = 0; options[i].Name != NULL; i++)
- {
- options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
- options[i].Value = NULL;
- }
- return 0;
- }
- int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options)
- {
- int i;
- for (i = 0; options[i].Name != NULL; i++)
- {
- options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
- options[i].Value = NULL;
- }
- return 0;
- }
- COMMAND_LINE_ARGUMENT_A* CommandLineFindArgumentA(COMMAND_LINE_ARGUMENT_A* options, LPCSTR Name)
- {
- int i;
- for (i = 0; options[i].Name != NULL; i++)
- {
- if (strcmp(options[i].Name, Name) == 0)
- return &options[i];
- if (options[i].Alias != NULL)
- {
- if (strcmp(options[i].Alias, Name) == 0)
- return &options[i];
- }
- }
- return NULL;
- }
- COMMAND_LINE_ARGUMENT_W* CommandLineFindArgumentW(COMMAND_LINE_ARGUMENT_W* options, LPCWSTR Name)
- {
- int i;
- for (i = 0; options[i].Name != NULL; i++)
- {
- if (_wcscmp(options[i].Name, Name) == 0)
- return &options[i];
- if (options[i].Alias != NULL)
- {
- if (_wcscmp(options[i].Alias, Name) == 0)
- return &options[i];
- }
- }
- return NULL;
- }
- COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(COMMAND_LINE_ARGUMENT_A* argument)
- {
- COMMAND_LINE_ARGUMENT_A* nextArgument;
- if (!argument || !argument->Name)
- return NULL;
- nextArgument = &argument[1];
- if (nextArgument->Name == NULL)
- return NULL;
- return nextArgument;
- }
- char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list, size_t* count)
- {
- char** p;
- char* str;
- size_t nArgs;
- size_t index;
- size_t nCommas;
- size_t prefix, len;
- nCommas = 0;
- if (count == NULL)
- return NULL;
- *count = 0;
- if (!list)
- {
- if (name)
- {
- size_t len = strlen(name);
- p = (char**)calloc(2UL + len, sizeof(char*));
- if (p)
- {
- char* dst = (char*)&p[1];
- p[0] = dst;
- sprintf_s(dst, len + 1, "%s", name);
- *count = 1;
- return p;
- }
- }
- return NULL;
- }
- {
- const char* it = list;
- while ((it = strchr(it, ',')) != NULL)
- {
- it++;
- nCommas++;
- }
- }
- nArgs = nCommas + 1;
- if (name)
- nArgs++;
- prefix = (nArgs + 1UL) * sizeof(char*);
- len = strlen(list);
- p = (char**)calloc(len + prefix + 1, sizeof(char*));
- if (!p)
- return NULL;
- str = &((char*)p)[prefix];
- memcpy(str, list, len);
- if (name)
- p[0] = (char*)name;
- for (index = name ? 1 : 0; index < nArgs; index++)
- {
- char* comma = strchr(str, ',');
- p[index] = str;
- if (comma)
- {
- str = comma + 1;
- *comma = '\0';
- }
- }
- *count = nArgs;
- return p;
- }
- char** CommandLineParseCommaSeparatedValues(const char* list, size_t* count)
- {
- return CommandLineParseCommaSeparatedValuesEx(NULL, list, count);
- }
|