summaryrefslogtreecommitdiff
path: root/src/util/option.h
blob: a5a698b0b07b79129545a4b5ab37bfb55202b5a5 (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
#pragma once

#include <stdbool.h>


enum option_spec_kind {
  OPTSPECKIND_SETBOOL,
  OPTSPECKIND_CALL,
  OPTSPECKIND_CALLP,
  OPTSPECKIND_WITHARG,

  OPTSPECKIND_HELPUSAGE,
  OPTSPECKIND_VERSION,

  OPTSPECKIND_END,  // end of spec list (rest of spec entry ignored)
};

// For usage info, see option_parse() below.
struct option_spec {
  char optc;  // short option character
  // TODO: long options?

  // From here, the fields should be filled in using the OPTION_* macros.
  enum option_spec_kind kind;
  union {
    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;
};

// Takes a bool* and stores 'true' there if the flag is found.
#define OPTION_SETBOOL(boolptr) \
  OPTSPECKIND_SETBOOL, {.setbool={.ptr=(boolptr)}}

// Takes a void(*)(void) and calls it every time the flag is found.
#define OPTION_CALL(funptr) \
  OPTSPECKIND_CALL, {.call={.fun=(funptr)}}

// Takes a void(*)(void*) and a 'void *data', and calls the function with 'data'
// every time the flag is found.
#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) \
  OPTSPECKIND_HELPUSAGE, {.helpusage={.usagestr=(usagestring)}}

// Prints version of the tool and exits with code 0.
#define OPTION_VERSION() \
  OPTSPECKIND_VERSION, {.version={}}

// This must be the final entry in the list of option specs; contrary to the
// other OPTION_* macros, it is an _entire_ 'struct option_spec'.
#define OPTION_SPEC_END {'\0', OPTSPECKIND_END, {}}

// The spec must not contain duplicate entries for the same short option
// character.
// Reshuffles positional arguments to the end of the argument list; returns a
// pointer to the first positional argument after reshuffling.
//
// The specs array is most conveniently declared as a C array:
//
//   const struct option_spec[] = {
//     {'h', OPTION_HELPUSAGE("Gebruik: %s <args>\n")},
//     {'V', OPTION_VERSION()},
//     OPTION_SPEC_END
//   }
//
// The individual option specs should be initialised with a brace-initialiser
// containing the short option character followed by one of the OPTION_* macros,
// as shown in the example above. See the comments above the macros for their
// meaning. End the list with OPTION_SPEC_END.
char** option_parse(int argc, char **argv, const struct option_spec *specs);