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