#include #include #include #include #include "option.h" #include "util/versie.h" #include "util/debug.h" /* static void debug_argv(int argc, char **argv) { fprintf(stderr, "argv: <"); for (int i = 0; i < argc; i++) { if (i != 0) fputc(',', stderr); fprintf(stderr, "'%s'", argv[i]); } fprintf(stderr, ">\n"); } */ char** option_parse(int argc, char **argv, const struct option_spec *speclist) { // fill table of specs const struct option_spec *spectable[256] = {0}; for (const struct option_spec *spec = speclist; spec->kind != OPTSPECKIND_END; spec++) { if (spectable[(uint8_t)spec->optc] != NULL) { fprintf(stderr, "option_parse: dubbele specs\n"); 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) { fprintf(stderr, "%s: Ongeldige optie: -%c\n", argv[0], argv[opti][j]); exit(1); } switch (spec->kind) { case OPTSPECKIND_SETBOOL: *spec->spec_sub.setbool.ptr = true; break; case OPTSPECKIND_CALL: spec->spec_sub.call.fun(); break; case OPTSPECKIND_CALLP: 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); case OPTSPECKIND_VERSION: drukkedoos_print_versie(stdout); exit(0); 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); // debug_argv(argc, argv); // collect positional arguments that need to be reshuffled char *posargs[npos_before_opts]; for (int i = 1, posi = 0; i < last_option_index; 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++) { if (is_option[readi]) { if (readi > noptargs + 1) argv[noptargs + 1] = argv[readi]; noptargs++; } } // debug_argv(argc, argv); // write positional arguments after the option arguments memcpy(argv + 1 + noptargs, posargs, npos_before_opts * sizeof(char*)); // debug_argv(argc, argv); free(is_option); return argv + 1 + noptargs; }