diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2006-10-03 21:00:06 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2006-10-03 21:00:06 +0000 |
commit | 67b23e6043d8e2b30b0bf3bc105b8583c2a26db5 (patch) | |
tree | edb58560b444979051b42ab7f0c0c718f7459754 /libbb/getopt32.c | |
parent | 40920825d59874cf285390434486e88c8498d2d8 (diff) | |
download | busybox-w32-67b23e6043d8e2b30b0bf3bc105b8583c2a26db5.tar.gz busybox-w32-67b23e6043d8e2b30b0bf3bc105b8583c2a26db5.tar.bz2 busybox-w32-67b23e6043d8e2b30b0bf3bc105b8583c2a26db5.zip |
getopt_ulflags -> getopt32.
It is impossible to formulate sane ABI based on
size of ulong because it can be 32-bit or 64-bit.
Basically it means that you cannot portably use
more that 32 option chars in one call anyway...
Make it explicit.
Diffstat (limited to 'libbb/getopt32.c')
-rw-r--r-- | libbb/getopt32.c | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/libbb/getopt32.c b/libbb/getopt32.c new file mode 100644 index 000000000..e08496578 --- /dev/null +++ b/libbb/getopt32.c | |||
@@ -0,0 +1,516 @@ | |||
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 tarball for details. | ||
8 | */ | ||
9 | |||
10 | #include "libbb.h" | ||
11 | #include <getopt.h> | ||
12 | |||
13 | /* Documentation | ||
14 | |||
15 | uint32_t | ||
16 | getopt32(int argc, char **argv, const char *applet_opts, ...) | ||
17 | |||
18 | The command line options must be declared in const char | ||
19 | *applet_opts as a string of chars, for example: | ||
20 | |||
21 | flags = getopt32(argc, argv, "rnug"); | ||
22 | |||
23 | If one of the given options is found, a flag value is added to | ||
24 | the return value (an unsigned long). | ||
25 | |||
26 | The flag value is determined by the position of the char in | ||
27 | applet_opts string. For example, in the above case: | ||
28 | |||
29 | flags = getopt32(argc, argv, "rnug"); | ||
30 | |||
31 | "r" will add 1 (bit 0) | ||
32 | "n" will add 2 (bit 1) | ||
33 | "u will add 4 (bit 2) | ||
34 | "g" will add 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 | ":" 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(argc, 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 (char* or llist_t*) may be controlled | ||
60 | by the "::" special separator that is set in the external string | ||
61 | opt_complementary (see below for more info). | ||
62 | |||
63 | "::" If option can have an *optional* argument, then add a "::" | ||
64 | after its char in applet_opts and provide a pointer to store | ||
65 | the argument. Note that optional arguments _must_ | ||
66 | immediately follow the option: -oparam, not -o param. | ||
67 | |||
68 | "+" If the first character in the applet_opts string is a plus, | ||
69 | then option processing will stop as soon as a non-option is | ||
70 | encountered in the argv array. Useful for applets like env | ||
71 | which should not process arguments to subprograms: | ||
72 | env -i ls -d / | ||
73 | Here we want env to process just the '-i', not the '-d'. | ||
74 | |||
75 | const struct option *applet_long_options | ||
76 | |||
77 | This struct allows you to define long options. The syntax for | ||
78 | declaring the array is just like that of getopt's longopts. | ||
79 | (see getopt(3)) | ||
80 | |||
81 | static const struct option applet_long_options[] = { | ||
82 | //name,has_arg,flag,val | ||
83 | { "verbose", 0, 0, 'v' }, | ||
84 | { 0, 0, 0, 0 } | ||
85 | }; | ||
86 | applet_long_options = applet_long_options; | ||
87 | |||
88 | The last member of struct option (val) typically is set to | ||
89 | matching short option from applet_opts. If there is no matching | ||
90 | char in applet_opts, then: | ||
91 | - return bit have next position after short options | ||
92 | - if has_arg is not "no_argument", use ptr for arg also | ||
93 | - opt_complementary affects it too | ||
94 | |||
95 | Note: a good applet will make long options configurable via the | ||
96 | config process and not a required feature. The current standard | ||
97 | is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS. | ||
98 | |||
99 | const char *opt_complementary | ||
100 | |||
101 | ":" The colon (":") is used to separate groups of two or more chars | ||
102 | and/or groups of chars and special characters (stating some | ||
103 | conditions to be checked). | ||
104 | |||
105 | "abc" If groups of two or more chars are specified, the first char | ||
106 | is the main option and the other chars are secondary options. | ||
107 | Their flags will be turned on if the main option is found even | ||
108 | if they are not specifed on the command line. For example: | ||
109 | |||
110 | opt_complementary = "abc"; | ||
111 | flags = getopt32(argc, argv, "abcd") | ||
112 | |||
113 | If getopt() finds "-a" on the command line, then | ||
114 | getopt32's return value will be as if "-a -b -c" were | ||
115 | found. | ||
116 | |||
117 | "ww" Adjacent double options have a counter associated which indicates | ||
118 | the number of occurences of the option. | ||
119 | For example the ps applet needs: | ||
120 | if w is given once, GNU ps sets the width to 132, | ||
121 | if w is given more than once, it is "unlimited" | ||
122 | |||
123 | int w_counter = 0; | ||
124 | opt_complementary = "ww"; | ||
125 | getopt32(argc, argv, "w", &w_counter); | ||
126 | if (w_counter) | ||
127 | width = (w_counter == 1) ? 132 : INT_MAX; | ||
128 | else | ||
129 | get_terminal_width(...&width...); | ||
130 | |||
131 | w_counter is a pointer to an integer. It has to be passed to | ||
132 | getopt32() after all other option argument sinks. | ||
133 | |||
134 | For example: accept multiple -v to indicate the level of verbosity | ||
135 | and for each -b optarg, add optarg to my_b. Finally, if b is given, | ||
136 | turn off c and vice versa: | ||
137 | |||
138 | llist_t *my_b = NULL; | ||
139 | int verbose_level = 0; | ||
140 | opt_complementary = "vv:b::b-c:c-b"; | ||
141 | f = getopt32(argc, argv, "vb:c", &my_b, &verbose_level); | ||
142 | if (f & 2) // -c after -b unsets -b flag | ||
143 | while (my_b) { dosomething_with(my_b->data); my_b = my_b->link; } | ||
144 | if (my_b) // but llist is stored if -b is specified | ||
145 | free_llist(my_b); | ||
146 | if (verbose_level) bb_printf("verbose level is %d\n", verbose_level); | ||
147 | |||
148 | Special characters: | ||
149 | |||
150 | "-" A dash between two options causes the second of the two | ||
151 | to be unset (and ignored) if it is given on the command line. | ||
152 | |||
153 | [FIXME: what if they are the same? like "x-x"? Is it ever useful?] | ||
154 | |||
155 | For example: | ||
156 | The du applet has the options "-s" and "-d depth". If | ||
157 | getopt32 finds -s, then -d is unset or if it finds -d | ||
158 | then -s is unset. (Note: busybox implements the GNU | ||
159 | "--max-depth" option as "-d".) To obtain this behavior, you | ||
160 | set opt_complementary = "s-d:d-s". Only one flag value is | ||
161 | added to getopt32's return value depending on the | ||
162 | position of the options on the command line. If one of the | ||
163 | two options requires an argument pointer (":" in applet_opts | ||
164 | as in "d:") optarg is set accordingly. | ||
165 | |||
166 | char *smax_print_depth; | ||
167 | |||
168 | opt_complementary = "s-d:d-s:x-x"; | ||
169 | opt = getopt32(argc, argv, "sd:x", &smax_print_depth); | ||
170 | |||
171 | if (opt & 2) | ||
172 | max_print_depth = atoi(smax_print_depth); | ||
173 | if (opt & 4) | ||
174 | printf("Detected odd -x usage\n"); | ||
175 | |||
176 | "-" A dash as the first char in a opt_complementary group forces | ||
177 | all arguments to be treated as options, even if they have | ||
178 | no leading dashes. Next char in this case can't be a digit (0-9), | ||
179 | use ':' or end of line. For example: | ||
180 | |||
181 | opt_complementary = "-:w-x:x-w"; | ||
182 | getopt32(argc, argv, "wx"); | ||
183 | |||
184 | Allows any arguments to be given without a dash (./program w x) | ||
185 | as well as with a dash (./program -x). | ||
186 | |||
187 | "-N" A dash as the first char in a opt_complementary group followed | ||
188 | by a single digit (0-9) means that at least N non-option | ||
189 | arguments must be present 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 double dash between two options, or between an option and a group | ||
197 | of options, means that they are mutually exclusive. Unlike | ||
198 | the "-" case above, an error will be forced if the options | ||
199 | are used together. | ||
200 | |||
201 | For example: | ||
202 | The cut applet must have only one type of list specified, so | ||
203 | -b, -c and -f are mutally exclusive and should raise an error | ||
204 | if specified together. In this case you must set | ||
205 | opt_complementary = "b--cf:c--bf:f--bc". If two of the | ||
206 | mutually exclusive options are found, getopt32's | ||
207 | return value will have the error flag set (BB_GETOPT_ERROR) so | ||
208 | that we can check for it: | ||
209 | |||
210 | if (flags & BB_GETOPT_ERROR) | ||
211 | bb_show_usage(); | ||
212 | |||
213 | "x--x" Variation of the above, it means that -x option should occur | ||
214 | at most once. | ||
215 | |||
216 | "?" A "?" as the first char in a opt_complementary group means: | ||
217 | if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage | ||
218 | and exit instead. Next char after '?' can't be a digit. | ||
219 | |||
220 | "?N" A "?" as the first char in a opt_complementary group followed | ||
221 | by a single digit (0-9) means that at most N arguments must be present | ||
222 | on the command line. | ||
223 | |||
224 | "::" A double colon after a char in opt_complementary means that the | ||
225 | option can occur multiple times. Each occurrence will be saved as | ||
226 | a llist_t element instead of char*. | ||
227 | |||
228 | For example: | ||
229 | The grep applet can have one or more "-e pattern" arguments. | ||
230 | In this case you should use getopt32() as follows: | ||
231 | |||
232 | llist_t *patterns = NULL; | ||
233 | |||
234 | (this pointer must be initializated to NULL if the list is empty | ||
235 | as required by *llist_add_to(llist_t *old_head, char *new_item).) | ||
236 | |||
237 | opt_complementary = "e::"; | ||
238 | |||
239 | getopt32(argc, argv, "e:", &patterns); | ||
240 | $ grep -e user -e root /etc/passwd | ||
241 | root:x:0:0:root:/root:/bin/bash | ||
242 | user:x:500:500::/home/user:/bin/bash | ||
243 | |||
244 | "--" A double dash at the beginning of opt_complementary means the | ||
245 | argv[1] string should always be treated as options, even if it isn't | ||
246 | prefixed with a "-". This is useful for special syntax in applets | ||
247 | such as "ar" and "tar": | ||
248 | tar xvf foo.tar | ||
249 | |||
250 | "?" An "?" between an option and a group of options means that | ||
251 | at least one of them is required to occur if the first option | ||
252 | occurs in preceding command line arguments. | ||
253 | |||
254 | For example from "id" applet: | ||
255 | |||
256 | // Don't allow -n -r -rn -ug -rug -nug -rnug | ||
257 | opt_complementary = "r?ug:n?ug:?u--g:g--u"; | ||
258 | flags = getopt32(argc, argv, "rnug"); | ||
259 | |||
260 | This example allowed only: | ||
261 | $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng | ||
262 | |||
263 | "X" A opt_complementary group with just a single letter means | ||
264 | that this option is required. If more than one such group exists, | ||
265 | at least one option is required to occur (not all of them). | ||
266 | For example from "start-stop-daemon" applet: | ||
267 | |||
268 | // Don't allow -KS -SK, but -S or -K is required | ||
269 | opt_complementary = "K:S:?K--S:S--K"; | ||
270 | flags = getopt32(argc, argv, "KS...); | ||
271 | |||
272 | |||
273 | Don't forget to use ':'. For example, "?322-22-23X-x-a" | ||
274 | is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" - | ||
275 | max 3 args; count uses of '-2'; min 2 args; if there is | ||
276 | a '-2' option then unset '-3', '-X' and '-a'; if there is | ||
277 | a '-2' and after it a '-x' then error out. | ||
278 | */ | ||
279 | |||
280 | /* Code here assumes that 'unsigned' is at least 32 bits wide */ | ||
281 | |||
282 | const char *opt_complementary; | ||
283 | |||
284 | typedef struct { | ||
285 | int opt; | ||
286 | int list_flg; | ||
287 | unsigned switch_on; | ||
288 | unsigned switch_off; | ||
289 | unsigned incongruously; | ||
290 | unsigned requires; | ||
291 | void **optarg; /* char **optarg or llist_t **optarg */ | ||
292 | int *counter; | ||
293 | } t_complementary; | ||
294 | |||
295 | /* You can set applet_long_options for parse called long options */ | ||
296 | #if ENABLE_GETOPT_LONG | ||
297 | static const struct option bb_default_long_options[] = { | ||
298 | /* { "help", 0, NULL, '?' }, */ | ||
299 | { 0, 0, 0, 0 } | ||
300 | }; | ||
301 | |||
302 | const struct option *applet_long_options = bb_default_long_options; | ||
303 | #endif | ||
304 | |||
305 | uint32_t | ||
306 | getopt32(int argc, char **argv, const char *applet_opts, ...) | ||
307 | { | ||
308 | unsigned flags = 0; | ||
309 | unsigned requires = 0; | ||
310 | t_complementary complementary[sizeof(flags) * 8 + 1]; | ||
311 | int c; | ||
312 | const unsigned char *s; | ||
313 | t_complementary *on_off; | ||
314 | va_list p; | ||
315 | #if ENABLE_GETOPT_LONG | ||
316 | const struct option *l_o; | ||
317 | #endif | ||
318 | unsigned trigger; | ||
319 | char **pargv = NULL; | ||
320 | int min_arg = 0; | ||
321 | int max_arg = -1; | ||
322 | |||
323 | #define SHOW_USAGE_IF_ERROR 1 | ||
324 | #define ALL_ARGV_IS_OPTS 2 | ||
325 | #define FIRST_ARGV_IS_OPT 4 | ||
326 | #define FREE_FIRST_ARGV_IS_OPT 8 | ||
327 | int spec_flgs = 0; | ||
328 | |||
329 | va_start(p, applet_opts); | ||
330 | |||
331 | c = 0; | ||
332 | on_off = complementary; | ||
333 | memset(on_off, 0, sizeof(complementary)); | ||
334 | |||
335 | /* skip GNU extension */ | ||
336 | s = (const unsigned char *)applet_opts; | ||
337 | if (*s == '+' || *s == '-') | ||
338 | s++; | ||
339 | for (; *s; s++) { | ||
340 | if (c >= (int)(sizeof(flags)*8)) | ||
341 | break; | ||
342 | on_off->opt = *s; | ||
343 | on_off->switch_on = (1 << c); | ||
344 | if (s[1] == ':') { | ||
345 | on_off->optarg = va_arg(p, void **); | ||
346 | do | ||
347 | s++; | ||
348 | while (s[1] == ':'); | ||
349 | } | ||
350 | on_off++; | ||
351 | c++; | ||
352 | } | ||
353 | |||
354 | #if ENABLE_GETOPT_LONG | ||
355 | for (l_o = applet_long_options; l_o->name; l_o++) { | ||
356 | if (l_o->flag) | ||
357 | continue; | ||
358 | for (on_off = complementary; on_off->opt != 0; on_off++) | ||
359 | if (on_off->opt == l_o->val) | ||
360 | break; | ||
361 | if (on_off->opt == 0) { | ||
362 | if (c >= (int)(sizeof(flags)*8)) | ||
363 | break; | ||
364 | on_off->opt = l_o->val; | ||
365 | on_off->switch_on = (1 << c); | ||
366 | if (l_o->has_arg != no_argument) | ||
367 | on_off->optarg = va_arg(p, void **); | ||
368 | c++; | ||
369 | } | ||
370 | } | ||
371 | #endif /* ENABLE_GETOPT_LONG */ | ||
372 | for (s = (const unsigned char *)opt_complementary; s && *s; s++) { | ||
373 | t_complementary *pair; | ||
374 | unsigned *pair_switch; | ||
375 | |||
376 | if (*s == ':') | ||
377 | continue; | ||
378 | c = s[1]; | ||
379 | if (*s == '?') { | ||
380 | if (c < '0' || c > '9') { | ||
381 | spec_flgs |= SHOW_USAGE_IF_ERROR; | ||
382 | } else { | ||
383 | max_arg = c - '0'; | ||
384 | s++; | ||
385 | } | ||
386 | continue; | ||
387 | } | ||
388 | if (*s == '-') { | ||
389 | if (c < '0' || c > '9') { | ||
390 | if (c == '-') { | ||
391 | spec_flgs |= FIRST_ARGV_IS_OPT; | ||
392 | s++; | ||
393 | } else | ||
394 | spec_flgs |= ALL_ARGV_IS_OPTS; | ||
395 | } else { | ||
396 | min_arg = c - '0'; | ||
397 | s++; | ||
398 | } | ||
399 | continue; | ||
400 | } | ||
401 | for (on_off = complementary; on_off->opt; on_off++) | ||
402 | if (on_off->opt == *s) | ||
403 | break; | ||
404 | if (c == ':' && s[2] == ':') { | ||
405 | on_off->list_flg++; | ||
406 | continue; | ||
407 | } | ||
408 | if (c == ':' || c == '\0') { | ||
409 | requires |= on_off->switch_on; | ||
410 | continue; | ||
411 | } | ||
412 | if (c == '-' && (s[2] == ':' || s[2] == '\0')) { | ||
413 | flags |= on_off->switch_on; | ||
414 | on_off->incongruously |= on_off->switch_on; | ||
415 | s++; | ||
416 | continue; | ||
417 | } | ||
418 | if (c == *s) { | ||
419 | on_off->counter = va_arg(p, int *); | ||
420 | s++; | ||
421 | } | ||
422 | pair = on_off; | ||
423 | pair_switch = &(pair->switch_on); | ||
424 | for (s++; *s && *s != ':'; s++) { | ||
425 | if (*s == '?') { | ||
426 | pair_switch = &(pair->requires); | ||
427 | } else if (*s == '-') { | ||
428 | if (pair_switch == &(pair->switch_off)) | ||
429 | pair_switch = &(pair->incongruously); | ||
430 | else | ||
431 | pair_switch = &(pair->switch_off); | ||
432 | } else { | ||
433 | for (on_off = complementary; on_off->opt; on_off++) | ||
434 | if (on_off->opt == *s) { | ||
435 | *pair_switch |= on_off->switch_on; | ||
436 | break; | ||
437 | } | ||
438 | } | ||
439 | } | ||
440 | s--; | ||
441 | } | ||
442 | va_end (p); | ||
443 | |||
444 | #if ENABLE_AR || ENABLE_TAR | ||
445 | if (spec_flgs & FIRST_ARGV_IS_OPT) { | ||
446 | if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') { | ||
447 | argv[1] = xasprintf("-%s", argv[1]); | ||
448 | if (ENABLE_FEATURE_CLEAN_UP) | ||
449 | spec_flgs |= FREE_FIRST_ARGV_IS_OPT; | ||
450 | } | ||
451 | } | ||
452 | #endif | ||
453 | #if ENABLE_GETOPT_LONG | ||
454 | while ((c = getopt_long(argc, argv, applet_opts, | ||
455 | applet_long_options, NULL)) >= 0) { | ||
456 | #else | ||
457 | while ((c = getopt(argc, argv, applet_opts)) >= 0) { | ||
458 | #endif /* ENABLE_GETOPT_LONG */ | ||
459 | loop_arg_is_opt: | ||
460 | for (on_off = complementary; on_off->opt != c; on_off++) { | ||
461 | /* c==0 if long opt have non NULL flag */ | ||
462 | if (on_off->opt == 0 && c != 0) | ||
463 | bb_show_usage(); | ||
464 | } | ||
465 | if (flags & on_off->incongruously) { | ||
466 | if ((spec_flgs & SHOW_USAGE_IF_ERROR)) | ||
467 | bb_show_usage(); | ||
468 | flags |= BB_GETOPT_ERROR; | ||
469 | } | ||
470 | trigger = on_off->switch_on & on_off->switch_off; | ||
471 | flags &= ~(on_off->switch_off ^ trigger); | ||
472 | flags |= on_off->switch_on ^ trigger; | ||
473 | flags ^= trigger; | ||
474 | if (on_off->counter) | ||
475 | (*(on_off->counter))++; | ||
476 | if (on_off->list_flg) { | ||
477 | llist_add_to((llist_t **)(on_off->optarg), optarg); | ||
478 | } else if (on_off->optarg) { | ||
479 | *(char **)(on_off->optarg) = optarg; | ||
480 | } | ||
481 | if (pargv != NULL) | ||
482 | break; | ||
483 | } | ||
484 | |||
485 | if (spec_flgs & ALL_ARGV_IS_OPTS) { | ||
486 | /* process argv is option, for example "ps" applet */ | ||
487 | if (pargv == NULL) | ||
488 | pargv = argv + optind; | ||
489 | while (*pargv) { | ||
490 | c = **pargv; | ||
491 | if (c == '\0') { | ||
492 | pargv++; | ||
493 | } else { | ||
494 | (*pargv)++; | ||
495 | goto loop_arg_is_opt; | ||
496 | } | ||
497 | } | ||
498 | } | ||
499 | |||
500 | #if (ENABLE_AR || ENABLE_TAR) && ENABLE_FEATURE_CLEAN_UP | ||
501 | if (spec_flgs & FREE_FIRST_ARGV_IS_OPT) | ||
502 | free(argv[1]); | ||
503 | #endif | ||
504 | /* check depending requires for given options */ | ||
505 | for (on_off = complementary; on_off->opt; on_off++) { | ||
506 | if (on_off->requires && (flags & on_off->switch_on) && | ||
507 | (flags & on_off->requires) == 0) | ||
508 | bb_show_usage(); | ||
509 | } | ||
510 | if (requires && (flags & requires) == 0) | ||
511 | bb_show_usage(); | ||
512 | argc -= optind; | ||
513 | if (argc < min_arg || (max_arg >= 0 && argc > max_arg)) | ||
514 | bb_show_usage(); | ||
515 | return flags; | ||
516 | } | ||