diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /js/src/zydis/Zycore/ArgParse.c | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | js/src/zydis/Zycore/ArgParse.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/js/src/zydis/Zycore/ArgParse.c b/js/src/zydis/Zycore/ArgParse.c new file mode 100644 index 0000000000..fe38545aed --- /dev/null +++ b/js/src/zydis/Zycore/ArgParse.c @@ -0,0 +1,279 @@ +/*************************************************************************************************** + + Zyan Core Library (Zycore-C) + + Original Author : Joel Hoener + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + +***************************************************************************************************/ + +#include "zydis/Zycore/ArgParse.h" +#include "zydis/Zycore/LibC.h" + +/* ============================================================================================== */ +/* Exported functions */ +/* ============================================================================================== */ + +#ifndef ZYAN_NO_LIBC + +ZyanStatus ZyanArgParse(const ZyanArgParseConfig *cfg, ZyanVector* parsed, + const char** error_token) +{ + return ZyanArgParseEx(cfg, parsed, error_token, ZyanAllocatorDefault()); +} + +#endif + +ZyanStatus ZyanArgParseEx(const ZyanArgParseConfig *cfg, ZyanVector* parsed, + const char** error_token, ZyanAllocator* allocator) +{ +# define ZYAN_ERR_TOK(tok) if (error_token) { *error_token = tok; } + + ZYAN_ASSERT(cfg); + ZYAN_ASSERT(parsed); + + // TODO: Once we have a decent hash map impl, refactor this to use it. The majority of for + // loops through the argument list could be avoided. + + if (cfg->min_unnamed_args > cfg->max_unnamed_args) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Check argument syntax. + for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) + { + // TODO: Duplicate check + + if (!def->name) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + ZyanUSize arg_len = ZYAN_STRLEN(def->name); + if (arg_len < 2 || def->name[0] != '-') + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + + // Single dash arguments only accept a single char name. + if (def->name[1] != '-' && arg_len != 2) + { + return ZYAN_STATUS_INVALID_ARGUMENT; + } + } + + // Initialize output vector. + ZYAN_CHECK(ZyanVectorInitEx(parsed, sizeof(ZyanArgParseArg), cfg->argc, ZYAN_NULL, allocator, + ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD)); + + ZyanStatus err; + ZyanBool accept_dash_args = ZYAN_TRUE; + ZyanUSize num_unnamed_args = 0; + for (ZyanUSize i = 1; i < cfg->argc; ++i) + { + const char* cur_arg = cfg->argv[i]; + ZyanUSize arg_len = ZYAN_STRLEN(cfg->argv[i]); + + // Double-dash argument? + if (accept_dash_args && arg_len >= 2 && ZYAN_MEMCMP(cur_arg, "--", 2) == 0) + { + // GNU style end of argument parsing. + if (arg_len == 2) + { + accept_dash_args = ZYAN_FALSE; + } + // Regular double-dash argument. + else + { + // Allocate parsed argument struct. + ZyanArgParseArg* parsed_arg; + ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL)); + ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg)); + + // Find corresponding argument definition. + for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) + { + if (ZYAN_STRCMP(def->name, cur_arg) == 0) + { + parsed_arg->def = def; + break; + } + } + + // Search exhausted & argument not found. RIP. + if (!parsed_arg->def) + { + err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD; + ZYAN_ERR_TOK(cur_arg); + goto failure; + } + + // Does the argument expect a value? If yes, consume next token. + if (!parsed_arg->def->boolean) + { + if (i == cfg->argc - 1) + { + err = ZYAN_STATUS_ARG_MISSES_VALUE; + ZYAN_ERR_TOK(cur_arg); + goto failure; + } + parsed_arg->has_value = ZYAN_TRUE; + ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i])); + } + } + + // Continue parsing at next token. + continue; + } + + // Single-dash argument? + // TODO: How to deal with just dashes? Current code treats it as unnamed arg. + if (accept_dash_args && arg_len > 1 && cur_arg[0] == '-') + { + // Iterate argument token chars until there are either no more chars left + // or we encounter a non-boolean argument, in which case we consume the + // remaining chars as its value. + for (const char* read_ptr = cur_arg + 1; *read_ptr; ++read_ptr) + { + // Allocate parsed argument struct. + ZyanArgParseArg* parsed_arg; + ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL)); + ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg)); + + // Find corresponding argument definition. + for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) + { + if (ZYAN_STRLEN(def->name) == 2 && + def->name[0] == '-' && + def->name[1] == *read_ptr) + { + parsed_arg->def = def; + break; + } + } + + // Search exhausted, no match found? + if (!parsed_arg->def) + { + err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD; + ZYAN_ERR_TOK(cur_arg); + goto failure; + } + + // Requires value? + if (!parsed_arg->def->boolean) + { + // If there are chars left, consume them (e.g. `-n1000`). + if (read_ptr[1]) + { + parsed_arg->has_value = ZYAN_TRUE; + ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, read_ptr + 1)); + } + // If not, consume next token (e.g. `-n 1000`). + else + { + if (i == cfg->argc - 1) + { + err = ZYAN_STATUS_ARG_MISSES_VALUE; + ZYAN_ERR_TOK(cur_arg) + goto failure; + } + + parsed_arg->has_value = ZYAN_TRUE; + ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i])); + } + + // Either way, continue with next argument. + goto continue_main_loop; + } + } + } + + // Still here? We're looking at an unnamed argument. + ++num_unnamed_args; + if (num_unnamed_args > cfg->max_unnamed_args) + { + err = ZYAN_STATUS_TOO_MANY_ARGS; + ZYAN_ERR_TOK(cur_arg); + goto failure; + } + + // Allocate parsed argument struct. + ZyanArgParseArg* parsed_arg; + ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL)); + ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg)); + parsed_arg->has_value = ZYAN_TRUE; + ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cur_arg)); + + continue_main_loop:; + } + + // All tokens processed. Do we have enough unnamed arguments? + if (num_unnamed_args < cfg->min_unnamed_args) + { + err = ZYAN_STATUS_TOO_FEW_ARGS; + // No sensible error token for this error type. + goto failure; + } + + // Check whether all required arguments are present. + ZyanUSize num_parsed_args; + ZYAN_CHECK(ZyanVectorGetSize(parsed, &num_parsed_args)); + for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def) + { + if (!def->required) continue; + + ZyanBool arg_found = ZYAN_FALSE; + for (ZyanUSize i = 0; i < num_parsed_args; ++i) + { + const ZyanArgParseArg* arg = ZYAN_NULL; + ZYAN_CHECK(ZyanVectorGetPointer(parsed, i, (const void**)&arg)); + + // Skip unnamed args. + if (!arg->def) continue; + + if (arg->def == def) + { + arg_found = ZYAN_TRUE; + break; + } + } + + if (!arg_found) + { + err = ZYAN_STATUS_REQUIRED_ARG_MISSING; + ZYAN_ERR_TOK(def->name); + goto failure; + } + } + + // Yay! + ZYAN_ERR_TOK(ZYAN_NULL); + return ZYAN_STATUS_SUCCESS; + +failure: + ZYAN_CHECK(ZyanVectorDestroy(parsed)); + return err; + +# undef ZYAN_ERR_TOK +} + +/* ============================================================================================== */ |