diff options
| -rw-r--r-- | src/util/option.c | 51 | ||||
| -rw-r--r-- | 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) \ | 
