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 : }