summaryrefslogtreecommitdiff
path: root/src/util/option.c
blob: 5efcbd72717419926b8f5774cca5698c3f4a7b05 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#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;
  }

  // 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++) {
    if (argv[opti][0] != '-') continue;  // skip non-options

    // 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 '--'

    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_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
      }
    }
  }

  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++) {
    // 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];
  }

  // 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 (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);

  return argv + 1 + noptargs;
}