Contents of /trunk/mkinitrd-magellan/busybox/libbb/getopt32.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 17123 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 17123 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
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) printf("verbose level is %d\n", verbose_level); |
147 | |
148 | Special characters: |
149 | |
150 | "-" A dash as the first char in a opt_complementary group forces |
151 | all arguments to be treated as options, even if they have |
152 | no leading dashes. Next char in this case can't be a digit (0-9), |
153 | use ':' or end of line. For example: |
154 | |
155 | opt_complementary = "-:w-x:x-w"; |
156 | getopt32(argc, argv, "wx"); |
157 | |
158 | Allows any arguments to be given without a dash (./program w x) |
159 | as well as with a dash (./program -x). |
160 | |
161 | "--" A double dash at the beginning of opt_complementary means the |
162 | argv[1] string should always be treated as options, even if it isn't |
163 | prefixed with a "-". This is useful for special syntax in applets |
164 | such as "ar" and "tar": |
165 | tar xvf foo.tar |
166 | |
167 | "-N" A dash as the first char in a opt_complementary group followed |
168 | by a single digit (0-9) means that at least N non-option |
169 | arguments must be present on the command line |
170 | |
171 | "=N" An equal sign as the first char in a opt_complementary group followed |
172 | by a single digit (0-9) means that exactly N non-option |
173 | arguments must be present on the command line |
174 | |
175 | "?N" A "?" as the first char in a opt_complementary group followed |
176 | by a single digit (0-9) means that at most N arguments must be present |
177 | on the command line. |
178 | |
179 | "V-" An option with dash before colon or end-of-line results in |
180 | bb_show_usage being called if this option is encountered. |
181 | This is typically used to implement "print verbose usage message |
182 | and exit" option. |
183 | |
184 | "-" A dash between two options causes the second of the two |
185 | to be unset (and ignored) if it is given on the command line. |
186 | |
187 | [FIXME: what if they are the same? like "x-x"? Is it ever useful?] |
188 | |
189 | For example: |
190 | The du applet has the options "-s" and "-d depth". If |
191 | getopt32 finds -s, then -d is unset or if it finds -d |
192 | then -s is unset. (Note: busybox implements the GNU |
193 | "--max-depth" option as "-d".) To obtain this behavior, you |
194 | set opt_complementary = "s-d:d-s". Only one flag value is |
195 | added to getopt32's return value depending on the |
196 | position of the options on the command line. If one of the |
197 | two options requires an argument pointer (":" in applet_opts |
198 | as in "d:") optarg is set accordingly. |
199 | |
200 | char *smax_print_depth; |
201 | |
202 | opt_complementary = "s-d:d-s:x-x"; |
203 | opt = getopt32(argc, argv, "sd:x", &smax_print_depth); |
204 | |
205 | if (opt & 2) |
206 | max_print_depth = atoi(smax_print_depth); |
207 | if (opt & 4) |
208 | printf("Detected odd -x usage\n"); |
209 | |
210 | "--" A double dash between two options, or between an option and a group |
211 | of options, means that they are mutually exclusive. Unlike |
212 | the "-" case above, an error will be forced if the options |
213 | are used together. |
214 | |
215 | For example: |
216 | The cut applet must have only one type of list specified, so |
217 | -b, -c and -f are mutally exclusive and should raise an error |
218 | if specified together. In this case you must set |
219 | opt_complementary = "b--cf:c--bf:f--bc". If two of the |
220 | mutually exclusive options are found, getopt32's |
221 | return value will have the error flag set (BB_GETOPT_ERROR) so |
222 | that we can check for it: |
223 | |
224 | if (flags & BB_GETOPT_ERROR) |
225 | bb_show_usage(); |
226 | |
227 | "x--x" Variation of the above, it means that -x option should occur |
228 | at most once. |
229 | |
230 | "?" A "?" as the first char in a opt_complementary group means: |
231 | if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage |
232 | and exit instead. Next char after '?' can't be a digit. |
233 | |
234 | "::" A double colon after a char in opt_complementary means that the |
235 | option can occur multiple times. Each occurrence will be saved as |
236 | a llist_t element instead of char*. |
237 | |
238 | For example: |
239 | The grep applet can have one or more "-e pattern" arguments. |
240 | In this case you should use getopt32() as follows: |
241 | |
242 | llist_t *patterns = NULL; |
243 | |
244 | (this pointer must be initializated to NULL if the list is empty |
245 | as required by *llist_add_to(llist_t *old_head, char *new_item).) |
246 | |
247 | opt_complementary = "e::"; |
248 | |
249 | getopt32(argc, argv, "e:", &patterns); |
250 | $ grep -e user -e root /etc/passwd |
251 | root:x:0:0:root:/root:/bin/bash |
252 | user:x:500:500::/home/user:/bin/bash |
253 | |
254 | "?" An "?" between an option and a group of options means that |
255 | at least one of them is required to occur if the first option |
256 | occurs in preceding command line arguments. |
257 | |
258 | For example from "id" applet: |
259 | |
260 | // Don't allow -n -r -rn -ug -rug -nug -rnug |
261 | opt_complementary = "r?ug:n?ug:?u--g:g--u"; |
262 | flags = getopt32(argc, argv, "rnug"); |
263 | |
264 | This example allowed only: |
265 | $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng |
266 | |
267 | "X" A opt_complementary group with just a single letter means |
268 | that this option is required. If more than one such group exists, |
269 | at least one option is required to occur (not all of them). |
270 | For example from "start-stop-daemon" applet: |
271 | |
272 | // Don't allow -KS -SK, but -S or -K is required |
273 | opt_complementary = "K:S:?K--S:S--K"; |
274 | flags = getopt32(argc, argv, "KS...); |
275 | |
276 | |
277 | Don't forget to use ':'. For example, "?322-22-23X-x-a" |
278 | is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" - |
279 | max 3 args; count uses of '-2'; min 2 args; if there is |
280 | a '-2' option then unset '-3', '-X' and '-a'; if there is |
281 | a '-2' and after it a '-x' then error out. |
282 | */ |
283 | |
284 | /* Code here assumes that 'unsigned' is at least 32 bits wide */ |
285 | |
286 | const char *opt_complementary; |
287 | |
288 | typedef struct { |
289 | int opt; |
290 | int list_flg; |
291 | unsigned switch_on; |
292 | unsigned switch_off; |
293 | unsigned incongruously; |
294 | unsigned requires; |
295 | void **optarg; /* char **optarg or llist_t **optarg */ |
296 | int *counter; |
297 | } t_complementary; |
298 | |
299 | /* You can set applet_long_options for parse called long options */ |
300 | #if ENABLE_GETOPT_LONG |
301 | static const struct option bb_default_long_options[] = { |
302 | /* { "help", 0, NULL, '?' }, */ |
303 | { 0, 0, 0, 0 } |
304 | }; |
305 | |
306 | const struct option *applet_long_options = bb_default_long_options; |
307 | #endif |
308 | |
309 | uint32_t option_mask32; |
310 | |
311 | uint32_t |
312 | getopt32(int argc, char **argv, const char *applet_opts, ...) |
313 | { |
314 | unsigned flags = 0; |
315 | unsigned requires = 0; |
316 | t_complementary complementary[33]; |
317 | int c; |
318 | const unsigned char *s; |
319 | t_complementary *on_off; |
320 | va_list p; |
321 | #if ENABLE_GETOPT_LONG |
322 | const struct option *l_o; |
323 | #endif |
324 | unsigned trigger; |
325 | char **pargv = NULL; |
326 | int min_arg = 0; |
327 | int max_arg = -1; |
328 | |
329 | #define SHOW_USAGE_IF_ERROR 1 |
330 | #define ALL_ARGV_IS_OPTS 2 |
331 | #define FIRST_ARGV_IS_OPT 4 |
332 | #define FREE_FIRST_ARGV_IS_OPT 8 |
333 | int spec_flgs = 0; |
334 | |
335 | va_start(p, applet_opts); |
336 | |
337 | c = 0; |
338 | on_off = complementary; |
339 | memset(on_off, 0, sizeof(complementary)); |
340 | |
341 | /* skip GNU extension */ |
342 | s = (const unsigned char *)applet_opts; |
343 | if (*s == '+' || *s == '-') |
344 | s++; |
345 | while (*s) { |
346 | if (c >= 32) break; |
347 | on_off->opt = *s; |
348 | on_off->switch_on = (1 << c); |
349 | if (*++s == ':') { |
350 | on_off->optarg = va_arg(p, void **); |
351 | while (*++s == ':') /* skip */; |
352 | } |
353 | on_off++; |
354 | c++; |
355 | } |
356 | |
357 | #if ENABLE_GETOPT_LONG |
358 | for (l_o = applet_long_options; l_o->name; l_o++) { |
359 | if (l_o->flag) |
360 | continue; |
361 | for (on_off = complementary; on_off->opt != 0; on_off++) |
362 | if (on_off->opt == l_o->val) |
363 | goto next_long; |
364 | if (c >= 32) break; |
365 | on_off->opt = l_o->val; |
366 | on_off->switch_on = (1 << c); |
367 | if (l_o->has_arg != no_argument) |
368 | on_off->optarg = va_arg(p, void **); |
369 | c++; |
370 | next_long: ; |
371 | } |
372 | #endif /* ENABLE_GETOPT_LONG */ |
373 | for (s = (const unsigned char *)opt_complementary; s && *s; s++) { |
374 | t_complementary *pair; |
375 | unsigned *pair_switch; |
376 | |
377 | if (*s == ':') |
378 | continue; |
379 | c = s[1]; |
380 | if (*s == '?') { |
381 | if (c < '0' || c > '9') { |
382 | spec_flgs |= SHOW_USAGE_IF_ERROR; |
383 | } else { |
384 | max_arg = c - '0'; |
385 | s++; |
386 | } |
387 | continue; |
388 | } |
389 | if (*s == '-') { |
390 | if (c < '0' || c > '9') { |
391 | if (c == '-') { |
392 | spec_flgs |= FIRST_ARGV_IS_OPT; |
393 | s++; |
394 | } else |
395 | spec_flgs |= ALL_ARGV_IS_OPTS; |
396 | } else { |
397 | min_arg = c - '0'; |
398 | s++; |
399 | } |
400 | continue; |
401 | } |
402 | if (*s == '=') { |
403 | min_arg = max_arg = c - '0'; |
404 | s++; |
405 | continue; |
406 | } |
407 | for (on_off = complementary; on_off->opt; on_off++) |
408 | if (on_off->opt == *s) |
409 | break; |
410 | if (c == ':' && s[2] == ':') { |
411 | on_off->list_flg++; |
412 | continue; |
413 | } |
414 | if (c == ':' || c == '\0') { |
415 | requires |= on_off->switch_on; |
416 | continue; |
417 | } |
418 | if (c == '-' && (s[2] == ':' || s[2] == '\0')) { |
419 | flags |= on_off->switch_on; |
420 | on_off->incongruously |= on_off->switch_on; |
421 | s++; |
422 | continue; |
423 | } |
424 | if (c == *s) { |
425 | on_off->counter = va_arg(p, int *); |
426 | s++; |
427 | } |
428 | pair = on_off; |
429 | pair_switch = &(pair->switch_on); |
430 | for (s++; *s && *s != ':'; s++) { |
431 | if (*s == '?') { |
432 | pair_switch = &(pair->requires); |
433 | } else if (*s == '-') { |
434 | if (pair_switch == &(pair->switch_off)) |
435 | pair_switch = &(pair->incongruously); |
436 | else |
437 | pair_switch = &(pair->switch_off); |
438 | } else { |
439 | for (on_off = complementary; on_off->opt; on_off++) |
440 | if (on_off->opt == *s) { |
441 | *pair_switch |= on_off->switch_on; |
442 | break; |
443 | } |
444 | } |
445 | } |
446 | s--; |
447 | } |
448 | va_end (p); |
449 | |
450 | if (spec_flgs & FIRST_ARGV_IS_OPT) { |
451 | if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') { |
452 | argv[1] = xasprintf("-%s", argv[1]); |
453 | if (ENABLE_FEATURE_CLEAN_UP) |
454 | spec_flgs |= FREE_FIRST_ARGV_IS_OPT; |
455 | } |
456 | } |
457 | /* Note: just "getopt() <= 0" will not work good for |
458 | * "fake" short options, like this one: |
459 | * wget $'-\203' "Test: test" http://kernel.org/ |
460 | * (supposed to act as --header, but doesn't) */ |
461 | #if ENABLE_GETOPT_LONG |
462 | while ((c = getopt_long(argc, argv, applet_opts, |
463 | applet_long_options, NULL)) != -1) { |
464 | #else |
465 | while ((c = getopt(argc, argv, applet_opts)) != -1) { |
466 | #endif /* ENABLE_GETOPT_LONG */ |
467 | c &= 0xff; /* fight libc's sign extends */ |
468 | loop_arg_is_opt: |
469 | for (on_off = complementary; on_off->opt != c; on_off++) { |
470 | /* c==0 if long opt have non NULL flag */ |
471 | if (on_off->opt == 0 && c != 0) |
472 | bb_show_usage(); |
473 | } |
474 | if (flags & on_off->incongruously) { |
475 | if ((spec_flgs & SHOW_USAGE_IF_ERROR)) |
476 | bb_show_usage(); |
477 | flags |= BB_GETOPT_ERROR; |
478 | } |
479 | trigger = on_off->switch_on & on_off->switch_off; |
480 | flags &= ~(on_off->switch_off ^ trigger); |
481 | flags |= on_off->switch_on ^ trigger; |
482 | flags ^= trigger; |
483 | if (on_off->counter) |
484 | (*(on_off->counter))++; |
485 | if (on_off->list_flg) { |
486 | llist_add_to((llist_t **)(on_off->optarg), optarg); |
487 | } else if (on_off->optarg) { |
488 | *(char **)(on_off->optarg) = optarg; |
489 | } |
490 | if (pargv != NULL) |
491 | break; |
492 | } |
493 | |
494 | if (spec_flgs & ALL_ARGV_IS_OPTS) { |
495 | /* process argv is option, for example "ps" applet */ |
496 | if (pargv == NULL) |
497 | pargv = argv + optind; |
498 | while (*pargv) { |
499 | c = **pargv; |
500 | if (c == '\0') { |
501 | pargv++; |
502 | } else { |
503 | (*pargv)++; |
504 | goto loop_arg_is_opt; |
505 | } |
506 | } |
507 | } |
508 | |
509 | #if (ENABLE_AR || ENABLE_TAR) && ENABLE_FEATURE_CLEAN_UP |
510 | if (spec_flgs & FREE_FIRST_ARGV_IS_OPT) |
511 | free(argv[1]); |
512 | #endif |
513 | /* check depending requires for given options */ |
514 | for (on_off = complementary; on_off->opt; on_off++) { |
515 | if (on_off->requires && (flags & on_off->switch_on) && |
516 | (flags & on_off->requires) == 0) |
517 | bb_show_usage(); |
518 | } |
519 | if (requires && (flags & requires) == 0) |
520 | bb_show_usage(); |
521 | argc -= optind; |
522 | if (argc < min_arg || (max_arg >= 0 && argc > max_arg)) |
523 | bb_show_usage(); |
524 | |
525 | option_mask32 = flags; |
526 | return flags; |
527 | } |