| File: | .build-ci/../src/argconfig.c |
| Warning: | line 343, column 12 Potential leak of memory pointed to by 'name' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | |||
| 2 | /* | |||
| 3 | * This file is part of nvme-cli. | |||
| 4 | * Copyright (c) 2014 PMC-Sierra, Inc. | |||
| 5 | * Copyright (c) 2026 SUSE Software Solutions | |||
| 6 | * | |||
| 7 | * Authors: Logan Gunthorpe <logang@deltatee.com> | |||
| 8 | * Daniel Wagner <dwagner@suse.de> | |||
| 9 | */ | |||
| 10 | ||||
| 11 | #include <ctype.h> | |||
| 12 | #include <errno(*__errno_location ()).h> | |||
| 13 | #include <inttypes.h> | |||
| 14 | #include <limits.h> | |||
| 15 | #include <locale.h> | |||
| 16 | #include <stdarg.h> | |||
| 17 | #include <stdbool.h> | |||
| 18 | #include <stdio.h> | |||
| 19 | #include <stdlib.h> | |||
| 20 | #include <string.h> | |||
| 21 | #include <unistd.h> | |||
| 22 | ||||
| 23 | #include <libnvme.h> | |||
| 24 | ||||
| 25 | #include <shared/parse-util.h> | |||
| 26 | #include <shared/string-util.h> | |||
| 27 | #include <shared/suffix-util.h> | |||
| 28 | #include <shared/wrap-util.h> | |||
| 29 | ||||
| 30 | #include "argconfig.h" | |||
| 31 | #include "cleanup.h" | |||
| 32 | ||||
| 33 | static bool_Bool is_null_or_empty(const char *s) | |||
| 34 | { | |||
| 35 | return !s || !*s; | |||
| 36 | } | |||
| 37 | ||||
| 38 | static const char *append_usage_str = ""; | |||
| 39 | ||||
| 40 | /* | |||
| 41 | * Options every command accepts, registered once instead of being added to | |||
| 42 | * each command's table. They are looked up before the command's own | |||
| 43 | * options, so a global option means the same for every command; a command | |||
| 44 | * must not use their names. | |||
| 45 | */ | |||
| 46 | static struct argconfig_commandline_options *global_options; | |||
| 47 | ||||
| 48 | /* Forget the options given before, also those of included tables */ | |||
| 49 | static void argconfig_reset_seen(struct argconfig_commandline_options *s) | |||
| 50 | { | |||
| 51 | for (; s->option; s++) { | |||
| 52 | if (s->config_type == CFG_INCLUDE) | |||
| 53 | argconfig_reset_seen(s->default_value); | |||
| 54 | s->seen = false0; | |||
| 55 | } | |||
| 56 | } | |||
| 57 | ||||
| 58 | /* None of the global options counts as given yet */ | |||
| 59 | void argconfig_set_global_options(struct argconfig_commandline_options *options) | |||
| 60 | { | |||
| 61 | global_options = options; | |||
| 62 | if (options) | |||
| 63 | argconfig_reset_seen(options); | |||
| 64 | } | |||
| 65 | ||||
| 66 | struct argconfig_commandline_options *argconfig_get_global_options(void) | |||
| 67 | { | |||
| 68 | return global_options; | |||
| 69 | } | |||
| 70 | ||||
| 71 | /* The entries of a root, see argconfig_root() */ | |||
| 72 | #define ARGCONFIG_ROOT_LEN3 3 | |||
| 73 | ||||
| 74 | /* | |||
| 75 | * The options a command accepts: its table @options and the global | |||
| 76 | * options, each as an include with the heading of the help. The global | |||
| 77 | * options are included last, as root[1]. The table of the global options | |||
| 78 | * themselves includes nothing else. | |||
| 79 | */ | |||
| 80 | static void argconfig_root(struct argconfig_commandline_options *root, | |||
| 81 | struct argconfig_commandline_options *options) | |||
| 82 | { | |||
| 83 | struct argconfig_commandline_options r[ARGCONFIG_ROOT_LEN3] = { | |||
| 84 | OPT_INCLUDE(options, "Options"){"", 0, ((void*)0), CFG_INCLUDE, options, ARGCONFIG_ARG_NONE, "Options", 0, ((void*)0)}, | |||
| 85 | OPT_INCLUDE(global_options, "Global options"){"", 0, ((void*)0), CFG_INCLUDE, global_options, ARGCONFIG_ARG_NONE , "Global options", 0, ((void*)0)}, | |||
| 86 | OPT_END(){ ((void*)0) } | |||
| 87 | }; | |||
| 88 | struct argconfig_commandline_options end[] = { OPT_END(){ ((void*)0) } }; | |||
| 89 | ||||
| 90 | memcpy(root, r, sizeof(r)); | |||
| 91 | if (!global_options || options == global_options) | |||
| 92 | memcpy(&root[1], end, sizeof(end)); | |||
| 93 | } | |||
| 94 | ||||
| 95 | void argconfig_append_usage(const char *str) | |||
| 96 | { | |||
| 97 | append_usage_str = str; | |||
| 98 | } | |||
| 99 | ||||
| 100 | /* "nvme" and "id ctrl" of the current command, for the usage errors */ | |||
| 101 | static const char *command_prog; | |||
| 102 | static const char *command_path; | |||
| 103 | /* argv[0] of the current parse, if no command is set */ | |||
| 104 | static const char *parse_argv0 = ""; | |||
| 105 | ||||
| 106 | void argconfig_set_command(const char *prog, const char *path) | |||
| 107 | { | |||
| 108 | command_prog = prog; | |||
| 109 | command_path = path; | |||
| 110 | } | |||
| 111 | ||||
| 112 | static const char *command_name(void) | |||
| 113 | { | |||
| 114 | return command_path ? command_path : parse_argv0; | |||
| 115 | } | |||
| 116 | ||||
| 117 | static void print_label(const char *name) | |||
| 118 | { | |||
| 119 | fprintf(stderrstderr, "%s%s:%s ", argconfig_esc(stderrstderr, ARGCONFIG_RED"\033[31m"), name, | |||
| 120 | argconfig_esc(stderrstderr, ARGCONFIG_RESET"\033[0m")); | |||
| 121 | } | |||
| 122 | ||||
| 123 | /* "<command>: " before an error of the current command, if one runs */ | |||
| 124 | void argconfig_print_error_label(void) | |||
| 125 | { | |||
| 126 | if (command_path) | |||
| 127 | print_label(command_path); | |||
| 128 | } | |||
| 129 | ||||
| 130 | /* An error of the current command, or of nvme-cli before one runs */ | |||
| 131 | void argconfig_error(const char *fmt, ...) | |||
| 132 | { | |||
| 133 | va_list ap; | |||
| 134 | ||||
| 135 | print_label(command_name()); | |||
| 136 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | |||
| 137 | vfprintf(stderrstderr, fmt, ap); | |||
| 138 | va_end(ap)__builtin_va_end(ap); | |||
| 139 | fprintf(stderrstderr, "\n"); | |||
| 140 | } | |||
| 141 | ||||
| 142 | /* The synopsis and where to find the help, after a usage error */ | |||
| 143 | void argconfig_print_usage(void) | |||
| 144 | { | |||
| 145 | fprintf(stderrstderr, "Usage: %s\n", append_usage_str); | |||
| 146 | if (command_prog && command_path) | |||
| 147 | fprintf(stderrstderr, "Try '%s %s --help' for more information.\n", | |||
| 148 | command_prog, command_path); | |||
| 149 | } | |||
| 150 | ||||
| 151 | /* A usage error of the current command, followed by its usage */ | |||
| 152 | int argconfig_usage_error(const char *fmt, ...) | |||
| 153 | { | |||
| 154 | va_list ap; | |||
| 155 | ||||
| 156 | print_label(command_name()); | |||
| 157 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | |||
| 158 | vfprintf(stderrstderr, fmt, ap); | |||
| 159 | va_end(ap)__builtin_va_end(ap); | |||
| 160 | fprintf(stderrstderr, "\n"); | |||
| 161 | argconfig_print_usage(); | |||
| 162 | ||||
| 163 | return -EINVAL22; | |||
| 164 | } | |||
| 165 | ||||
| 166 | /* The names an option accepts, below its description */ | |||
| 167 | static void show_value_names(FILE *out, const struct argconfig_opt_val *v) | |||
| 168 | { | |||
| 169 | __cleanup_free__attribute__((cleanup(shr_freep))) char *names = NULL((void*)0); | |||
| 170 | size_t len = sizeof("\nNames:"); | |||
| 171 | const struct argconfig_opt_val *n; | |||
| 172 | char *p; | |||
| 173 | ||||
| 174 | if (!v || !v->str) | |||
| 175 | return; | |||
| 176 | ||||
| 177 | for (n = v; n->str; n++) | |||
| 178 | len += strlen(n->str) + 2; | |||
| 179 | ||||
| 180 | names = malloc(len); | |||
| 181 | if (!names) | |||
| 182 | return; | |||
| 183 | ||||
| 184 | p = names + snprintf(names, len, "\nNames:"); | |||
| 185 | for (n = v; n->str; n++) | |||
| 186 | p += snprintf(p, len - (p - names), " %s%s", n->str, | |||
| 187 | n[1].str ? "," : ""); | |||
| 188 | ||||
| 189 | shr_print_word_wrapped(names, 44, 44, out); | |||
| 190 | } | |||
| 191 | ||||
| 192 | static void show_option(FILE *out, | |||
| 193 | const struct argconfig_commandline_options *option) | |||
| 194 | { | |||
| 195 | const char *meta = option->meta ? option->meta : "arg"; | |||
| 196 | enum argconfig_argument arg = option->argument_type; | |||
| 197 | char buffer[0x1000]; | |||
| 198 | size_t len = 0; | |||
| 199 | ||||
| 200 | buffer[0] = '\0'; | |||
| 201 | shr_snprintf_append(buffer, sizeof(buffer), &len, " [ "); | |||
| 202 | if (option->option) { | |||
| 203 | shr_snprintf_append(buffer, sizeof(buffer), &len, " --%s", | |||
| 204 | option->option); | |||
| 205 | if (arg == ARGCONFIG_ARG_OPTIONAL) | |||
| 206 | shr_snprintf_append(buffer, sizeof(buffer), &len, | |||
| 207 | "[=<%s>]", meta); | |||
| 208 | if (arg == ARGCONFIG_ARG_REQUIRED) | |||
| 209 | shr_snprintf_append(buffer, sizeof(buffer), &len, | |||
| 210 | "=<%s>", meta); | |||
| 211 | if (option->short_option) | |||
| 212 | shr_snprintf_append(buffer, sizeof(buffer), &len, ","); | |||
| 213 | } | |||
| 214 | if (option->short_option) { | |||
| 215 | shr_snprintf_append(buffer, sizeof(buffer), &len, " -%c", | |||
| 216 | option->short_option); | |||
| 217 | if (arg == ARGCONFIG_ARG_OPTIONAL) | |||
| 218 | shr_snprintf_append(buffer, sizeof(buffer), &len, | |||
| 219 | " [<%s>]", meta); | |||
| 220 | if (arg == ARGCONFIG_ARG_REQUIRED) | |||
| 221 | shr_snprintf_append(buffer, sizeof(buffer), &len, | |||
| 222 | " <%s>", meta); | |||
| 223 | } | |||
| 224 | shr_snprintf_append(buffer, sizeof(buffer), &len, " ] "); | |||
| 225 | ||||
| 226 | fprintf(out, "%s", buffer); | |||
| 227 | if (option->help) { | |||
| 228 | shr_print_word_wrapped("--- ", 40, len, out); | |||
| 229 | shr_print_word_wrapped(option->help, 44, 44, out); | |||
| 230 | show_value_names(out, option->opt_val); | |||
| 231 | } | |||
| 232 | fprintf(out, "\n"); | |||
| 233 | } | |||
| 234 | ||||
| 235 | /* | |||
| 236 | * Escape sequences for bold and colors go only to a terminal, and not | |||
| 237 | * when NO_COLOR is set to a non-empty value (https://no-color.org/). | |||
| 238 | */ | |||
| 239 | const char *argconfig_esc(FILE *out, const char *seq) | |||
| 240 | { | |||
| 241 | const char *no_color = getenv("NO_COLOR"); | |||
| 242 | ||||
| 243 | if (!isatty(fileno(out)) || (no_color && *no_color)) | |||
| 244 | return ""; | |||
| 245 | ||||
| 246 | return seq; | |||
| 247 | } | |||
| 248 | ||||
| 249 | /* "WARNING:" on stderr, in yellow on a terminal */ | |||
| 250 | void argconfig_print_warning(void) | |||
| 251 | { | |||
| 252 | fprintf(stderrstderr, "%sWARNING:%s", argconfig_esc(stderrstderr, ARGCONFIG_YELLOW"\033[33m"), | |||
| 253 | argconfig_esc(stderrstderr, ARGCONFIG_RESET"\033[0m")); | |||
| 254 | } | |||
| 255 | ||||
| 256 | /* A heading, in bold on a terminal */ | |||
| 257 | void argconfig_print_heading(FILE *out, const char *fmt, const char *text) | |||
| 258 | { | |||
| 259 | fputs(argconfig_esc(out, ARGCONFIG_BOLD"\033[1m"), out); | |||
| 260 | fprintf(out, fmt, text); | |||
| 261 | fputs(argconfig_esc(out, ARGCONFIG_RESET"\033[0m"), out); | |||
| 262 | } | |||
| 263 | ||||
| 264 | /* A group header is printed before the first option which follows it */ | |||
| 265 | static void print_options(FILE *out, struct argconfig_commandline_options *s, | |||
| 266 | const char *header) | |||
| 267 | { | |||
| 268 | bool_Bool header_printed = false0; | |||
| 269 | ||||
| 270 | for (; s && s->option; s++) { | |||
| 271 | if (s->config_type == CFG_INCLUDE) { | |||
| 272 | print_options(out, s->default_value, s->help); | |||
| 273 | /* the own options after it get their heading again */ | |||
| 274 | header_printed = false0; | |||
| 275 | continue; | |||
| 276 | } | |||
| 277 | if (s->config_type == CFG_GROUP_SEPARATOR) { | |||
| 278 | header = s->help; | |||
| 279 | header_printed = false0; | |||
| 280 | continue; | |||
| 281 | } | |||
| 282 | if (!header_printed) { | |||
| 283 | fputc('\n', out); | |||
| 284 | argconfig_print_heading(out, "%s:", header); | |||
| 285 | fputc('\n', out); | |||
| 286 | header_printed = true1; | |||
| 287 | } | |||
| 288 | if (!s->deprecated) | |||
| 289 | show_option(out, s); | |||
| 290 | } | |||
| 291 | } | |||
| 292 | ||||
| 293 | static void show_help(FILE *out, const char *program_desc, | |||
| 294 | struct argconfig_commandline_options *root) | |||
| 295 | { | |||
| 296 | argconfig_print_heading(out, "Usage: %s", append_usage_str); | |||
| 297 | fputs("\n\n", out); | |||
| 298 | ||||
| 299 | shr_print_word_wrapped(program_desc, 0, 0, out); | |||
| 300 | fputc('\n', out); | |||
| 301 | ||||
| 302 | print_options(out, root, "Options"); | |||
| 303 | } | |||
| 304 | ||||
| 305 | static bool_Bool help_requested; | |||
| 306 | ||||
| 307 | /* True once argconfig_parse() printed the help -h or --help asked for */ | |||
| 308 | bool_Bool argconfig_help_requested(void) | |||
| 309 | { | |||
| 310 | return help_requested; | |||
| 311 | } | |||
| 312 | ||||
| 313 | static bool_Bool argconfig_is_string(enum argconfig_types type) | |||
| 314 | { | |||
| 315 | return type == CFG_STRING || type == CFG_FILE || type == CFG_DIR; | |||
| 316 | } | |||
| 317 | ||||
| 318 | /* @u is in the range of @s, if it has one */ | |||
| 319 | bool_Bool argconfig_in_range(const struct argconfig_commandline_options *s, | |||
| 320 | uint64_t u) | |||
| 321 | { | |||
| 322 | return !s->max || (u >= s->min && u <= s->max); | |||
| 323 | } | |||
| 324 | ||||
| 325 | static struct argconfig_opt_val * | |||
| 326 | argconfig_match_val(struct argconfig_opt_val *v, const char *str); | |||
| 327 | ||||
| 328 | /* The bits the names in the comma-separated list @arg stand for */ | |||
| 329 | static int argconfig_parse_enum_list(struct argconfig_commandline_options *s, | |||
| 330 | const char *arg, uint32_t *bits) | |||
| 331 | { | |||
| 332 | __cleanup_free__attribute__((cleanup(shr_freep))) char *list = strdup(arg); | |||
| 333 | struct argconfig_opt_val *v; | |||
| 334 | char *p = list, *name; | |||
| 335 | ||||
| 336 | if (!list) | |||
| 337 | return -ENOMEM12; | |||
| 338 | ||||
| 339 | *bits = 0; | |||
| 340 | while ((name = shr_strsep(&p, ",")) != NULL((void*)0)) { | |||
| 341 | v = s->opt_val ? argconfig_match_val(s->opt_val, name) : NULL((void*)0); | |||
| 342 | if (!v
| |||
| 343 | return -EINVAL22; | |||
| ||||
| 344 | *bits |= (uint32_t)v->val.int_val; | |||
| 345 | } | |||
| 346 | ||||
| 347 | return 0; | |||
| 348 | } | |||
| 349 | ||||
| 350 | /* An unsigned number up to @max, in the range of @s */ | |||
| 351 | static int argconfig_parse_u64(struct argconfig_commandline_options *s, | |||
| 352 | const char *arg, uint64_t max, uint64_t *u) | |||
| 353 | { | |||
| 354 | int ret = shr_parse_u64(arg, max, u); | |||
| 355 | ||||
| 356 | if (!ret && !argconfig_in_range(s, *u)) | |||
| 357 | return -ERANGE34; | |||
| 358 | ||||
| 359 | return ret; | |||
| 360 | } | |||
| 361 | ||||
| 362 | static int argconfig_parse_type(struct argconfig_commandline_options *s, | |||
| 363 | char *arg) | |||
| 364 | { | |||
| 365 | void *value = s->default_value; | |||
| 366 | uint64_t u; | |||
| 367 | int64_t i; | |||
| 368 | double d; | |||
| 369 | int ret = 0; | |||
| 370 | ||||
| 371 | /* only a string option can do without a value */ | |||
| 372 | if (!arg && !argconfig_is_string(s->config_type) && | |||
| ||||
| 373 | s->config_type != CFG_FLAG && s->config_type != CFG_INCREMENT) | |||
| 374 | return -EINVAL22; | |||
| 375 | ||||
| 376 | switch (s->config_type) { | |||
| 377 | case CFG_STRING: | |||
| 378 | case CFG_FILE: | |||
| 379 | case CFG_DIR: | |||
| 380 | *(char **)value = arg; | |||
| 381 | break; | |||
| 382 | case CFG_INT: | |||
| 383 | ret = shr_parse_s64(arg, INT_MIN(-2147483647 -1), INT_MAX2147483647, &i); | |||
| 384 | if (!ret) | |||
| 385 | *(int *)value = i; | |||
| 386 | break; | |||
| 387 | case CFG_BYTE: | |||
| 388 | ret = argconfig_parse_u64(s, arg, UINT8_MAX(255), &u); | |||
| 389 | if (!ret) | |||
| 390 | *(uint8_t *)value = u; | |||
| 391 | break; | |||
| 392 | case CFG_SHORT: | |||
| 393 | ret = argconfig_parse_u64(s, arg, UINT16_MAX(65535), &u); | |||
| 394 | if (!ret) | |||
| 395 | *(uint16_t *)value = u; | |||
| 396 | break; | |||
| 397 | case CFG_POSITIVE: | |||
| 398 | ret = argconfig_parse_u64(s, arg, UINT32_MAX(4294967295U), &u); | |||
| 399 | if (!ret) | |||
| 400 | *(uint32_t *)value = u; | |||
| 401 | break; | |||
| 402 | case CFG_INCREMENT: | |||
| 403 | *(int *)value += 1; | |||
| 404 | break; | |||
| 405 | case CFG_ENUM: | |||
| 406 | /* only the names, which argconfig_parse_val() looked up */ | |||
| 407 | ret = -EINVAL22; | |||
| 408 | break; | |||
| 409 | case CFG_ENUM_LIST: { | |||
| 410 | uint32_t bits; | |||
| 411 | ||||
| 412 | ret = argconfig_parse_enum_list(s, arg, &bits); | |||
| 413 | if (!ret) | |||
| 414 | *(uint32_t *)value |= bits; | |||
| 415 | break; | |||
| 416 | } | |||
| 417 | case CFG_LONG: | |||
| 418 | ret = argconfig_parse_u64(s, arg, UINT64_MAX(18446744073709551615UL), &u); | |||
| 419 | if (!ret) | |||
| 420 | *(uint64_t *)value = u; | |||
| 421 | break; | |||
| 422 | case CFG_LONG_SUFFIX: { | |||
| 423 | char *endptr; | |||
| 424 | ||||
| 425 | /* the suffix parser takes whitespace and a sign */ | |||
| 426 | if (!isdigit((unsigned char)*arg)((*__ctype_b_loc ())[(int) (((unsigned char)*arg))] & (unsigned short int) _ISdigit)) | |||
| 427 | return -EINVAL22; | |||
| 428 | ret = shr_suffix_binary_parse(arg, &endptr, &u); | |||
| 429 | if (!ret && !argconfig_in_range(s, u)) | |||
| 430 | ret = -ERANGE34; | |||
| 431 | if (!ret) | |||
| 432 | *(uint64_t *)value = u; | |||
| 433 | break; | |||
| 434 | } | |||
| 435 | case CFG_DOUBLE: | |||
| 436 | ret = shr_parse_double(arg, &d); | |||
| 437 | if (!ret) | |||
| 438 | *(double *)value = d; | |||
| 439 | break; | |||
| 440 | case CFG_FLAG: | |||
| 441 | *(bool_Bool *)value = true1; | |||
| 442 | break; | |||
| 443 | case CFG_GROUP_SEPARATOR: | |||
| 444 | case CFG_INCLUDE: | |||
| 445 | break; | |||
| 446 | } | |||
| 447 | ||||
| 448 | return ret; | |||
| 449 | } | |||
| 450 | ||||
| 451 | static void argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *opt_val, void *val) | |||
| 452 | { | |||
| 453 | switch (type) { | |||
| 454 | case CFG_FLAG: | |||
| 455 | *(bool_Bool *)val = opt_val->bool_val; | |||
| 456 | break; | |||
| 457 | case CFG_LONG_SUFFIX: | |||
| 458 | *(uint64_t *)val = opt_val->long_suffix; | |||
| 459 | break; | |||
| 460 | case CFG_POSITIVE: | |||
| 461 | *(uint32_t *)val = opt_val->positive; | |||
| 462 | break; | |||
| 463 | case CFG_INT: | |||
| 464 | case CFG_ENUM: | |||
| 465 | *(int *)val = opt_val->int_val; | |||
| 466 | break; | |||
| 467 | case CFG_LONG: | |||
| 468 | *(uint64_t *)val = opt_val->long_val; | |||
| 469 | break; | |||
| 470 | case CFG_DOUBLE: | |||
| 471 | *(double *)val = opt_val->double_val; | |||
| 472 | break; | |||
| 473 | case CFG_BYTE: | |||
| 474 | *(uint8_t *)val = opt_val->byte; | |||
| 475 | break; | |||
| 476 | case CFG_SHORT: | |||
| 477 | *(uint16_t *)val = opt_val->short_val; | |||
| 478 | break; | |||
| 479 | case CFG_INCREMENT: | |||
| 480 | *(int *)val = opt_val->increment; | |||
| 481 | break; | |||
| 482 | case CFG_STRING: | |||
| 483 | case CFG_FILE: | |||
| 484 | case CFG_DIR: | |||
| 485 | *(char **)val = opt_val->string; | |||
| 486 | break; | |||
| 487 | case CFG_ENUM_LIST: | |||
| 488 | case CFG_GROUP_SEPARATOR: | |||
| 489 | case CFG_INCLUDE: | |||
| 490 | break; | |||
| 491 | } | |||
| 492 | } | |||
| 493 | ||||
| 494 | static struct argconfig_opt_val * | |||
| 495 | argconfig_match_val(struct argconfig_opt_val *v, const char *str) | |||
| 496 | { | |||
| 497 | size_t len = strlen(str); | |||
| 498 | struct argconfig_opt_val *match = NULL((void*)0); | |||
| 499 | ||||
| 500 | /* an empty value is a prefix of every name */ | |||
| 501 | if (!len) | |||
| 502 | return NULL((void*)0); | |||
| 503 | ||||
| 504 | for (; v->str; v++) { | |||
| 505 | if (strncasecmp(str, v->str, len)) | |||
| 506 | continue; | |||
| 507 | ||||
| 508 | if (len == strlen(v->str)) | |||
| 509 | return v; | |||
| 510 | ||||
| 511 | if (match) | |||
| 512 | return NULL((void*)0); /* multiple matches; input is ambiguous */ | |||
| 513 | ||||
| 514 | match = v; | |||
| 515 | } | |||
| 516 | ||||
| 517 | return match; | |||
| 518 | } | |||
| 519 | ||||
| 520 | /* | |||
| 521 | * Store the value which @str names in the value table of @s. False if | |||
| 522 | * the option has no value of that name. | |||
| 523 | */ | |||
| 524 | bool_Bool argconfig_set_value_name(struct argconfig_commandline_options *s, | |||
| 525 | const char *str) | |||
| 526 | { | |||
| 527 | struct argconfig_opt_val *v = s->opt_val; | |||
| 528 | ||||
| 529 | if (v) | |||
| 530 | v = argconfig_match_val(v, str); | |||
| 531 | if (!v) | |||
| 532 | return false0; | |||
| 533 | ||||
| 534 | argconfig_set_opt_val(v->type, &v->val, s->default_value); | |||
| 535 | return true1; | |||
| 536 | } | |||
| 537 | ||||
| 538 | static int argconfig_parse_val(struct argconfig_commandline_options *s, | |||
| 539 | char *arg) | |||
| 540 | { | |||
| 541 | /* the names of a list are looked up one by one */ | |||
| 542 | if (arg && s->config_type != CFG_ENUM_LIST && | |||
| 543 | argconfig_set_value_name(s, arg)) | |||
| 544 | return 0; | |||
| 545 | ||||
| 546 | return argconfig_parse_type(s, arg); | |||
| 547 | } | |||
| 548 | ||||
| 549 | /* The non-option arguments of the last successful argconfig_parse() */ | |||
| 550 | static struct { | |||
| 551 | char **argv; | |||
| 552 | int argc; | |||
| 553 | } operands; | |||
| 554 | ||||
| 555 | int argconfig_nr_operands(void) | |||
| 556 | { | |||
| 557 | return operands.argc; | |||
| 558 | } | |||
| 559 | ||||
| 560 | char *argconfig_operand(int i) | |||
| 561 | { | |||
| 562 | if (i < 0 || i >= operands.argc) | |||
| 563 | return NULL((void*)0); | |||
| 564 | ||||
| 565 | return operands.argv[i]; | |||
| 566 | } | |||
| 567 | ||||
| 568 | /* | |||
| 569 | * The state of one parse. argv is not modified: the operands are collected | |||
| 570 | * in their own array, in the order they are given. | |||
| 571 | */ | |||
| 572 | struct argconfig_parser { | |||
| 573 | int argc; | |||
| 574 | char **argv; | |||
| 575 | int ind; | |||
| 576 | /* the table of the command, the global options included */ | |||
| 577 | struct argconfig_commandline_options root[ARGCONFIG_ROOT_LEN3]; | |||
| 578 | /* | |||
| 579 | * Global options stop at the first operand, the subcommand. Their | |||
| 580 | * implicit -h, --help, -V and --version are recorded and the parse | |||
| 581 | * goes on. | |||
| 582 | */ | |||
| 583 | bool_Bool global; | |||
| 584 | bool_Bool help; | |||
| 585 | bool_Bool version; | |||
| 586 | char **operands; | |||
| 587 | int nr_operands; | |||
| 588 | }; | |||
| 589 | ||||
| 590 | /* | |||
| 591 | * Internal result of argconfig_parse_args(): an option was malformed. Like | |||
| 592 | * ARGCONFIG_HELP, it prints the help of a command. | |||
| 593 | */ | |||
| 594 | #define ARGCONFIG_USAGE4 4 | |||
| 595 | ||||
| 596 | /* | |||
| 597 | * The parse errors use the wording of glibc's getopt, which printed them | |||
| 598 | * before argconfig had its own parser. | |||
| 599 | */ | |||
| 600 | static int argconfig_parse_error(struct argconfig_parser *p, | |||
| 601 | const char *fmt, ...) | |||
| 602 | { | |||
| 603 | va_list ap; | |||
| 604 | ||||
| 605 | print_label(command_name()); | |||
| 606 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | |||
| 607 | vfprintf(stderrstderr, fmt, ap); | |||
| 608 | va_end(ap)__builtin_va_end(ap); | |||
| 609 | fprintf(stderrstderr, "\n"); | |||
| 610 | ||||
| 611 | return ARGCONFIG_USAGE4; | |||
| 612 | } | |||
| 613 | ||||
| 614 | static struct argconfig_commandline_options * | |||
| 615 | argconfig_find_long(struct argconfig_commandline_options *s, | |||
| 616 | const char *name, size_t len) | |||
| 617 | { | |||
| 618 | struct argconfig_commandline_options *o; | |||
| 619 | ||||
| 620 | for (; s->option; s++) { | |||
| 621 | if (s->config_type == CFG_INCLUDE) { | |||
| 622 | o = argconfig_find_long(s->default_value, name, len); | |||
| 623 | if (o) | |||
| 624 | return o; | |||
| 625 | continue; | |||
| 626 | } | |||
| 627 | if (s->config_type == CFG_GROUP_SEPARATOR || | |||
| 628 | is_null_or_empty(s->option)) | |||
| 629 | continue; | |||
| 630 | if (strlen(s->option) == len && !strncmp(s->option, name, len)) | |||
| 631 | return s; | |||
| 632 | if (s->old_name && strlen(s->old_name) == len && | |||
| 633 | !strncmp(s->old_name, name, len)) | |||
| 634 | return s; | |||
| 635 | } | |||
| 636 | ||||
| 637 | return NULL((void*)0); | |||
| 638 | } | |||
| 639 | ||||
| 640 | static struct argconfig_commandline_options * | |||
| 641 | argconfig_find_short(struct argconfig_commandline_options *s, char c) | |||
| 642 | { | |||
| 643 | struct argconfig_commandline_options *o; | |||
| 644 | ||||
| 645 | for (; s->option; s++) { | |||
| 646 | if (s->config_type == CFG_INCLUDE) { | |||
| 647 | o = argconfig_find_short(s->default_value, c); | |||
| 648 | if (o) | |||
| 649 | return o; | |||
| 650 | continue; | |||
| 651 | } | |||
| 652 | if (s->config_type == CFG_GROUP_SEPARATOR) | |||
| 653 | continue; | |||
| 654 | if (s->short_option == c) | |||
| 655 | return s; | |||
| 656 | } | |||
| 657 | ||||
| 658 | return NULL((void*)0); | |||
| 659 | } | |||
| 660 | ||||
| 661 | /* | |||
| 662 | * The option of @root with the long name @name, a global option first: a | |||
| 663 | * global option is the same for every command. &root[1] is the include | |||
| 664 | * of the global options alone. | |||
| 665 | */ | |||
| 666 | static struct argconfig_commandline_options * | |||
| 667 | argconfig_find_long_any(struct argconfig_commandline_options *root, | |||
| 668 | const char *name, size_t len) | |||
| 669 | { | |||
| 670 | struct argconfig_commandline_options *o; | |||
| 671 | ||||
| 672 | o = argconfig_find_long(&root[1], name, len); | |||
| 673 | ||||
| 674 | return o ? o : argconfig_find_long(root, name, len); | |||
| 675 | } | |||
| 676 | ||||
| 677 | static struct argconfig_commandline_options * | |||
| 678 | argconfig_find_short_any(struct argconfig_commandline_options *root, char c) | |||
| 679 | { | |||
| 680 | struct argconfig_commandline_options *o; | |||
| 681 | ||||
| 682 | o = argconfig_find_short(&root[1], c); | |||
| 683 | ||||
| 684 | return o ? o : argconfig_find_short(root, c); | |||
| 685 | } | |||
| 686 | ||||
| 687 | /* What a value of @s has to look like */ | |||
| 688 | static const char *argconfig_expected(struct argconfig_commandline_options *s) | |||
| 689 | { | |||
| 690 | static char range[64]; | |||
| 691 | ||||
| 692 | if (s->max) { | |||
| 693 | snprintf(range, sizeof(range), "a number from %" PRIu64"l" "u" | |||
| 694 | " to %" PRIu64"l" "u", s->min, s->max); | |||
| 695 | return range; | |||
| 696 | } | |||
| 697 | ||||
| 698 | switch (s->config_type) { | |||
| 699 | case CFG_BYTE: | |||
| 700 | return "a number from 0 to 255"; | |||
| 701 | case CFG_SHORT: | |||
| 702 | return "a number from 0 to 65535"; | |||
| 703 | case CFG_POSITIVE: | |||
| 704 | return "a number from 0 to 4294967295"; | |||
| 705 | case CFG_LONG: | |||
| 706 | return "a number from 0 to 18446744073709551615"; | |||
| 707 | case CFG_LONG_SUFFIX: | |||
| 708 | return "a size such as 4096 or 4Ki"; | |||
| 709 | default: | |||
| 710 | return "a number"; | |||
| 711 | } | |||
| 712 | } | |||
| 713 | ||||
| 714 | /* @arg is no value of @s, given by its short name @c or its long name */ | |||
| 715 | static int argconfig_value_error(struct argconfig_commandline_options *s, | |||
| 716 | char c, const char *arg) | |||
| 717 | { | |||
| 718 | __cleanup_free__attribute__((cleanup(shr_freep))) char *e = arg ? shr_escape(arg) : NULL((void*)0); | |||
| 719 | bool_Bool list = s->config_type == CFG_ENUM_LIST; | |||
| 720 | const struct argconfig_opt_val *v; | |||
| 721 | int n = 0; | |||
| 722 | ||||
| 723 | print_label(command_name()); | |||
| 724 | fprintf(stderrstderr, "invalid value '%s' for ", e ? e : ""); | |||
| 725 | if (c) | |||
| 726 | fprintf(stderrstderr, "-%c", c); | |||
| 727 | else | |||
| 728 | fprintf(stderrstderr, "--%s", s->option); | |||
| 729 | fprintf(stderrstderr, ": expected "); | |||
| 730 | if (list) | |||
| 731 | fprintf(stderrstderr, "a comma-separated list of "); | |||
| 732 | else if (s->config_type != CFG_ENUM) | |||
| 733 | fprintf(stderrstderr, "%s%s", argconfig_expected(s), | |||
| 734 | s->opt_val ? " or " : ""); | |||
| 735 | ||||
| 736 | /* A long list of names would drown the error, the help has them */ | |||
| 737 | for (v = s->opt_val; v && v->str; v++) | |||
| 738 | n++; | |||
| 739 | if (n > 8) | |||
| 740 | fprintf(stderrstderr, "%sthe names in the help", | |||
| 741 | list ? "" : "one of "); | |||
| 742 | for (v = s->opt_val; n <= 8 && v && v->str; v++) | |||
| 743 | fprintf(stderrstderr, "%s%s", v != s->opt_val ? ", " : | |||
| 744 | list ? "" : "one of: ", v->str); | |||
| 745 | fprintf(stderrstderr, "\n"); | |||
| 746 | ||||
| 747 | /* "010" was octal once, say what to write instead */ | |||
| 748 | if (arg && !list && s->config_type != CFG_ENUM && | |||
| 749 | shr_has_leading_zero(arg)) { | |||
| 750 | const char *d = arg; | |||
| 751 | ||||
| 752 | while (d[0] == '0' && d[1]) | |||
| 753 | d++; | |||
| 754 | fprintf(stderrstderr, | |||
| 755 | "a number must not start with 0: write %s, or 0x%s for hexadecimal\n", | |||
| 756 | d, d); | |||
| 757 | } | |||
| 758 | ||||
| 759 | return ARGCONFIG_USAGE4; | |||
| 760 | } | |||
| 761 | ||||
| 762 | /* @s given by its short name @c, or by its long name without, and @arg */ | |||
| 763 | static int argconfig_apply(struct argconfig_commandline_options *s, char c, | |||
| 764 | char *arg) | |||
| 765 | { | |||
| 766 | int ret; | |||
| 767 | ||||
| 768 | if (s->deprecated && !s->seen) { | |||
| 769 | argconfig_print_warning(); | |||
| 770 | if (c) | |||
| 771 | fprintf(stderrstderr, " '-%c'", c); | |||
| 772 | else | |||
| 773 | fprintf(stderrstderr, " '--%s'", s->option); | |||
| 774 | fprintf(stderrstderr, | |||
| 775 | " is deprecated and will be removed in the next major version, use '%s' instead\n", | |||
| 776 | s->deprecated); | |||
| 777 | } | |||
| 778 | s->seen = true1; | |||
| 779 | ||||
| 780 | if (!s->default_value) | |||
| 781 | return 0; | |||
| 782 | ||||
| 783 | ret = argconfig_parse_val(s, arg); | |||
| 784 | if (ret == -EINVAL22 || ret == -ERANGE34) | |||
| 785 | return argconfig_value_error(s, c, arg); | |||
| 786 | ||||
| 787 | return ret; | |||
| 788 | } | |||
| 789 | ||||
| 790 | /* | |||
| 791 | * A copy of the template table @tmpl, whose options store into the | |||
| 792 | * struct at @tmpl_base of @size bytes, bound to the struct of the same | |||
| 793 | * type at @base. Commands share the template and parse into their own | |||
| 794 | * struct. The caller frees the copy. | |||
| 795 | */ | |||
| 796 | struct argconfig_commandline_options * | |||
| 797 | argconfig_bind(const struct argconfig_commandline_options *tmpl, | |||
| 798 | const void *tmpl_base, size_t size, void *base) | |||
| 799 | { | |||
| 800 | struct argconfig_commandline_options *dst; | |||
| 801 | uintptr_t start = (uintptr_t)tmpl_base; | |||
| 802 | uintptr_t v; | |||
| 803 | size_t n = 0, i; | |||
| 804 | ||||
| 805 | while (tmpl[n].option) | |||
| 806 | n++; | |||
| 807 | ||||
| 808 | dst = calloc(n + 1, sizeof(*dst)); | |||
| 809 | if (!dst) | |||
| 810 | return NULL((void*)0); | |||
| 811 | memcpy(dst, tmpl, (n + 1) * sizeof(*dst)); | |||
| 812 | ||||
| 813 | for (i = 0; i < n; i++) { | |||
| 814 | v = (uintptr_t)tmpl[i].default_value; | |||
| 815 | if (v >= start && v < start + size) | |||
| 816 | dst[i].default_value = (char *)base + (v - start); | |||
| 817 | } | |||
| 818 | ||||
| 819 | return dst; | |||
| 820 | } | |||
| 821 | ||||
| 822 | /* The name of the enum value @val in the value table @v, or NULL */ | |||
| 823 | const char *argconfig_value_name(const struct argconfig_opt_val *v, int val) | |||
| 824 | { | |||
| 825 | for (; v && v->str; v++) { | |||
| 826 | if (v->type == CFG_ENUM && v->val.int_val == val) | |||
| 827 | return v->str; | |||
| 828 | } | |||
| 829 | ||||
| 830 | return NULL((void*)0); | |||
| 831 | } | |||
| 832 | ||||
| 833 | /* The value of a required argument which is not attached to the option */ | |||
| 834 | static char *argconfig_next_arg(struct argconfig_parser *p) | |||
| 835 | { | |||
| 836 | if (p->ind + 1 >= p->argc) | |||
| 837 | return NULL((void*)0); | |||
| 838 | ||||
| 839 | return p->argv[++p->ind]; | |||
| 840 | } | |||
| 841 | ||||
| 842 | /* "--name", "--name=value" or "--name value" */ | |||
| 843 | static int argconfig_parse_long(struct argconfig_parser *p, char *arg) | |||
| 844 | { | |||
| 845 | struct argconfig_commandline_options *s; | |||
| 846 | char *name = arg + 2, *value = NULL((void*)0); | |||
| 847 | char *eq = strchr(name, '=')_Generic (0 ? (name) : (void *) 1, const void *: (const char * ) (strchr (name, '=')), default: strchr (name, '=')); | |||
| 848 | size_t len = eq ? (size_t)(eq - name) : strlen(name); | |||
| 849 | ||||
| 850 | s = argconfig_find_long_any(p->root, name, len); | |||
| 851 | if (!s) { | |||
| 852 | bool_Bool help = len == 4 && !strncmp(name, "help", 4); | |||
| 853 | bool_Bool version = len == 7 && !strncmp(name, "version", 7); | |||
| 854 | ||||
| 855 | if (help && !p->global) | |||
| 856 | return ARGCONFIG_HELP1; | |||
| 857 | if (!p->global || (!help && !version)) { | |||
| 858 | __cleanup_free__attribute__((cleanup(shr_freep))) char *e = shr_escape(arg); | |||
| 859 | ||||
| 860 | return argconfig_parse_error(p, | |||
| 861 | "unrecognized option '%s'", e ? e : ""); | |||
| 862 | } | |||
| 863 | if (eq) | |||
| 864 | return argconfig_parse_error(p, | |||
| 865 | "option '--%.*s' doesn't allow an argument", | |||
| 866 | (int)len, name); | |||
| 867 | p->help |= help; | |||
| 868 | p->version |= version; | |||
| 869 | return 0; | |||
| 870 | } | |||
| 871 | ||||
| 872 | switch (s->argument_type) { | |||
| 873 | case ARGCONFIG_ARG_NONE: | |||
| 874 | if (eq) | |||
| 875 | return argconfig_parse_error(p, | |||
| 876 | "option '--%s' doesn't allow an argument", | |||
| 877 | s->option); | |||
| 878 | break; | |||
| 879 | case ARGCONFIG_ARG_REQUIRED: | |||
| 880 | value = eq ? eq + 1 : argconfig_next_arg(p); | |||
| 881 | if (!value) | |||
| 882 | return argconfig_parse_error(p, | |||
| 883 | "option '--%s' requires an argument", | |||
| 884 | s->option); | |||
| 885 | break; | |||
| 886 | case ARGCONFIG_ARG_OPTIONAL: | |||
| 887 | /* Only an attached value, "--name value" is an operand */ | |||
| 888 | if (eq) | |||
| 889 | value = eq + 1; | |||
| 890 | break; | |||
| 891 | } | |||
| 892 | ||||
| 893 | if (s->old_name && !s->seen && | |||
| 894 | (strlen(s->option) != len || strncmp(s->option, name, len))) { | |||
| 895 | argconfig_print_warning(); | |||
| 896 | fprintf(stderrstderr, | |||
| 897 | " '--%s' is deprecated and will be removed in the next major version, use '--%s' instead\n", | |||
| 898 | s->old_name, s->option); | |||
| 899 | } | |||
| 900 | ||||
| 901 | return argconfig_apply(s, 0, value); | |||
| 902 | } | |||
| 903 | ||||
| 904 | /* "-a", "-abc", "-ovalue", "-o value" */ | |||
| 905 | static int argconfig_parse_short(struct argconfig_parser *p, char *arg) | |||
| 906 | { | |||
| 907 | struct argconfig_commandline_options *s; | |||
| 908 | char *c, *value; | |||
| 909 | int ret; | |||
| 910 | ||||
| 911 | for (c = arg + 1; *c; c++) { | |||
| 912 | if (!p->global && (*c == 'h' || *c == '?')) | |||
| 913 | return ARGCONFIG_HELP1; | |||
| 914 | ||||
| 915 | s = argconfig_find_short_any(p->root, *c); | |||
| 916 | if (!s && p->global && (*c == 'h' || *c == 'V')) { | |||
| 917 | p->help |= *c == 'h'; | |||
| 918 | p->version |= *c == 'V'; | |||
| 919 | continue; | |||
| 920 | } | |||
| 921 | if (!s) { | |||
| 922 | char bad[2] = { *c, '\0' }; | |||
| 923 | __cleanup_free__attribute__((cleanup(shr_freep))) char *e = shr_escape(bad); | |||
| 924 | ||||
| 925 | return argconfig_parse_error(p, | |||
| 926 | "invalid option -- '%s'", e ? e : ""); | |||
| 927 | } | |||
| 928 | ||||
| 929 | value = NULL((void*)0); | |||
| 930 | switch (s->argument_type) { | |||
| 931 | case ARGCONFIG_ARG_NONE: | |||
| 932 | break; | |||
| 933 | case ARGCONFIG_ARG_REQUIRED: | |||
| 934 | value = c[1] ? c + 1 : argconfig_next_arg(p); | |||
| 935 | if (!value) | |||
| 936 | return argconfig_parse_error(p, | |||
| 937 | "option requires an argument -- '%c'", | |||
| 938 | *c); | |||
| 939 | break; | |||
| 940 | case ARGCONFIG_ARG_OPTIONAL: | |||
| 941 | if (c[1]) | |||
| 942 | value = c + 1; | |||
| 943 | break; | |||
| 944 | } | |||
| 945 | ||||
| 946 | ret = argconfig_apply(s, *c, value); | |||
| 947 | if (ret || s->argument_type != ARGCONFIG_ARG_NONE) | |||
| 948 | return ret; | |||
| 949 | } | |||
| 950 | ||||
| 951 | return 0; | |||
| 952 | } | |||
| 953 | ||||
| 954 | /* | |||
| 955 | * Options and operands may be given in any order, "--" ends the options. | |||
| 956 | * Returns 0, ARGCONFIG_HELP, ARGCONFIG_USAGE or a negative errno from the | |||
| 957 | * value conversion. | |||
| 958 | */ | |||
| 959 | static int argconfig_parse_args(struct argconfig_parser *p) | |||
| 960 | { | |||
| 961 | char *arg; | |||
| 962 | int ret; | |||
| 963 | ||||
| 964 | /* | |||
| 965 | * The global options keep theirs: they may have been given before | |||
| 966 | * the command name. | |||
| 967 | */ | |||
| 968 | argconfig_reset_seen(p->root[0].default_value); | |||
| 969 | ||||
| 970 | /* argv ends at argc or at a NULL, whichever comes first */ | |||
| 971 | for (p->ind = 1; p->ind < p->argc && p->argv[p->ind]; p->ind++) { | |||
| 972 | arg = p->argv[p->ind]; | |||
| 973 | ||||
| 974 | if (!strcmp(arg, "--")) { | |||
| 975 | p->ind++; | |||
| 976 | if (p->global) | |||
| 977 | return 0; | |||
| 978 | while (p->ind < p->argc && p->argv[p->ind]) | |||
| 979 | p->operands[p->nr_operands++] = | |||
| 980 | p->argv[p->ind++]; | |||
| 981 | return 0; | |||
| 982 | } | |||
| 983 | ||||
| 984 | if (arg[0] != '-' || !arg[1]) { | |||
| 985 | if (p->global) | |||
| 986 | return 0; | |||
| 987 | p->operands[p->nr_operands++] = arg; | |||
| 988 | continue; | |||
| 989 | } | |||
| 990 | ||||
| 991 | if (arg[1] == '-') | |||
| 992 | ret = argconfig_parse_long(p, arg); | |||
| 993 | else | |||
| 994 | ret = argconfig_parse_short(p, arg); | |||
| 995 | if (ret) | |||
| 996 | return ret; | |||
| 997 | } | |||
| 998 | ||||
| 999 | return 0; | |||
| 1000 | } | |||
| 1001 | ||||
| 1002 | static argconfig_parse_hook_fn argconfig_parse_hook; | |||
| 1003 | ||||
| 1004 | void argconfig_set_parse_hook(argconfig_parse_hook_fn hook) | |||
| 1005 | { | |||
| 1006 | argconfig_parse_hook = hook; | |||
| 1007 | } | |||
| 1008 | ||||
| 1009 | static bool_Bool given_in(struct argconfig_commandline_options *s, | |||
| 1010 | const void *value) | |||
| 1011 | { | |||
| 1012 | bool_Bool given = false0; | |||
| 1013 | ||||
| 1014 | for (; s && s->option; s++) { | |||
| 1015 | if (s->config_type == CFG_INCLUDE) | |||
| 1016 | given |= given_in(s->default_value, value); | |||
| 1017 | else | |||
| 1018 | given |= s->default_value == value && s->seen; | |||
| 1019 | } | |||
| 1020 | ||||
| 1021 | return given; | |||
| 1022 | } | |||
| 1023 | ||||
| 1024 | /* --verbose, or an alias of it such as --human-readable, was given */ | |||
| 1025 | static bool_Bool argconfig_check_verbose(struct argconfig_commandline_options *root) | |||
| 1026 | { | |||
| 1027 | struct argconfig_commandline_options *v; | |||
| 1028 | ||||
| 1029 | v = argconfig_find_long_any(root, "verbose", strlen("verbose")); | |||
| 1030 | if (!v || v->config_type != CFG_INCREMENT) | |||
| 1031 | return false0; | |||
| 1032 | ||||
| 1033 | return given_in(root, v->default_value); | |||
| 1034 | } | |||
| 1035 | ||||
| 1036 | int argconfig_parse(int argc, char *argv[], const char *program_desc, | |||
| 1037 | struct argconfig_commandline_options *options) | |||
| 1038 | { | |||
| 1039 | static char **operand_buf; | |||
| 1040 | struct argconfig_parser p = { | |||
| 1041 | .argc = argc, | |||
| 1042 | .argv = argv, | |||
| 1043 | }; | |||
| 1044 | int ret; | |||
| 1045 | ||||
| 1046 | argconfig_root(p.root, options); | |||
| 1047 | parse_argv0 = argc > 0 ? argv[0] : ""; | |||
| 1048 | free(operand_buf); | |||
| 1049 | operand_buf = NULL((void*)0); | |||
| 1050 | operands.argv = NULL((void*)0); | |||
| 1051 | operands.argc = 0; | |||
| 1052 | ||||
| 1053 | if (argconfig_parse_hook) | |||
| 1054 | return argconfig_parse_hook(argc, argv, program_desc, p.root); | |||
| 1055 | ||||
| 1056 | p.operands = calloc(argc > 0 ? argc : 1, sizeof(*p.operands)); | |||
| 1057 | if (!p.operands) | |||
| 1058 | return -ENOMEM12; | |||
| 1059 | ||||
| 1060 | ret = argconfig_parse_args(&p); | |||
| 1061 | if (ret) { | |||
| 1062 | /* | |||
| 1063 | * The help that was asked for goes to stdout, a usage error is | |||
| 1064 | * followed by the synopsis on stderr. The command stops | |||
| 1065 | * either way: it gets ARGCONFIG_HELP or an error. | |||
| 1066 | */ | |||
| 1067 | if (ret == ARGCONFIG_HELP1) { | |||
| 1068 | show_help(stdoutstdout, program_desc, p.root); | |||
| 1069 | help_requested = true1; | |||
| 1070 | } else if (ret > 0) { | |||
| 1071 | argconfig_print_usage(); | |||
| 1072 | ret = -EINVAL22; | |||
| 1073 | } | |||
| 1074 | free(p.operands); | |||
| 1075 | } else { | |||
| 1076 | operand_buf = p.operands; | |||
| 1077 | operands.argv = p.operands; | |||
| 1078 | operands.argc = p.nr_operands; | |||
| 1079 | } | |||
| 1080 | ||||
| 1081 | if (!argconfig_check_verbose(p.root)) | |||
| 1082 | setlocale(LC_ALL6, "C"); | |||
| 1083 | ||||
| 1084 | return ret; | |||
| 1085 | } | |||
| 1086 | ||||
| 1087 | /* | |||
| 1088 | * Parse global (pre-subcommand) options from argc/argv. Stops at the first | |||
| 1089 | * non-option argument (i.e. the subcommand name) so that subcommand-specific | |||
| 1090 | * options are left in place for the subcommand dispatcher. After a successful | |||
| 1091 | * call, *argind is the index of the first non-option argument (the | |||
| 1092 | * subcommand), or argc if there is none. | |||
| 1093 | * | |||
| 1094 | * Unless the table has its own, -h, --help, -V and --version are accepted | |||
| 1095 | * like a global option. Returns ARGCONFIG_HELP and ARGCONFIG_VERSION for | |||
| 1096 | * them, 0 otherwise, or a negative errno on failure. @options may be NULL. | |||
| 1097 | */ | |||
| 1098 | int argconfig_parse_global(int argc, char *argv[], | |||
| 1099 | struct argconfig_commandline_options *options, | |||
| 1100 | int *argind) | |||
| 1101 | { | |||
| 1102 | static struct argconfig_commandline_options no_options[] = { | |||
| 1103 | OPT_END(){ ((void*)0) } | |||
| 1104 | }; | |||
| 1105 | struct argconfig_parser p = { | |||
| 1106 | .argc = argc, | |||
| 1107 | .argv = argv, | |||
| 1108 | .global = true1, | |||
| 1109 | }; | |||
| 1110 | int ret; | |||
| 1111 | ||||
| 1112 | argconfig_root(p.root, options ? options : no_options); | |||
| 1113 | ||||
| 1114 | parse_argv0 = argc > 0 ? argv[0] : ""; | |||
| 1115 | ret = argconfig_parse_args(&p); | |||
| 1116 | *argind = p.ind; | |||
| 1117 | if (ret) | |||
| 1118 | return ret > 0 ? -EINVAL22 : ret; | |||
| 1119 | ||||
| 1120 | return (p.help ? ARGCONFIG_HELP1 : 0) | | |||
| 1121 | (p.version ? ARGCONFIG_VERSION2 : 0); | |||
| 1122 | } | |||
| 1123 | ||||
| 1124 | /* | |||
| 1125 | * True if an option which stores to @value was given, in the table or in | |||
| 1126 | * the global options. Aliases share the variable, so any of them counts. | |||
| 1127 | */ | |||
| 1128 | bool_Bool argconfig_given(struct argconfig_commandline_options *s, | |||
| 1129 | const void *value) | |||
| 1130 | { | |||
| 1131 | struct argconfig_commandline_options root[ARGCONFIG_ROOT_LEN3]; | |||
| 1132 | ||||
| 1133 | if (!value) | |||
| 1134 | return false0; | |||
| 1135 | argconfig_root(root, s); | |||
| 1136 | ||||
| 1137 | return given_in(root, value); | |||
| 1138 | } |