| File: | .build-ci/../tests/unit/c/argconfig-getopt-ref.c |
| Warning: | line 314, column 3 Potential leak of memory pointed to by 'long_opts' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* | |||
| 3 | * A frozen copy of the getopt based argconfig parser, as it was before | |||
| 4 | * argconfig got its own parser. test-argconfig-diff compares both, so any | |||
| 5 | * change in behaviour shows up. Help output and the locale side effect | |||
| 6 | * are left out. The test compares the parsing of the command line, so the | |||
| 7 | * value conversion is the one of argconfig, which uses the strict number | |||
| 8 | * parsers of shared/parse-util. | |||
| 9 | */ | |||
| 10 | ||||
| 11 | #include <ctype.h> | |||
| 12 | #include <errno(*__errno_location ()).h> | |||
| 13 | #include <limits.h> | |||
| 14 | #include <getopt.h> | |||
| 15 | #include <stdbool.h> | |||
| 16 | #include <stdio.h> | |||
| 17 | #include <stdlib.h> | |||
| 18 | #include <string.h> | |||
| 19 | ||||
| 20 | #include <libnvme.h> | |||
| 21 | ||||
| 22 | #include <shared/parse-util.h> | |||
| 23 | #include <shared/suffix-util.h> | |||
| 24 | ||||
| 25 | #include "../../../src/argconfig.h" | |||
| 26 | #include "../../../src/cleanup.h" | |||
| 27 | #include "argconfig-getopt-ref.h" | |||
| 28 | ||||
| 29 | /* getopt's has_arg for an option */ | |||
| 30 | static int has_arg(struct argconfig_commandline_options *s) | |||
| 31 | { | |||
| 32 | switch (s->argument_type) { | |||
| 33 | case ARGCONFIG_ARG_REQUIRED: | |||
| 34 | return required_argument1; | |||
| 35 | case ARGCONFIG_ARG_OPTIONAL: | |||
| 36 | return optional_argument2; | |||
| 37 | default: | |||
| 38 | return no_argument0; | |||
| 39 | } | |||
| 40 | } | |||
| 41 | ||||
| 42 | static bool_Bool is_null_or_empty(const char *s) | |||
| 43 | { | |||
| 44 | return !s || !*s; | |||
| 45 | } | |||
| 46 | ||||
| 47 | /* The value conversion of argconfig, see argconfig_parse_type() */ | |||
| 48 | static int argconfig_parse_type(struct argconfig_commandline_options *s, | |||
| 49 | char *arg) | |||
| 50 | { | |||
| 51 | void *value = s->default_value; | |||
| 52 | char *endptr; | |||
| 53 | uint64_t u; | |||
| 54 | int64_t i; | |||
| 55 | double d; | |||
| 56 | int ret = 0; | |||
| 57 | ||||
| 58 | if (!arg && s->config_type != CFG_STRING && | |||
| 59 | s->config_type != CFG_FLAG && s->config_type != CFG_INCREMENT) | |||
| 60 | return -EINVAL22; | |||
| 61 | ||||
| 62 | switch (s->config_type) { | |||
| 63 | case CFG_STRING: | |||
| 64 | case CFG_FILE: | |||
| 65 | case CFG_DIR: | |||
| 66 | *(char **)value = arg; | |||
| 67 | break; | |||
| 68 | case CFG_INT: | |||
| 69 | ret = shr_parse_s64(arg, INT_MIN(-2147483647 -1), INT_MAX2147483647, &i); | |||
| 70 | if (!ret) | |||
| 71 | *(int *)value = i; | |||
| 72 | break; | |||
| 73 | case CFG_BYTE: | |||
| 74 | ret = shr_parse_u64(arg, UINT8_MAX(255), &u); | |||
| 75 | if (!ret) | |||
| 76 | *(uint8_t *)value = u; | |||
| 77 | break; | |||
| 78 | case CFG_SHORT: | |||
| 79 | ret = shr_parse_u64(arg, UINT16_MAX(65535), &u); | |||
| 80 | if (!ret) | |||
| 81 | *(uint16_t *)value = u; | |||
| 82 | break; | |||
| 83 | case CFG_POSITIVE: | |||
| 84 | ret = shr_parse_u64(arg, UINT32_MAX(4294967295U), &u); | |||
| 85 | if (!ret) | |||
| 86 | *(uint32_t *)value = u; | |||
| 87 | break; | |||
| 88 | case CFG_INCREMENT: | |||
| 89 | *(int *)value += 1; | |||
| 90 | break; | |||
| 91 | case CFG_ENUM: | |||
| 92 | case CFG_ENUM_LIST: | |||
| 93 | ret = -EINVAL22; | |||
| 94 | break; | |||
| 95 | case CFG_LONG: | |||
| 96 | ret = shr_parse_u64(arg, UINT64_MAX(18446744073709551615UL), &u); | |||
| 97 | if (!ret) | |||
| 98 | *(uint64_t *)value = u; | |||
| 99 | break; | |||
| 100 | case CFG_LONG_SUFFIX: | |||
| 101 | if (!isdigit((unsigned char)*arg)((*__ctype_b_loc ())[(int) (((unsigned char)*arg))] & (unsigned short int) _ISdigit)) | |||
| 102 | return -EINVAL22; | |||
| 103 | ret = shr_suffix_binary_parse(arg, &endptr, &u); | |||
| 104 | if (!ret) | |||
| 105 | *(uint64_t *)value = u; | |||
| 106 | break; | |||
| 107 | case CFG_DOUBLE: | |||
| 108 | ret = shr_parse_double(arg, &d); | |||
| 109 | if (!ret) | |||
| 110 | *(double *)value = d; | |||
| 111 | break; | |||
| 112 | case CFG_FLAG: | |||
| 113 | *(bool_Bool *)value = true1; | |||
| 114 | break; | |||
| 115 | case CFG_GROUP_SEPARATOR: | |||
| 116 | case CFG_INCLUDE: | |||
| 117 | break; | |||
| 118 | } | |||
| 119 | ||||
| 120 | return ret ? -EINVAL22 : 0; | |||
| 121 | } | |||
| 122 | ||||
| 123 | static void argconfig_set_opt_val(enum argconfig_types type, | |||
| 124 | union argconfig_val *opt_val, void *val) | |||
| 125 | { | |||
| 126 | switch (type) { | |||
| 127 | case CFG_FLAG: | |||
| 128 | *(bool_Bool *)val = opt_val->bool_val; | |||
| 129 | break; | |||
| 130 | case CFG_LONG_SUFFIX: | |||
| 131 | *(uint64_t *)val = opt_val->long_suffix; | |||
| 132 | break; | |||
| 133 | case CFG_POSITIVE: | |||
| 134 | *(uint32_t *)val = opt_val->positive; | |||
| 135 | break; | |||
| 136 | case CFG_INT: | |||
| 137 | case CFG_ENUM: | |||
| 138 | *(int *)val = opt_val->int_val; | |||
| 139 | break; | |||
| 140 | case CFG_LONG: | |||
| 141 | *(uint64_t *)val = opt_val->long_val; | |||
| 142 | break; | |||
| 143 | case CFG_DOUBLE: | |||
| 144 | *(double *)val = opt_val->double_val; | |||
| 145 | break; | |||
| 146 | case CFG_BYTE: | |||
| 147 | *(uint8_t *)val = opt_val->byte; | |||
| 148 | break; | |||
| 149 | case CFG_SHORT: | |||
| 150 | *(uint16_t *)val = opt_val->short_val; | |||
| 151 | break; | |||
| 152 | case CFG_INCREMENT: | |||
| 153 | *(int *)val = opt_val->increment; | |||
| 154 | break; | |||
| 155 | case CFG_STRING: | |||
| 156 | case CFG_FILE: | |||
| 157 | case CFG_DIR: | |||
| 158 | *(char **)val = opt_val->string; | |||
| 159 | break; | |||
| 160 | case CFG_ENUM_LIST: | |||
| 161 | case CFG_GROUP_SEPARATOR: | |||
| 162 | case CFG_INCLUDE: | |||
| 163 | break; | |||
| 164 | } | |||
| 165 | } | |||
| 166 | ||||
| 167 | static struct argconfig_opt_val * | |||
| 168 | argconfig_match_val(struct argconfig_opt_val *v, const char *str) | |||
| 169 | { | |||
| 170 | size_t len = strlen(str); | |||
| 171 | struct argconfig_opt_val *match = NULL((void*)0); | |||
| 172 | ||||
| 173 | for (; v->str; v++) { | |||
| 174 | if (strncasecmp(str, v->str, len)) | |||
| 175 | continue; | |||
| 176 | ||||
| 177 | if (len == strlen(v->str)) | |||
| 178 | return v; | |||
| 179 | ||||
| 180 | if (match) | |||
| 181 | return NULL((void*)0); /* multiple matches; input is ambiguous */ | |||
| 182 | ||||
| 183 | match = v; | |||
| 184 | } | |||
| 185 | ||||
| 186 | return match; | |||
| 187 | } | |||
| 188 | ||||
| 189 | static int argconfig_parse_val(struct argconfig_commandline_options *s, | |||
| 190 | char *arg) | |||
| 191 | { | |||
| 192 | struct argconfig_opt_val *v = s->opt_val; | |||
| 193 | ||||
| 194 | if (v) | |||
| 195 | v = argconfig_match_val(v, arg); | |||
| 196 | if (!v) | |||
| 197 | return argconfig_parse_type(s, arg); | |||
| 198 | ||||
| 199 | argconfig_set_opt_val(v->type, &v->val, s->default_value); | |||
| 200 | return 0; | |||
| 201 | } | |||
| 202 | ||||
| 203 | int ref_argconfig_parse(int argc, char *argv[], | |||
| 204 | struct argconfig_commandline_options *options, | |||
| 205 | char ***operands, int *nr_operands) | |||
| 206 | { | |||
| 207 | __cleanup_free__attribute__((cleanup(shr_freep))) char *short_opts = NULL((void*)0); | |||
| 208 | __cleanup_free__attribute__((cleanup(shr_freep))) struct option *long_opts = NULL((void*)0); | |||
| 209 | __cleanup_free__attribute__((cleanup(shr_freep))) int *long_opt_map = NULL((void*)0); | |||
| 210 | struct argconfig_commandline_options *s; | |||
| 211 | int c, long_opt_index = 0, opt_index = 0, short_index = 0; | |||
| 212 | int options_count = 0; | |||
| 213 | int ret = 0; | |||
| 214 | ||||
| 215 | errno(*__errno_location ()) = 0; | |||
| 216 | for (s = options; s->option; s++) | |||
| 217 | options_count++; | |||
| 218 | ||||
| 219 | long_opts = calloc(options_count + 2, sizeof(struct option)); | |||
| 220 | short_opts = calloc(options_count * 3 + 3, sizeof(*short_opts)); | |||
| 221 | long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map)); | |||
| 222 | ||||
| 223 | if (!long_opts || !short_opts || !long_opt_map) { | |||
| 224 | fprintf(stderrstderr, "failed to allocate memory for opts: %s\n", | |||
| 225 | libnvme_strerror(errno(*__errno_location ()))); | |||
| 226 | return -errno(*__errno_location ()); | |||
| 227 | } | |||
| 228 | ||||
| 229 | for (s = options, opt_index = 0; s->option; s++, opt_index++) { | |||
| 230 | s->seen = false0; | |||
| 231 | if (s->config_type == CFG_GROUP_SEPARATOR) | |||
| 232 | continue; | |||
| 233 | if (s->short_option) { | |||
| 234 | short_opts[short_index++] = s->short_option; | |||
| 235 | if (s->argument_type == ARGCONFIG_ARG_REQUIRED || | |||
| 236 | s->argument_type == ARGCONFIG_ARG_OPTIONAL) | |||
| 237 | short_opts[short_index++] = ':'; | |||
| 238 | if (s->argument_type == ARGCONFIG_ARG_OPTIONAL) | |||
| 239 | short_opts[short_index++] = ':'; | |||
| 240 | } | |||
| 241 | if (!is_null_or_empty(s->option)) { | |||
| 242 | long_opts[long_opt_index].name = s->option; | |||
| 243 | long_opts[long_opt_index].has_arg = has_arg(s); | |||
| 244 | long_opt_map[long_opt_index] = opt_index; | |||
| 245 | long_opt_index++; | |||
| 246 | } | |||
| 247 | } | |||
| 248 | ||||
| 249 | long_opts[long_opt_index].name = "help"; | |||
| 250 | long_opts[long_opt_index].val = 'h'; | |||
| 251 | ||||
| 252 | short_opts[short_index++] = '?'; | |||
| 253 | short_opts[short_index] = 'h'; | |||
| 254 | ||||
| 255 | optind = 0; | |||
| 256 | while ((c = getopt_long_only(argc, argv, short_opts, long_opts, | |||
| 257 | &long_opt_index)) != -1) { | |||
| 258 | if (c) { | |||
| 259 | if (c == '?' || c == 'h') { | |||
| 260 | ret = -EINVAL22; | |||
| 261 | break; | |||
| 262 | } | |||
| 263 | for (opt_index = 0; opt_index < options_count; | |||
| 264 | opt_index++) { | |||
| 265 | if (c == options[opt_index].short_option) | |||
| 266 | break; | |||
| 267 | } | |||
| 268 | if (opt_index == options_count) | |||
| 269 | continue; | |||
| 270 | } else { | |||
| 271 | opt_index = long_opt_map[long_opt_index]; | |||
| 272 | } | |||
| 273 | ||||
| 274 | s = &options[opt_index]; | |||
| 275 | s->seen = true1; | |||
| 276 | ||||
| 277 | if (!s->default_value) | |||
| 278 | continue; | |||
| 279 | ||||
| 280 | ret = argconfig_parse_val(s, optarg); | |||
| 281 | if (ret) | |||
| 282 | break; | |||
| 283 | } | |||
| 284 | ||||
| 285 | *operands = &argv[optind]; | |||
| 286 | *nr_operands = argc - optind; | |||
| 287 | ||||
| 288 | return ret; | |||
| 289 | } | |||
| 290 | ||||
| 291 | /* Global options: stop at the first non-option argument. */ | |||
| 292 | int ref_argconfig_parse_global(int argc, char *argv[], | |||
| 293 | struct argconfig_commandline_options *options, | |||
| 294 | int *argind) | |||
| 295 | { | |||
| 296 | __cleanup_free__attribute__((cleanup(shr_freep))) char *short_opts = NULL((void*)0); | |||
| 297 | __cleanup_free__attribute__((cleanup(shr_freep))) struct option *long_opts = NULL((void*)0); | |||
| 298 | __cleanup_free__attribute__((cleanup(shr_freep))) int *long_opt_map = NULL((void*)0); | |||
| 299 | struct argconfig_commandline_options *s; | |||
| 300 | int c, long_opt_index = 0, opt_index = 0; | |||
| 301 | int short_index = 0, options_count = 0; | |||
| 302 | int ret = 0; | |||
| 303 | ||||
| 304 | errno(*__errno_location ()) = 0; | |||
| 305 | for (s = options; s->option; s++) | |||
| ||||
| 306 | options_count++; | |||
| 307 | ||||
| 308 | long_opts = calloc(options_count + 2, sizeof(struct option)); | |||
| 309 | /* '+' prefix: stop at the first non-option argument (the subcommand) */ | |||
| 310 | short_opts = calloc(options_count * 3 + 4, sizeof(*short_opts)); | |||
| 311 | long_opt_map = calloc(options_count + 2, sizeof(*long_opt_map)); | |||
| 312 | ||||
| 313 | if (!long_opts || !short_opts || !long_opt_map) { | |||
| 314 | fprintf(stderrstderr, "failed to allocate memory for opts: %s\n", | |||
| ||||
| 315 | libnvme_strerror(errno(*__errno_location ()))); | |||
| 316 | return -errno(*__errno_location ()); | |||
| 317 | } | |||
| 318 | ||||
| 319 | short_opts[short_index++] = '+'; | |||
| 320 | ||||
| 321 | for (s = options, opt_index = 0; s->option; s++, opt_index++) { | |||
| 322 | s->seen = false0; | |||
| 323 | if (s->config_type == CFG_GROUP_SEPARATOR) | |||
| 324 | continue; | |||
| 325 | if (s->short_option) { | |||
| 326 | short_opts[short_index++] = s->short_option; | |||
| 327 | if (s->argument_type == ARGCONFIG_ARG_REQUIRED || | |||
| 328 | s->argument_type == ARGCONFIG_ARG_OPTIONAL) | |||
| 329 | short_opts[short_index++] = ':'; | |||
| 330 | if (s->argument_type == ARGCONFIG_ARG_OPTIONAL) | |||
| 331 | short_opts[short_index++] = ':'; | |||
| 332 | } | |||
| 333 | if (!is_null_or_empty(s->option)) { | |||
| 334 | long_opts[long_opt_index].name = s->option; | |||
| 335 | long_opts[long_opt_index].has_arg = has_arg(s); | |||
| 336 | long_opt_map[long_opt_index] = opt_index; | |||
| 337 | long_opt_index++; | |||
| 338 | } | |||
| 339 | } | |||
| 340 | ||||
| 341 | optind = 0; | |||
| 342 | while ((c = getopt_long(argc, argv, short_opts, | |||
| 343 | long_opts, &long_opt_index)) != -1) { | |||
| 344 | if (c == '?' || c == ':') { | |||
| 345 | ret = -EINVAL22; | |||
| 346 | break; | |||
| 347 | } | |||
| 348 | if (c) { | |||
| 349 | for (opt_index = 0; opt_index < options_count; | |||
| 350 | opt_index++) { | |||
| 351 | if (c == options[opt_index].short_option) | |||
| 352 | break; | |||
| 353 | } | |||
| 354 | if (opt_index == options_count) { | |||
| 355 | ret = -EINVAL22; | |||
| 356 | break; | |||
| 357 | } | |||
| 358 | } else { | |||
| 359 | opt_index = long_opt_map[long_opt_index]; | |||
| 360 | } | |||
| 361 | ||||
| 362 | s = &options[opt_index]; | |||
| 363 | s->seen = true1; | |||
| 364 | ||||
| 365 | if (!s->default_value) | |||
| 366 | continue; | |||
| 367 | ||||
| 368 | ret = argconfig_parse_val(s, optarg); | |||
| 369 | if (ret) | |||
| 370 | break; | |||
| 371 | } | |||
| 372 | ||||
| 373 | *argind = optind; | |||
| 374 | ||||
| 375 | return ret; | |||
| 376 | } |