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