1 /* vi: set sw=4 ts=4: */
2 /*
3  * universal getopt32 implementation for busybox
4  *
5  * Copyright (C) 2003-2005  Vladimir Oleynik  <dzo@simtreas.ru>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9 #if ENABLE_LONG_OPTS
10 # include <getopt.h>
11 #endif
12 #include "libbb.h"
13 
14 //kbuild:lib-y += getopt32.o
15 
16 /*      Documentation
17 
18 uint32_t
19 getopt32(char **argv, const char *applet_opts, ...)
20 
21         The command line options are passed as the applet_opts string.
22 
23         If one of the given options is found, a flag value is added to
24         the return value.
25 
26         The flag value is determined by the position of the char in
27         applet_opts string.  For example:
28 
29         flags = getopt32(argv, "rnug");
30 
31         "r" will set 1    (bit 0)
32         "n" will set 2    (bit 1)
33         "u" will set 4    (bit 2)
34         "g" will set 8    (bit 3)
35 
36         and so on.  You can also look at the return value as a bit
37         field and each option sets one bit.
38 
39         On exit, global variable optind is set so that if you
40         will do argc -= optind; argv += optind; then
41         argc will be equal to number of remaining non-option
42         arguments, first one would be in argv[0], next in argv[1] and so on
43         (options and their parameters will be moved into argv[]
44         positions prior to argv[optind]).
45 
46  "o:"   If one of the options requires an argument, then add a ":"
47         after the char in applet_opts and provide a pointer to store
48         the argument.  For example:
49 
50         char *pointer_to_arg_for_a;
51         char *pointer_to_arg_for_b;
52         char *pointer_to_arg_for_c;
53         char *pointer_to_arg_for_d;
54 
55         flags = getopt32(argv, "a:b:c:d:",
56                         &pointer_to_arg_for_a, &pointer_to_arg_for_b,
57                         &pointer_to_arg_for_c, &pointer_to_arg_for_d);
58 
59         The type of the pointer may be controlled by "o::" or "o+" in
60         the external string opt_complementary (see below for more info).
61 
62  "o::"  If option can have an *optional* argument, then add a "::"
63         after its char in applet_opts and provide a pointer to store
64         the argument.  Note that optional arguments _must_
65         immediately follow the option: -oparam, not -o param.
66 
67  "o:+"  This means that the parameter for this option is a nonnegative integer.
68         It will be processed with xatoi_positive() - allowed range
69         is 0..INT_MAX.
70 
71         int param;  // "unsigned param;" will also work
72         getopt32(argv, "p:+", &param);
73 
74  "o:*"  This means that the option can occur multiple times. Each occurrence
75         will be saved as a llist_t element instead of char*.
76 
77         For example:
78         The grep applet can have one or more "-e pattern" arguments.
79         In this case you should use getopt32() as follows:
80 
81         llist_t *patterns = NULL;
82 
83         (this pointer must be initializated to NULL if the list is empty
84         as required by llist_add_to_end(llist_t **old_head, char *new_item).)
85 
86         getopt32(argv, "e:*", &patterns);
87 
88         $ grep -e user -e root /etc/passwd
89         root:x:0:0:root:/root:/bin/bash
90         user:x:500:500::/home/user:/bin/bash
91 
92  "^"    options string is "^optchars""\0""opt_complementary".
93 
94  "!"    If the first character in the applet_opts string is a '!',
95         report bad options, missing required options,
96         inconsistent options with all-ones return value (instead of abort.
97 
98  "+"    If the first character in the applet_opts string is a plus,
99         then option processing will stop as soon as a non-option is
100         encountered in the argv array.  Useful for applets like env
101         which should not process arguments to subprograms:
102         env -i ls -d /
103         Here we want env to process just the '-i', not the '-d'.
104 
105         (The order of multiple prefixes must be "^!+...")
106 
107 uint32_t
108 getopt32long(char **argv, const char *applet_opts, const char *logopts...)
109 
110         This allows you to define long options:
111 
112         static const char applet_longopts[] ALIGN1 =
113                 //"name\0"  has_arg     val
114                 "verbose\0" No_argument "v"
115                 ;
116         opt = getopt32long(argv, applet_opts, applet_longopts, ...);
117 
118         The last element (val) typically is set to
119         matching short option from applet_opts. If there is no matching
120         char in applet_opts, then:
121         - return bit has next position after short options
122         - if has_arg is not "No_argument", use ptr for arg also
123         - opt_complementary affects it too
124 
125         Note: a good applet will make long options configurable via the
126         config process and not a required feature.  The current standard
127         is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
128 
129 opt_complementary - option modifiers.
130 
131  ":"    The colon (":") is used to separate groups of two or more chars
132         and/or groups of chars and special characters (stating some
133         conditions to be checked).
134 
135  "abc"  If groups of two or more chars are specified, the first char
136         is the main option and the other chars are secondary options.
137         Their flags will be turned on if the main option is found even
138         if they are not specified on the command line.  For example:
139 
140         flags = getopt32(argv, "^abcd""\0""abc")
141 
142         If getopt() finds "-a" on the command line, then
143         getopt32's return value will be as if "-a -b -c" were
144         found.
145 
146  "ww"   Adjacent double options have a counter associated which indicates
147         the number of occurrences of the option.
148         For example the ps applet needs:
149         if w is given once, GNU ps sets the width to 132,
150         if w is given more than once, it is "unlimited"
151 
152         int w_counter = 0; // must be initialized!
153         getopt32(argv, "^w""\0""ww", &w_counter);
154         if (w_counter)
155                 width = (w_counter == 1) ? 132 : INT_MAX;
156         else
157                 get_terminal_width(...&width...);
158 
159         w_counter is a pointer to an integer. It has to be passed to
160         getopt32() after all other option argument sinks.
161 
162         For example: accept multiple -v to indicate the level of verbosity
163         and for each -b optarg, add optarg to my_b. Finally, if b is given,
164         turn off c and vice versa:
165 
166         llist_t *my_b = NULL;
167         int verbose_level = 0;
168         f = getopt32(argv, "^vb:*c"
169 			"\0""vv:b-c:c-b"
170 			, &my_b, &verbose_level);
171         if (f & 2)       // -c after -b unsets -b flag
172                 while (my_b) dosomething_with(llist_pop(&my_b));
173         if (my_b)        // but llist is stored if -b is specified
174                 free_llist(my_b);
175         if (verbose_level) printf("verbose level is %d\n", verbose_level);
176 
177 Special characters:
178 
179  "-N"   A dash as the first char in a opt_complementary group followed
180         by a single digit (0-9) means that at least N non-option
181         arguments must be present on the command line
182 
183  "=N"   An equal sign as the first char in a opt_complementary group followed
184         by a single digit (0-9) means that exactly N non-option
185         arguments must be present on the command line
186 
187  "?N"   A "?" as the first char in a opt_complementary group followed
188         by a single digit (0-9) means that at most N arguments must be present
189         on the command line.
190 
191  "V-"   An option with dash before colon or end-of-line results in
192         bb_show_usage() being called if this option is encountered.
193         This is typically used to implement "print verbose usage message
194         and exit" option.
195 
196  "a-b"  A dash between two options causes the second of the two
197         to be unset (and ignored) if it is given on the command line.
198 
199         [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
200 
201         For example:
202         The du applet has the options "-s" and "-d depth".  If
203         getopt32 finds -s, then -d is unset or if it finds -d
204         then -s is unset.  (Note:  busybox implements the GNU
205         "--max-depth" option as "-d".)  To obtain this behavior, you
206         set opt_complementary to "s-d:d-s".  Only one flag value is
207         added to getopt32's return value depending on the
208         position of the options on the command line.  If one of the
209         two options requires an argument pointer (":" in applet_opts
210         as in "d:") optarg is set accordingly.
211 
212         char *smax_print_depth;
213 
214         opt = getopt32(argv, "^sd:x""\0""s-d:d-s:x-x", &smax_print_depth);
215 
216         if (opt & 2)
217                 max_print_depth = atoi(smax_print_depth);
218         if (opt & 4)
219                 printf("Detected odd -x usage\n");
220 
221  "a--b" A double dash between two options, or between an option and a group
222         of options, means that they are mutually exclusive.  Unlike
223         the "-" case above, an error will be forced if the options
224         are used together.
225 
226         For example:
227         The cut applet must have only one type of list specified, so
228         -b, -c and -f are mutually exclusive and should raise an error
229         if specified together.  In this case you must set
230         opt_complementary to "b--cf:c--bf:f--bc".  If two of the
231         mutually exclusive options are found, getopt32 will call
232         bb_show_usage() and die.
233 
234  "x--x" Variation of the above, it means that -x option should occur
235         at most once.
236 
237  "o+"   A plus after a char in opt_complementary means that the parameter
238         for this option is a nonnegative integer. It will be processed
239         with xatoi_positive() - allowed range is 0..INT_MAX.
240 
241         int param;  // "unsigned param;" will also work
242         getopt32(argv, "^p:""\0""p+", &param);
243 
244  "o::"  A double colon after a char in opt_complementary means that the
245         option can occur multiple times. Each occurrence will be saved as
246         a llist_t element instead of char*.
247 
248         For example:
249         The grep applet can have one or more "-e pattern" arguments.
250         In this case you should use getopt32() as follows:
251 
252         llist_t *patterns = NULL;
253 
254         (this pointer must be initializated to NULL if the list is empty
255         as required by llist_add_to_end(llist_t **old_head, char *new_item).)
256 
257         getopt32(argv, "^e:""\0""e::", &patterns);
258 
259         $ grep -e user -e root /etc/passwd
260         root:x:0:0:root:/root:/bin/bash
261         user:x:500:500::/home/user:/bin/bash
262 
263         "o+" and "o::" can be handled by "o:+" and "o:*" specifiers
264         in option string (and it is preferred), but this does not work
265         for "long options only" cases, such as tar --exclude=PATTERN,
266         wget --header=HDR cases.
267 
268  "a?b"  A "?" between an option and a group of options means that
269         at least one of them is required to occur if the first option
270         occurs in preceding command line arguments.
271 
272         For example from "id" applet:
273 
274         // Don't allow -n -r -rn -ug -rug -nug -rnug
275         flags = getopt32(argv, "^rnug""\0""r?ug:n?ug:u--g:g--u");
276 
277         This example allowed only:
278         $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
279 
280  "X"    A opt_complementary group with just a single letter means
281         that this option is required. If more than one such group exists,
282         at least one option is required to occur (not all of them).
283         For example from "start-stop-daemon" applet:
284 
285         // Don't allow -KS -SK, but -S or -K is required
286         flags = getopt32(argv, "^KS...""\0""K:S:K--S:S--K");
287 
288 
289         Don't forget to use ':'. For example, "?322-22-23X-x-a"
290         is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" -
291         max 3 args; count uses of '-2'; min 2 args; if there is
292         a '-2' option then unset '-3', '-X' and '-a'; if there is
293         a '-2' and after it a '-x' then error out.
294         But it's far too obfuscated. Use ':' to separate groups.
295 */
296 
297 /* Code here assumes that 'unsigned' is at least 32 bits wide */
298 
299 const char *const bb_argv_dash[] = { "-", NULL };
300 
301 enum {
302 	PARAM_STRING,
303 	PARAM_LIST,
304 	PARAM_INT,
305 };
306 
307 typedef struct {
308 	unsigned char opt_char;
309 	smallint param_type;
310 	unsigned switch_on;
311 	unsigned switch_off;
312 	unsigned incongruously;
313 	unsigned requires;
314 	void **optarg;  /* char**, llist_t** or int *. */
315 	int *counter;
316 } t_complementary;
317 
318 uint32_t option_mask32;
319 
320 #if ENABLE_LONG_OPTS
321 static const struct option bb_null_long_options[1] = {
322 	{ 0, 0, 0, 0 }
323 };
324 #else
325 #define vgetopt32(argv,applet_opts,applet_long_options,p) \
326 	vgetopt32(argv,applet_opts,p)
327 #endif
328 
329 /* Please keep getopt32 free from xmalloc */
330 
331 static uint32_t
vgetopt32(char ** argv,const char * applet_opts,const char * applet_long_options,va_list p)332 vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, va_list p)
333 {
334 	int argc;
335 	unsigned flags = 0;
336 	unsigned requires = 0;
337 	unsigned len;
338 	t_complementary complementary[33]; /* last stays zero-filled */
339 	char dont_die_flag;
340 	int c;
341 	const unsigned char *s;
342 	const char *opt_complementary;
343 	t_complementary *on_off;
344 #if ENABLE_LONG_OPTS
345 	const struct option *l_o;
346 	struct option *long_options = (struct option *) &bb_null_long_options;
347 #endif
348 	unsigned trigger;
349 	int min_arg = 0;
350 	int max_arg = -1;
351 	int spec_flgs = 0;
352 
353 #define SHOW_USAGE_IF_ERROR     1
354 
355 	on_off = complementary;
356 	memset(on_off, 0, sizeof(complementary));
357 
358 	len = strlen(applet_opts);
359 
360 	/* skip bbox extension */
361 	opt_complementary = NULL;
362 	if (applet_opts[0] == '^') {
363 		applet_opts++;
364 		/* point it past terminating NUL */
365 		opt_complementary = applet_opts + len;
366 	}
367 
368 	/* skip another bbox extension */
369 	dont_die_flag = applet_opts[0];
370 	if (dont_die_flag == '!')
371 		applet_opts++;
372 
373 	applet_opts = strcpy(alloca(len + 1), applet_opts);
374 
375 	/* skip GNU extension */
376 	s = (const unsigned char *)applet_opts;
377 	if (*s == '+' || *s == '-')
378 		s++;
379 	c = 0;
380 	while (*s) {
381 		if (c >= 32)
382 			break;
383 		on_off->opt_char = *s;
384 		on_off->switch_on = (1U << c);
385 		if (*++s == ':') {
386 			on_off->optarg = va_arg(p, void **);
387 			if (s[1] == '+' || s[1] == '*') {
388 				/* 'o:+' or 'o:*' */
389 				on_off->param_type = (s[1] == '+') ?
390 					PARAM_INT : PARAM_LIST;
391 				overlapping_strcpy((char*)s + 1, (char*)s + 2);
392 			}
393 			/* skip possible 'o::' (or 'o:+:' !) */
394 			while (*++s == ':')
395 				continue;
396 		}
397 		on_off++;
398 		c++;
399 	}
400 
401 #if ENABLE_LONG_OPTS
402 	if (applet_long_options) {
403 		const char *optstr;
404 		unsigned i, count;
405 
406 		count = 1;
407 		optstr = applet_long_options;
408 		while (optstr[0]) {
409 			optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */
410 			count++;
411 		}
412 		/* count == no. of longopts + 1 */
413 		long_options = alloca(count * sizeof(*long_options));
414 		memset(long_options, 0, count * sizeof(*long_options));
415 		i = 0;
416 		optstr = applet_long_options;
417 		while (--count) {
418 			long_options[i].name = optstr;
419 			optstr += strlen(optstr) + 1;
420 			long_options[i].has_arg = (unsigned char)(*optstr++);
421 			/* long_options[i].flag = NULL; */
422 			long_options[i].val = (unsigned char)(*optstr++);
423 			i++;
424 		}
425 		for (l_o = long_options; l_o->name; l_o++) {
426 			if (l_o->flag)
427 				continue;
428 			for (on_off = complementary; on_off->opt_char; on_off++)
429 				if (on_off->opt_char == l_o->val)
430 					goto next_long;
431 			if (c >= 32)
432 				break;
433 			on_off->opt_char = l_o->val;
434 			on_off->switch_on = (1U << c);
435 			if (l_o->has_arg != no_argument)
436 				on_off->optarg = va_arg(p, void **);
437 			c++;
438  next_long: ;
439 		}
440 	}
441 #endif /* ENABLE_LONG_OPTS */
442 
443 	s = (const unsigned char *)opt_complementary;
444 	if (s) for (; *s; s++) {
445 		t_complementary *pair;
446 		unsigned *pair_switch;
447 
448 		if (*s == ':')
449 			continue;
450 		c = s[1];
451 		if (*s == '?') {
452 			if (c < '0' || c > '9') {
453 				spec_flgs |= SHOW_USAGE_IF_ERROR;
454 			} else {
455 				max_arg = c - '0';
456 				s++;
457 			}
458 			continue;
459 		}
460 		if (*s == '-') {
461 			if (c >= '0' && c <= '9') {
462 				min_arg = c - '0';
463 				s++;
464 			}
465 			continue;
466 		}
467 		if (*s == '=') {
468 			min_arg = max_arg = c - '0';
469 			s++;
470 			continue;
471 		}
472 		for (on_off = complementary; on_off->opt_char; on_off++)
473 			if (on_off->opt_char == *s)
474 				goto found_opt;
475 		/* Without this, diagnostic of such bugs is not easy */
476 		bb_error_msg_and_die("NO OPT %c!", *s);
477  found_opt:
478 		if (c == ':' && s[2] == ':') {
479 			on_off->param_type = PARAM_LIST;
480 			continue;
481 		}
482 		if (c == '+' && (s[2] == ':' || s[2] == '\0')) {
483 			on_off->param_type = PARAM_INT;
484 			s++;
485 			continue;
486 		}
487 		if (c == ':' || c == '\0') {
488 			requires |= on_off->switch_on;
489 			continue;
490 		}
491 		if (c == '-' && (s[2] == ':' || s[2] == '\0')) {
492 			flags |= on_off->switch_on;
493 			on_off->incongruously |= on_off->switch_on;
494 			s++;
495 			continue;
496 		}
497 		if (c == *s) {
498 			on_off->counter = va_arg(p, int *);
499 			s++;
500 		}
501 		pair = on_off;
502 		pair_switch = &pair->switch_on;
503 		for (s++; *s && *s != ':'; s++) {
504 			if (*s == '?') {
505 				pair_switch = &pair->requires;
506 			} else if (*s == '-') {
507 				if (pair_switch == &pair->switch_off)
508 					pair_switch = &pair->incongruously;
509 				else
510 					pair_switch = &pair->switch_off;
511 			} else {
512 				for (on_off = complementary; on_off->opt_char; on_off++)
513 					if (on_off->opt_char == *s) {
514 						*pair_switch |= on_off->switch_on;
515 						break;
516 					}
517 			}
518 		}
519 		s--;
520 	}
521 
522 	/* In case getopt32 was already called:
523 	 * reset libc getopt() internal state.
524 	 * run_nofork_applet() does this, but we might end up here
525 	 * also via gunzip_main() -> gzip_main(). Play safe.
526 	 */
527 	GETOPT_RESET();
528 
529 	/* skip 0: some applets cheat: they do not actually HAVE argv[0] */
530 	argc = 1 + string_array_len(argv + 1);
531 
532 	/* Note: just "getopt() <= 0" will not work well for
533 	 * "fake" short options, like this one:
534 	 * wget $'-\203' "Test: test" http://kernel.org/
535 	 * (supposed to act as --header, but doesn't) */
536 #if ENABLE_LONG_OPTS
537 	while ((c = getopt_long(argc, argv, applet_opts,
538 			long_options, NULL)) != -1) {
539 #else
540 	while ((c = getopt(argc, argv, applet_opts)) != -1) {
541 #endif
542 		/* getopt prints "option requires an argument -- X"
543 		 * and returns '?' if an option has no arg, but one is reqd */
544 		c &= 0xff; /* fight libc's sign extension */
545 		for (on_off = complementary; on_off->opt_char != c; on_off++) {
546 			/* c can be NUL if long opt has non-NULL ->flag,
547 			 * but we construct long opts so that flag
548 			 * is always NULL (see above) */
549 			if (on_off->opt_char == '\0' /* && c != '\0' */) {
550 				/* c is probably '?' - "bad option" */
551 				goto error;
552 			}
553 		}
554 		if (flags & on_off->incongruously)
555 			goto error;
556 		trigger = on_off->switch_on & on_off->switch_off;
557 		flags &= ~(on_off->switch_off ^ trigger);
558 		flags |= on_off->switch_on ^ trigger;
559 		flags ^= trigger;
560 		if (on_off->counter)
561 			(*(on_off->counter))++;
562 		if (optarg) {
563 			if (on_off->param_type == PARAM_LIST) {
564 				llist_add_to_end((llist_t **)(on_off->optarg), optarg);
565 			} else if (on_off->param_type == PARAM_INT) {
566 //TODO: xatoi_positive indirectly pulls in printf machinery
567 				*(unsigned*)(on_off->optarg) = xatoi_positive(optarg);
568 			} else if (on_off->optarg) {
569 				*(char **)(on_off->optarg) = optarg;
570 			}
571 		}
572 	}
573 
574 	/* check depending requires for given options */
575 	for (on_off = complementary; on_off->opt_char; on_off++) {
576 		if (on_off->requires
577 		 && (flags & on_off->switch_on)
578 		 && (flags & on_off->requires) == 0
579 		) {
580 			goto error;
581 		}
582 	}
583 	if (requires && (flags & requires) == 0)
584 		goto error;
585 	argc -= optind;
586 	if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
587 		goto error;
588 
589 	option_mask32 = flags;
590 	return flags;
591 
592  error:
593 	if (dont_die_flag != '!')
594 		bb_show_usage();
595 	return (int32_t)-1;
596 }
597 
598 uint32_t FAST_FUNC
599 getopt32(char **argv, const char *applet_opts, ...)
600 {
601 	uint32_t opt;
602 	va_list p;
603 
604 	va_start(p, applet_opts);
605 	opt = vgetopt32(argv, applet_opts, NULL, p);
606 	va_end(p);
607 	return opt;
608 }
609 
610 #if ENABLE_LONG_OPTS
611 uint32_t FAST_FUNC
612 getopt32long(char **argv, const char *applet_opts, const char *longopts, ...)
613 {
614 	uint32_t opt;
615 	va_list p;
616 
617 	va_start(p, longopts);
618 	opt = vgetopt32(argv, applet_opts, longopts, p);
619 	va_end(p);
620 	return opt;
621 }
622 #endif
623