LCOV - code coverage report
Current view: top level - src/command - args.c Hit Total Coverage
Test: dte Lines: 77 77 100.0 %
Date: 2023-03-25 22:04:15

          Line data    Source code
       1             : #include <stdlib.h>
       2             : #include <string.h>
       3             : #include "args.h"
       4             : #include "error.h"
       5             : #include "util/str-util.h"
       6             : 
       7             : /*
       8             :  * Flags and first "--" are removed.
       9             :  * Flag arguments are moved to beginning.
      10             :  * Other arguments come right after flag arguments.
      11             :  *
      12             :  * a->args field should be set before calling.
      13             :  * If parsing succeeds, the other fields are set and 0 is returned.
      14             :  */
      15        5039 : ArgParseError do_parse_args(const Command *cmd, CommandArgs *a)
      16             : {
      17        5039 :     char **args = a->args;
      18        5039 :     BUG_ON(!args);
      19             : 
      20        5039 :     size_t argc = string_array_length(args);
      21        5039 :     size_t nr_flags = 0;
      22        5039 :     size_t nr_flag_args = 0;
      23             : 
      24        5039 :     const char *flag_desc = cmd->flags;
      25        5039 :     bool flags_after_arg = true;
      26        5039 :     if (flag_desc[0] == '-') {
      27         663 :         flag_desc++;
      28         663 :         flags_after_arg = false;
      29             :     }
      30             : 
      31       15888 :     for (size_t i = 0; args[i]; ) {
      32       11519 :         char *arg = args[i];
      33       11519 :         if (unlikely(streq(arg, "--"))) {
      34             :             // Move the NULL too
      35          25 :             memmove(args + i, args + i + 1, (argc - i) * sizeof(*args));
      36          25 :             free(arg);
      37          25 :             argc--;
      38          25 :             break;
      39             :         }
      40             : 
      41       11494 :         if (arg[0] != '-' || arg[1] == '\0') {
      42       10825 :             if (!flags_after_arg) {
      43             :                 break;
      44             :             }
      45       10188 :             i++;
      46       10188 :             continue;
      47             :         }
      48             : 
      49        1428 :         for (size_t j = 1; arg[j]; j++) {
      50         767 :             unsigned char flag = arg[j];
      51         767 :             const char *flagp = strchr(flag_desc, flag);
      52         767 :             if (unlikely(!flagp || flag == '=')) {
      53           3 :                 a->flags[0] = flag;
      54           3 :                 return ARGERR_INVALID_OPTION;
      55             :             }
      56             : 
      57         764 :             a->flag_set |= cmdargs_flagset_value(flag);
      58         764 :             a->flags[nr_flags++] = flag;
      59         764 :             if (unlikely(nr_flags == ARRAYLEN(a->flags))) {
      60             :                 return ARGERR_TOO_MANY_OPTIONS;
      61             :             }
      62             : 
      63         763 :             if (likely(flagp[1] != '=')) {
      64         742 :                 continue;
      65             :             }
      66             : 
      67          21 :             if (unlikely(j > 1 || arg[j + 1])) {
      68           2 :                 a->flags[0] = flag;
      69           2 :                 return ARGERR_OPTION_ARGUMENT_NOT_SEPARATE;
      70             :             }
      71             : 
      72          19 :             char *flag_arg = args[i + 1];
      73          19 :             if (unlikely(!flag_arg)) {
      74           2 :                 a->flags[0] = flag;
      75           2 :                 return ARGERR_OPTION_ARGUMENT_MISSING;
      76             :             }
      77             : 
      78             :             // Move flag argument before any other arguments
      79          17 :             if (i != nr_flag_args) {
      80             :                 // farg1 arg1  arg2 -f   farg2 arg3
      81             :                 // farg1 farg2 arg1 arg2 arg3
      82           1 :                 size_t count = i - nr_flag_args;
      83           1 :                 char **ptr = args + nr_flag_args;
      84           1 :                 memmove(ptr + 1, ptr, count * sizeof(*args));
      85             :             }
      86          17 :             args[nr_flag_args++] = flag_arg;
      87          17 :             i++;
      88             :         }
      89             : 
      90         661 :         memmove(args + i, args + i + 1, (argc - i) * sizeof(*args));
      91         661 :         free(arg);
      92         661 :         argc--;
      93             :     }
      94             : 
      95        5031 :     a->flags[nr_flags] = '\0';
      96        5031 :     a->nr_flags = nr_flags;
      97        5031 :     a->nr_args = argc - nr_flag_args;
      98        5031 :     a->nr_flag_args = nr_flag_args;
      99             : 
     100        5031 :     if (unlikely(a->nr_args < cmd->min_args)) {
     101             :         return ARGERR_TOO_FEW_ARGUMENTS;
     102             :     }
     103             : 
     104        4996 :     static_assert_compatible_types(cmd->max_args, uint8_t);
     105        4996 :     if (unlikely(a->nr_args > cmd->max_args && cmd->max_args != 0xFF)) {
     106           4 :         return ARGERR_TOO_MANY_ARGUMENTS;
     107             :     }
     108             : 
     109             :     return 0;
     110             : }
     111             : 
     112           7 : static bool arg_parse_error_msg(const Command *cmd, const CommandArgs *a, ArgParseError err)
     113             : {
     114           7 :     switch (err) {
     115           1 :     case ARGERR_INVALID_OPTION:
     116           1 :         return error_msg("Invalid option -%c", a->flags[0]);
     117           1 :     case ARGERR_TOO_MANY_OPTIONS:
     118           1 :         return error_msg("Too many options given");
     119           1 :     case ARGERR_OPTION_ARGUMENT_NOT_SEPARATE:
     120           1 :         return error_msg (
     121             :             "Flag -%c must be given separately because it"
     122             :             " requires an argument",
     123           1 :             a->flags[0]
     124             :         );
     125           1 :     case ARGERR_OPTION_ARGUMENT_MISSING:
     126           1 :         return error_msg("Option -%c requires an argument", a->flags[0]);
     127           2 :     case ARGERR_TOO_FEW_ARGUMENTS:
     128           2 :         return error_msg (
     129             :             "Too few arguments (got: %zu, minimum: %u)",
     130             :             a->nr_args,
     131           2 :             (unsigned int)cmd->min_args
     132             :         );
     133           1 :     case ARGERR_TOO_MANY_ARGUMENTS:
     134           1 :         return error_msg (
     135             :             "Too many arguments (got: %zu, maximum: %u)",
     136             :             a->nr_args,
     137           1 :             (unsigned int)cmd->max_args
     138             :         );
     139             :     case ARGERR_NONE:
     140             :         break;
     141             :     }
     142             : 
     143             :     BUG("unhandled error type");
     144             :     return false;
     145             : }
     146             : 
     147        4287 : bool parse_args(const Command *cmd, CommandArgs *a)
     148             : {
     149        4287 :     ArgParseError err = do_parse_args(cmd, a);
     150        4287 :     return likely(err == ARGERR_NONE) || arg_parse_error_msg(cmd, a, err);
     151             : }

Generated by: LCOV version 1.14