summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom@tomsmeding.com>2024-09-08 22:02:02 +0200
committerTom Smeding <tom@tomsmeding.com>2024-09-08 22:02:21 +0200
commitd244e4e3cb702dcb87f1c3c1232abd2fc936d8ec (patch)
tree6152a76d96e321b52548aa01fd5d3445f2ecec12
parentf11bd88153ca6ab57d9ebb58807f8771cbd40924 (diff)
OPTION_WITHARG
-rw-r--r--src/util/option.c51
-rw-r--r--src/util/option.h8
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) \