From d244e4e3cb702dcb87f1c3c1232abd2fc936d8ec Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Sun, 8 Sep 2024 22:02:02 +0200 Subject: OPTION_WITHARG --- src/util/option.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- src/util/option.h | 8 ++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/util/option.c b/src/util/option.c index 5efcbd7..2ad20d1 100644 --- a/src/util/option.c +++ b/src/util/option.c @@ -25,21 +25,34 @@ char** option_parse(int argc, char **argv, const struct option_spec *speclist) { abort(); } spectable[(uint8_t)spec->optc] = spec; + + if (spec->kind == OPTSPECKIND_WITHARG) { + *spec->spec_sub.witharg.dst = NULL; + } } + // option, option argument, or '--'. + bool *is_option = calloc(argc, 1); + // parse options int last_option_index = 0; // index of last option argument, treating '--' as option arg int npos_before_opts = 0; // number of positional arguments before last_option_index int opti; for (opti = 1; opti < argc; opti++) { + DEBUG("opti = %d\n", opti); + if (argv[opti][0] != '-') continue; // skip non-options + is_option[opti] = true; + // count an option argument even if it's '--' npos_before_opts += opti - (last_option_index + 1); last_option_index = opti; if (strcmp(argv[opti], "--") == 0) break; // but don't try to parse '--' + bool skip_next_argument = false; + for (int j = 1; argv[opti][j]; j++) { const struct option_spec *spec = spectable[(uint8_t)argv[opti][j]]; if (!spec) { @@ -60,6 +73,27 @@ char** option_parse(int argc, char **argv, const struct option_spec *speclist) { spec->spec_sub.callp.fun(spec->spec_sub.callp.data); break; + case OPTSPECKIND_WITHARG: + if (argv[opti][j+1] != '\0') { + fprintf(stderr, "%s: Optie -%c heeft een argument nodig, maar is niet de laatste op rij\n", argv[0], argv[opti][j]); + exit(1); + } + + if (opti + 1 < argc) { + char **dst = spec->spec_sub.witharg.dst; + if (*dst) free(*dst); // override any previous value + + const size_t arglen = strlen(argv[opti+1]); + *dst = malloc(arglen + 1); + memcpy(*dst, argv[opti+1], arglen + 1); + + skip_next_argument = true; + } else { + fprintf(stderr, "%s: Optie -%c heeft een argument nodig\n", argv[0], argv[opti][j]); + exit(1); + } + break; + case OPTSPECKIND_HELPUSAGE: printf(spec->spec_sub.helpusage.usagestr, argv[0]); exit(0); @@ -71,6 +105,12 @@ char** option_parse(int argc, char **argv, const struct option_spec *speclist) { case OPTSPECKIND_END: abort(); // unreachable } } + + if (skip_next_argument) { + // skip next argument, but mark it as an option + last_option_index = ++opti; + is_option[opti] = true; + } } DEBUG("last_option_index=%d npos_before_opts=%d\n", last_option_index, npos_before_opts); @@ -80,18 +120,13 @@ char** option_parse(int argc, char **argv, const struct option_spec *speclist) { // collect positional arguments that need to be reshuffled char *posargs[npos_before_opts]; for (int i = 1, posi = 0; i < last_option_index; i++) { - // don't need to check for '--' here because '--' is an option argument and - // this loop doesn't go beyond it - if (argv[i][0] == '-') continue; - - posargs[posi++] = argv[i]; + if (!is_option[i]) posargs[posi++] = argv[i]; } // move option arguments to the beginning int noptargs = 0; for (int readi = 1; readi <= last_option_index; readi++) { - // no need to check for '--' here - if (argv[readi][0] == '-') { + if (is_option[readi]) { if (readi > noptargs + 1) argv[noptargs + 1] = argv[readi]; noptargs++; } @@ -104,5 +139,7 @@ char** option_parse(int argc, char **argv, const struct option_spec *speclist) { // debug_argv(argc, argv); + free(is_option); + return argv + 1 + noptargs; } diff --git a/src/util/option.h b/src/util/option.h index 780a6e9..a5a698b 100644 --- a/src/util/option.h +++ b/src/util/option.h @@ -7,6 +7,7 @@ enum option_spec_kind { OPTSPECKIND_SETBOOL, OPTSPECKIND_CALL, OPTSPECKIND_CALLP, + OPTSPECKIND_WITHARG, OPTSPECKIND_HELPUSAGE, OPTSPECKIND_VERSION, @@ -25,6 +26,7 @@ struct option_spec { struct { bool *ptr; } setbool; struct { void (*fun)(void); } call; struct { void (*fun)(void*); void *data; } callp; + struct { char **dst; } witharg; struct { const char *usagestr; } helpusage; struct {} version; } spec_sub; @@ -43,6 +45,12 @@ struct option_spec { #define OPTION_CALLP(funptr, dataptr) \ OPTSPECKIND_CALLP, {.callp={.fun=(funptr), .data=(dataptr)}} +// An option with an argument. Takes a char** and stores a newly malloc()ed +// string in that pointer for the given argument. Later occurrences override any +// previous ones, and NULL is written if the option never occurs. +#define OPTION_WITHARG(argptr) \ + OPTSPECKIND_WITHARG, {.witharg={.dst=(argptr)}} + // Takes a printf format string with a single %s, which is substituted (by // printf) for argv[0]. Prints the formatted string and exits with code 0. #define OPTION_HELPUSAGE(usagestring) \ -- cgit v1.2.3-70-g09d2