]>
Commit | Line | Data |
---|---|---|
0c1f3509 MG |
1 | #ifndef _GETOPT_H_ |
2 | #define _GETOPT_H_ | |
3 | ||
4 | #include <setjmp.h> | |
5 | #include <stddef.h> | |
6 | ||
7 | /** | |
8 | * This getopt implementation parses options of the following forms: | |
9 | * -a -b -c foo (single-character options) | |
10 | * -abc foo (packed single-character options) | |
11 | * -abcfoo (packed single-character options and an argument) | |
12 | * --foo bar (long option) | |
13 | * --foo=bar (long option and argument separated by '=') | |
14 | * | |
15 | * It does not support abbreviated options (e.g., interpreting --foo as | |
16 | * --foobar when there are no other --foo* options) since that misfeature | |
17 | * results in breakage when new options are added. It also does not support | |
18 | * options appearing after non-options (e.g., "cp foo bar -R") since that is | |
19 | * a horrible GNU perversion. | |
20 | */ | |
21 | ||
22 | /* Work around LLVM bug. */ | |
23 | #ifdef __clang__ | |
24 | #warning Working around bug in LLVM optimizer | |
25 | #warning For more details see https://llvm.org/bugs/show_bug.cgi?id=27190 | |
26 | #define DO_SETJMP _DO_SETJMP(__LINE__) | |
27 | #define _DO_SETJMP(x) __DO_SETJMP(x) | |
28 | #define __DO_SETJMP(x) \ | |
29 | void * getopt_initloop = && getopt_initloop_ ## x; \ | |
30 | getopt_initloop_ ## x: | |
31 | #define DO_LONGJMP \ | |
32 | goto *getopt_initloop | |
33 | #else | |
34 | #define DO_SETJMP \ | |
35 | sigjmp_buf getopt_initloop; \ | |
36 | if (!getopt_initialized) \ | |
37 | sigsetjmp(getopt_initloop, 0) | |
38 | #define DO_LONGJMP \ | |
39 | siglongjmp(getopt_initloop, 1) | |
40 | #endif | |
41 | ||
42 | /* Avoid namespace collisions with libc getopt. */ | |
43 | #define getopt libcperciva_getopt | |
44 | #define optarg libcperciva_optarg | |
45 | #define optind libcperciva_optind | |
46 | #define opterr libcperciva_opterr | |
47 | #define optreset libcperciva_optreset | |
48 | ||
49 | /* Standard getopt global variables. */ | |
50 | extern const char * optarg; | |
51 | extern int optind, opterr, optreset; | |
52 | ||
53 | /* Dummy option string, equal to "(dummy)". */ | |
54 | #define GETOPT_DUMMY getopt_dummy | |
55 | ||
56 | /** | |
57 | * GETOPT(argc, argv): | |
58 | * When called for the first time (or the first time after optreset is set to | |
59 | * a nonzero value), return GETOPT_DUMMY, aka. "(dummy)". Thereafter, return | |
60 | * the next option string and set optarg / optind appropriately; abort if not | |
61 | * properly initialized when not being called for the first time. | |
62 | */ | |
63 | #define GETOPT(argc, argv) getopt(argc, argv) | |
64 | ||
65 | /** | |
66 | * GETOPT_SWITCH(ch): | |
67 | * Jump to the appropriate GETOPT_OPT, GETOPT_OPTARG, GETOPT_MISSING_ARG, or | |
68 | * GETOPT_DEFAULT based on the option string ${ch}. When called for the first | |
69 | * time, perform magic to index the options. | |
70 | * | |
71 | * GETOPT_SWITCH(ch) is equivalent to "switch (ch)" in a standard getopt loop. | |
72 | */ | |
73 | #define GETOPT_SWITCH(ch) \ | |
74 | volatile size_t getopt_ln_min = __LINE__; \ | |
75 | volatile size_t getopt_ln = getopt_ln_min - 1; \ | |
76 | volatile int getopt_default_missing = 0; \ | |
77 | DO_SETJMP; \ | |
78 | switch (getopt_initialized ? getopt_lookup(ch) + getopt_ln_min : getopt_ln++) | |
79 | ||
80 | /** | |
81 | * GETOPT_OPT(os): | |
82 | * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH. | |
83 | * | |
84 | * GETOPT_OPT("-x") is equivalent to "case 'x'" in a standard getopt loop | |
85 | * which has an optstring containing "x". | |
86 | */ | |
87 | #define GETOPT_OPT(os) _GETOPT_OPT(os, __LINE__) | |
88 | #define _GETOPT_OPT(os, ln) __GETOPT_OPT(os, ln) | |
89 | #define __GETOPT_OPT(os, ln) \ | |
90 | case ln: \ | |
91 | if (getopt_initialized) \ | |
92 | goto getopt_skip_ ## ln; \ | |
93 | getopt_register_opt(os, ln - getopt_ln_min, 0); \ | |
94 | DO_LONGJMP; \ | |
95 | getopt_skip_ ## ln | |
96 | ||
97 | /** | |
98 | * GETOPT_OPTARG(os): | |
99 | * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH, | |
100 | * unless no argument is available, in which case jump to GETOPT_MISSING_ARG | |
101 | * (if present) or GETOPT_DEFAULT (if not). | |
102 | * | |
103 | * GETOPT_OPTARG("-x") is equivalent to "case 'x'" in a standard getopt loop | |
104 | * which has an optstring containing "x:". | |
105 | */ | |
106 | #define GETOPT_OPTARG(os) _GETOPT_OPTARG(os, __LINE__) | |
107 | #define _GETOPT_OPTARG(os, ln) __GETOPT_OPTARG(os, ln) | |
108 | #define __GETOPT_OPTARG(os, ln) \ | |
109 | case ln: \ | |
110 | if (getopt_initialized) \ | |
111 | goto getopt_skip_ ## ln; \ | |
112 | getopt_register_opt(os, ln - getopt_ln_min, 1); \ | |
113 | DO_LONGJMP; \ | |
114 | getopt_skip_ ## ln | |
115 | ||
116 | /** | |
117 | * GETOPT_MISSING_ARG: | |
118 | * Jump to this point if an option string specified in GETOPT_OPTARG is seen | |
119 | * but no argument is available. | |
120 | * | |
121 | * GETOPT_MISSING_ARG is equivalent to "case ':'" in a standard getopt loop | |
122 | * which has an optstring starting with ":". As such, it also has the effect | |
123 | * of disabling warnings about invalid options, as if opterr had been zeroed. | |
124 | */ | |
125 | #define GETOPT_MISSING_ARG _GETOPT_MISSING_ARG(__LINE__) | |
126 | #define _GETOPT_MISSING_ARG(ln) __GETOPT_MISSING_ARG(ln) | |
127 | #define __GETOPT_MISSING_ARG(ln) \ | |
128 | case ln: \ | |
129 | if (getopt_initialized) \ | |
130 | goto getopt_skip_ ## ln; \ | |
131 | getopt_register_missing(ln - getopt_ln_min); \ | |
132 | DO_LONGJMP; \ | |
133 | getopt_skip_ ## ln | |
134 | ||
135 | /** | |
136 | * GETOPT_DEFAULT: | |
137 | * Jump to this point if an unrecognized option is seen or if an option | |
138 | * specified in GETOPT_OPTARG is seen, no argument is available, and there is | |
139 | * no GETOPT_MISSING_ARG label. | |
140 | * | |
141 | * GETOPT_DEFAULT is equivalent to "case '?'" in a standard getopt loop. | |
142 | * | |
143 | * NOTE: This MUST be present in the GETOPT_SWITCH statement, and MUST occur | |
144 | * after all other GETOPT_* labels. | |
145 | */ | |
146 | #define GETOPT_DEFAULT _GETOPT_DEFAULT(__LINE__) | |
147 | #define _GETOPT_DEFAULT(ln) __GETOPT_DEFAULT(ln) | |
148 | #define __GETOPT_DEFAULT(ln) \ | |
149 | goto getopt_skip_ ## ln; \ | |
150 | case ln: \ | |
151 | getopt_initialized = 1; \ | |
152 | break; \ | |
153 | default: \ | |
154 | if (getopt_initialized) \ | |
155 | goto getopt_skip_ ## ln; \ | |
156 | if (!getopt_default_missing) { \ | |
157 | getopt_setrange(ln - getopt_ln_min); \ | |
158 | getopt_default_missing = 1; \ | |
159 | } \ | |
160 | DO_LONGJMP; \ | |
161 | getopt_skip_ ## ln | |
162 | ||
163 | /* | |
164 | * The back-end implementation. These should be considered internal | |
165 | * interfaces and not used directly. | |
166 | */ | |
167 | const char * getopt(int, char * const []); | |
168 | size_t getopt_lookup(const char *); | |
169 | void getopt_register_opt(const char *, size_t, int); | |
170 | void getopt_register_missing(size_t); | |
171 | void getopt_setrange(size_t); | |
172 | extern const char * getopt_dummy; | |
173 | extern int getopt_initialized; | |
174 | ||
175 | #endif /* !_GETOPT_H_ */ |