| 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_ */ |