tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ArgParse.c (10064B)


      1 /***************************************************************************************************
      2 
      3  Zyan Core Library (Zycore-C)
      4 
      5  Original Author : Joel Hoener
      6 
      7 * Permission is hereby granted, free of charge, to any person obtaining a copy
      8 * of this software and associated documentation files (the "Software"), to deal
      9 * in the Software without restriction, including without limitation the rights
     10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     11 * copies of the Software, and to permit persons to whom the Software is
     12 * furnished to do so, subject to the following conditions:
     13 *
     14 * The above copyright notice and this permission notice shall be included in all
     15 * copies or substantial portions of the Software.
     16 *
     17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     23 * SOFTWARE.
     24 
     25 ***************************************************************************************************/
     26 
     27 #include "zydis/Zycore/ArgParse.h"
     28 #include "zydis/Zycore/LibC.h"
     29 
     30 /* ============================================================================================== */
     31 /* Exported functions                                                                             */
     32 /* ============================================================================================== */
     33 
     34 #ifndef ZYAN_NO_LIBC
     35 
     36 ZyanStatus ZyanArgParse(const ZyanArgParseConfig *cfg, ZyanVector* parsed,
     37    const char** error_token)
     38 {
     39    return ZyanArgParseEx(cfg, parsed, error_token, ZyanAllocatorDefault());
     40 }
     41 
     42 #endif
     43 
     44 ZyanStatus ZyanArgParseEx(const ZyanArgParseConfig *cfg, ZyanVector* parsed,
     45    const char** error_token, ZyanAllocator* allocator)
     46 {
     47 #   define ZYAN_ERR_TOK(tok) if (error_token) { *error_token = tok; }
     48 
     49    ZYAN_ASSERT(cfg);
     50    ZYAN_ASSERT(parsed);
     51 
     52    // TODO: Once we have a decent hash map impl, refactor this to use it. The majority of for
     53    //       loops through the argument list could be avoided.
     54 
     55    if (cfg->min_unnamed_args > cfg->max_unnamed_args)
     56    {
     57        return ZYAN_STATUS_INVALID_ARGUMENT;
     58    }
     59 
     60    // Check argument syntax.
     61    for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
     62    {
     63        // TODO: Duplicate check
     64 
     65        if (!def->name)
     66        {
     67            return ZYAN_STATUS_INVALID_ARGUMENT;
     68        }
     69 
     70        ZyanUSize arg_len = ZYAN_STRLEN(def->name);
     71        if (arg_len < 2 || def->name[0] != '-')
     72        {
     73            return ZYAN_STATUS_INVALID_ARGUMENT;
     74        }
     75 
     76        // Single dash arguments only accept a single char name.
     77        if (def->name[1] != '-' && arg_len != 2)
     78        {
     79            return ZYAN_STATUS_INVALID_ARGUMENT;
     80        }
     81    }
     82 
     83    // Initialize output vector.
     84    ZYAN_CHECK(ZyanVectorInitEx(parsed, sizeof(ZyanArgParseArg), cfg->argc, ZYAN_NULL, allocator,
     85        ZYAN_VECTOR_DEFAULT_GROWTH_FACTOR, ZYAN_VECTOR_DEFAULT_SHRINK_THRESHOLD));
     86 
     87    ZyanStatus err;
     88    ZyanBool accept_dash_args = ZYAN_TRUE;
     89    ZyanUSize num_unnamed_args = 0;
     90    for (ZyanUSize i = 1; i < cfg->argc; ++i)
     91    {
     92        const char* cur_arg = cfg->argv[i];
     93        ZyanUSize arg_len = ZYAN_STRLEN(cfg->argv[i]);
     94 
     95        // Double-dash argument?
     96        if (accept_dash_args && arg_len >= 2 && ZYAN_MEMCMP(cur_arg, "--", 2) == 0)
     97        {
     98            // GNU style end of argument parsing.
     99            if (arg_len == 2)
    100            {
    101                accept_dash_args = ZYAN_FALSE;
    102            }
    103            // Regular double-dash argument.
    104            else
    105            {
    106                // Allocate parsed argument struct.
    107                ZyanArgParseArg* parsed_arg;
    108                ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL));
    109                ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg));
    110 
    111                // Find corresponding argument definition.
    112                for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
    113                {
    114                    if (ZYAN_STRCMP(def->name, cur_arg) == 0)
    115                    {
    116                        parsed_arg->def = def;
    117                        break;
    118                    }
    119                }
    120 
    121                // Search exhausted & argument not found. RIP.
    122                if (!parsed_arg->def)
    123                {
    124                    err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD;
    125                    ZYAN_ERR_TOK(cur_arg);
    126                    goto failure;
    127                }
    128 
    129                // Does the argument expect a value? If yes, consume next token.
    130                if (!parsed_arg->def->boolean)
    131                {
    132                    if (i == cfg->argc - 1)
    133                    {
    134                        err = ZYAN_STATUS_ARG_MISSES_VALUE;
    135                        ZYAN_ERR_TOK(cur_arg);
    136                        goto failure;
    137                    }
    138                    parsed_arg->has_value = ZYAN_TRUE;
    139                    ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i]));
    140                }
    141            }
    142 
    143            // Continue parsing at next token.
    144            continue;
    145        }
    146 
    147        // Single-dash argument?
    148        // TODO: How to deal with just dashes? Current code treats it as unnamed arg.
    149        if (accept_dash_args && arg_len > 1 && cur_arg[0] == '-')
    150        {
    151            // Iterate argument token chars until there are either no more chars left
    152            // or we encounter a non-boolean argument, in which case we consume the
    153            // remaining chars as its value.
    154            for (const char* read_ptr = cur_arg + 1; *read_ptr; ++read_ptr)
    155            {
    156                // Allocate parsed argument struct.
    157                ZyanArgParseArg* parsed_arg;
    158                ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL));
    159                ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg));
    160 
    161                // Find corresponding argument definition.
    162                for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
    163                {
    164                    if (ZYAN_STRLEN(def->name) == 2 &&
    165                        def->name[0] == '-' &&
    166                        def->name[1] == *read_ptr)
    167                    {
    168                        parsed_arg->def = def;
    169                        break;
    170                    }
    171                }
    172 
    173                // Search exhausted, no match found?
    174                if (!parsed_arg->def)
    175                {
    176                    err = ZYAN_STATUS_ARG_NOT_UNDERSTOOD;
    177                    ZYAN_ERR_TOK(cur_arg);
    178                    goto failure;
    179                }
    180 
    181                // Requires value?
    182                if (!parsed_arg->def->boolean)
    183                {
    184                    // If there are chars left, consume them (e.g. `-n1000`).
    185                    if (read_ptr[1])
    186                    {
    187                        parsed_arg->has_value = ZYAN_TRUE;
    188                        ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, read_ptr + 1));
    189                    }
    190                    // If not, consume next token (e.g. `-n 1000`).
    191                    else
    192                    {
    193                        if (i == cfg->argc - 1)
    194                        {
    195                            err = ZYAN_STATUS_ARG_MISSES_VALUE;
    196                            ZYAN_ERR_TOK(cur_arg)
    197                            goto failure;
    198                        }
    199 
    200                        parsed_arg->has_value = ZYAN_TRUE;
    201                        ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cfg->argv[++i]));
    202                    }
    203 
    204                    // Either way, continue with next argument.
    205                    goto continue_main_loop;
    206                }
    207            }
    208        }
    209 
    210        // Still here? We're looking at an unnamed argument.
    211        ++num_unnamed_args;
    212        if (num_unnamed_args > cfg->max_unnamed_args)
    213        {
    214            err = ZYAN_STATUS_TOO_MANY_ARGS;
    215            ZYAN_ERR_TOK(cur_arg);
    216            goto failure;
    217        }
    218 
    219        // Allocate parsed argument struct.
    220        ZyanArgParseArg* parsed_arg;
    221        ZYAN_CHECK(ZyanVectorEmplace(parsed, (void**)&parsed_arg, ZYAN_NULL));
    222        ZYAN_MEMSET(parsed_arg, 0, sizeof(*parsed_arg));
    223        parsed_arg->has_value = ZYAN_TRUE;
    224        ZYAN_CHECK(ZyanStringViewInsideBuffer(&parsed_arg->value, cur_arg));
    225 
    226    continue_main_loop:;
    227    }
    228 
    229    // All tokens processed. Do we have enough unnamed arguments?
    230    if (num_unnamed_args < cfg->min_unnamed_args)
    231    {
    232        err = ZYAN_STATUS_TOO_FEW_ARGS;
    233        // No sensible error token for this error type.
    234        goto failure;
    235    }
    236 
    237    // Check whether all required arguments are present.
    238    ZyanUSize num_parsed_args;
    239    ZYAN_CHECK(ZyanVectorGetSize(parsed, &num_parsed_args));
    240    for (const ZyanArgParseDefinition* def = cfg->args; def && def->name; ++def)
    241    {
    242        if (!def->required) continue;
    243 
    244        ZyanBool arg_found = ZYAN_FALSE;
    245        for (ZyanUSize i = 0; i < num_parsed_args; ++i)
    246        {
    247            const ZyanArgParseArg* arg = ZYAN_NULL;
    248            ZYAN_CHECK(ZyanVectorGetPointer(parsed, i, (const void**)&arg));
    249 
    250            // Skip unnamed args.
    251            if (!arg->def) continue;
    252 
    253            if (arg->def == def)
    254            {
    255                arg_found = ZYAN_TRUE;
    256                break;
    257            }
    258        }
    259 
    260        if (!arg_found)
    261        {
    262            err = ZYAN_STATUS_REQUIRED_ARG_MISSING;
    263            ZYAN_ERR_TOK(def->name);
    264            goto failure;
    265        }
    266    }
    267 
    268    // Yay!
    269    ZYAN_ERR_TOK(ZYAN_NULL);
    270    return ZYAN_STATUS_SUCCESS;
    271 
    272 failure:
    273    ZYAN_CHECK(ZyanVectorDestroy(parsed));
    274    return err;
    275 
    276 #   undef ZYAN_ERR_TOK
    277 }
    278 
    279 /* ============================================================================================== */