diff options
Diffstat (limited to 'libbb/getopt32.c')
-rw-r--r-- | libbb/getopt32.c | 198 |
1 files changed, 87 insertions, 111 deletions
diff --git a/libbb/getopt32.c b/libbb/getopt32.c index 80f4cc060..f778c6e89 100644 --- a/libbb/getopt32.c +++ b/libbb/getopt32.c | |||
@@ -6,12 +6,13 @@ | |||
6 | * | 6 | * |
7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
8 | */ | 8 | */ |
9 | 9 | #if ENABLE_LONG_OPTS | |
10 | #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG | ||
11 | # include <getopt.h> | 10 | # include <getopt.h> |
12 | #endif | 11 | #endif |
13 | #include "libbb.h" | 12 | #include "libbb.h" |
14 | 13 | ||
14 | //kbuild:lib-y += getopt32.o | ||
15 | |||
15 | /* Documentation | 16 | /* Documentation |
16 | 17 | ||
17 | uint32_t | 18 | uint32_t |
@@ -95,20 +96,23 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
95 | env -i ls -d / | 96 | env -i ls -d / |
96 | Here we want env to process just the '-i', not the '-d'. | 97 | Here we want env to process just the '-i', not the '-d'. |
97 | 98 | ||
98 | "!" Report bad option, missing required options, | 99 | "!" Report bad options, missing required options, |
99 | inconsistent options with all-ones return value (instead of abort). | 100 | inconsistent options with all-ones return value (instead of abort). |
100 | 101 | ||
101 | const char *applet_long_options | 102 | "^" options string is "^optchars""\0""opt_complementary". |
103 | |||
104 | uint32_t | ||
105 | getopt32long(char **argv, const char *applet_opts, const char *logopts...) | ||
102 | 106 | ||
103 | This struct allows you to define long options: | 107 | This allows you to define long options: |
104 | 108 | ||
105 | static const char applet_longopts[] ALIGN1 = | 109 | static const char applet_longopts[] ALIGN1 = |
106 | //"name\0" has_arg val | 110 | //"name\0" has_arg val |
107 | "verbose\0" No_argument "v" | 111 | "verbose\0" No_argument "v" |
108 | ; | 112 | ; |
109 | applet_long_options = applet_longopts; | 113 | opt = getopt32long(argv, applet_opts, applet_longopts, ...); |
110 | 114 | ||
111 | The last member of struct option (val) typically is set to | 115 | The last element (val) typically is set to |
112 | matching short option from applet_opts. If there is no matching | 116 | matching short option from applet_opts. If there is no matching |
113 | char in applet_opts, then: | 117 | char in applet_opts, then: |
114 | - return bit has next position after short options | 118 | - return bit has next position after short options |
@@ -119,7 +123,7 @@ const char *applet_long_options | |||
119 | config process and not a required feature. The current standard | 123 | config process and not a required feature. The current standard |
120 | is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS. | 124 | is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS. |
121 | 125 | ||
122 | const char *opt_complementary | 126 | opt_complementary - option modifiers. |
123 | 127 | ||
124 | ":" The colon (":") is used to separate groups of two or more chars | 128 | ":" The colon (":") is used to separate groups of two or more chars |
125 | and/or groups of chars and special characters (stating some | 129 | and/or groups of chars and special characters (stating some |
@@ -130,8 +134,7 @@ const char *opt_complementary | |||
130 | Their flags will be turned on if the main option is found even | 134 | Their flags will be turned on if the main option is found even |
131 | if they are not specified on the command line. For example: | 135 | if they are not specified on the command line. For example: |
132 | 136 | ||
133 | opt_complementary = "abc"; | 137 | flags = getopt32(argv, "^abcd""\0""abc") |
134 | flags = getopt32(argv, "abcd") | ||
135 | 138 | ||
136 | If getopt() finds "-a" on the command line, then | 139 | If getopt() finds "-a" on the command line, then |
137 | getopt32's return value will be as if "-a -b -c" were | 140 | getopt32's return value will be as if "-a -b -c" were |
@@ -144,8 +147,7 @@ const char *opt_complementary | |||
144 | if w is given more than once, it is "unlimited" | 147 | if w is given more than once, it is "unlimited" |
145 | 148 | ||
146 | int w_counter = 0; // must be initialized! | 149 | int w_counter = 0; // must be initialized! |
147 | opt_complementary = "ww"; | 150 | getopt32(argv, "^w""\0""ww", &w_counter); |
148 | getopt32(argv, "w", &w_counter); | ||
149 | if (w_counter) | 151 | if (w_counter) |
150 | width = (w_counter == 1) ? 132 : INT_MAX; | 152 | width = (w_counter == 1) ? 132 : INT_MAX; |
151 | else | 153 | else |
@@ -160,8 +162,9 @@ const char *opt_complementary | |||
160 | 162 | ||
161 | llist_t *my_b = NULL; | 163 | llist_t *my_b = NULL; |
162 | int verbose_level = 0; | 164 | int verbose_level = 0; |
163 | opt_complementary = "vv:b-c:c-b"; | 165 | f = getopt32(argv, "^vb:*c" |
164 | f = getopt32(argv, "vb:*c", &my_b, &verbose_level); | 166 | "\0""vv:b-c:c-b" |
167 | , &my_b, &verbose_level); | ||
165 | if (f & 2) // -c after -b unsets -b flag | 168 | if (f & 2) // -c after -b unsets -b flag |
166 | while (my_b) dosomething_with(llist_pop(&my_b)); | 169 | while (my_b) dosomething_with(llist_pop(&my_b)); |
167 | if (my_b) // but llist is stored if -b is specified | 170 | if (my_b) // but llist is stored if -b is specified |
@@ -170,31 +173,6 @@ const char *opt_complementary | |||
170 | 173 | ||
171 | Special characters: | 174 | Special characters: |
172 | 175 | ||
173 | "-" A group consisting of just a dash forces all arguments | ||
174 | to be treated as options, even if they have no leading dashes. | ||
175 | Next char in this case can't be a digit (0-9), use ':' or end of line. | ||
176 | Example: | ||
177 | |||
178 | opt_complementary = "-:w-x:x-w"; // "-w-x:x-w" would also work, | ||
179 | getopt32(argv, "wx"); // but is less readable | ||
180 | |||
181 | This makes it possible to use options without a dash (./program w x) | ||
182 | as well as with a dash (./program -x). | ||
183 | |||
184 | NB: getopt32() will leak a small amount of memory if you use | ||
185 | this option! Do not use it if there is a possibility of recursive | ||
186 | getopt32() calls. | ||
187 | |||
188 | "--" A double dash at the beginning of opt_complementary means the | ||
189 | argv[1] string should always be treated as options, even if it isn't | ||
190 | prefixed with a "-". This is useful for special syntax in applets | ||
191 | such as "ar" and "tar": | ||
192 | tar xvf foo.tar | ||
193 | |||
194 | NB: getopt32() will leak a small amount of memory if you use | ||
195 | this option! Do not use it if there is a possibility of recursive | ||
196 | getopt32() calls. | ||
197 | |||
198 | "-N" A dash as the first char in a opt_complementary group followed | 176 | "-N" A dash as the first char in a opt_complementary group followed |
199 | by a single digit (0-9) means that at least N non-option | 177 | by a single digit (0-9) means that at least N non-option |
200 | arguments must be present on the command line | 178 | arguments must be present on the command line |
@@ -222,7 +200,7 @@ Special characters: | |||
222 | getopt32 finds -s, then -d is unset or if it finds -d | 200 | getopt32 finds -s, then -d is unset or if it finds -d |
223 | then -s is unset. (Note: busybox implements the GNU | 201 | then -s is unset. (Note: busybox implements the GNU |
224 | "--max-depth" option as "-d".) To obtain this behavior, you | 202 | "--max-depth" option as "-d".) To obtain this behavior, you |
225 | set opt_complementary = "s-d:d-s". Only one flag value is | 203 | set opt_complementary to "s-d:d-s". Only one flag value is |
226 | added to getopt32's return value depending on the | 204 | added to getopt32's return value depending on the |
227 | position of the options on the command line. If one of the | 205 | position of the options on the command line. If one of the |
228 | two options requires an argument pointer (":" in applet_opts | 206 | two options requires an argument pointer (":" in applet_opts |
@@ -230,8 +208,7 @@ Special characters: | |||
230 | 208 | ||
231 | char *smax_print_depth; | 209 | char *smax_print_depth; |
232 | 210 | ||
233 | opt_complementary = "s-d:d-s:x-x"; | 211 | opt = getopt32(argv, "^sd:x""\0""s-d:d-s:x-x", &smax_print_depth); |
234 | opt = getopt32(argv, "sd:x", &smax_print_depth); | ||
235 | 212 | ||
236 | if (opt & 2) | 213 | if (opt & 2) |
237 | max_print_depth = atoi(smax_print_depth); | 214 | max_print_depth = atoi(smax_print_depth); |
@@ -247,7 +224,7 @@ Special characters: | |||
247 | The cut applet must have only one type of list specified, so | 224 | The cut applet must have only one type of list specified, so |
248 | -b, -c and -f are mutually exclusive and should raise an error | 225 | -b, -c and -f are mutually exclusive and should raise an error |
249 | if specified together. In this case you must set | 226 | if specified together. In this case you must set |
250 | opt_complementary = "b--cf:c--bf:f--bc". If two of the | 227 | opt_complementary to "b--cf:c--bf:f--bc". If two of the |
251 | mutually exclusive options are found, getopt32 will call | 228 | mutually exclusive options are found, getopt32 will call |
252 | bb_show_usage() and die. | 229 | bb_show_usage() and die. |
253 | 230 | ||
@@ -259,8 +236,7 @@ Special characters: | |||
259 | with xatoi_positive() - allowed range is 0..INT_MAX. | 236 | with xatoi_positive() - allowed range is 0..INT_MAX. |
260 | 237 | ||
261 | int param; // "unsigned param;" will also work | 238 | int param; // "unsigned param;" will also work |
262 | opt_complementary = "p+"; | 239 | getopt32(argv, "^p:""\0""p+", ¶m); |
263 | getopt32(argv, "p:", ¶m); | ||
264 | 240 | ||
265 | "o::" A double colon after a char in opt_complementary means that the | 241 | "o::" A double colon after a char in opt_complementary means that the |
266 | option can occur multiple times. Each occurrence will be saved as | 242 | option can occur multiple times. Each occurrence will be saved as |
@@ -275,8 +251,7 @@ Special characters: | |||
275 | (this pointer must be initializated to NULL if the list is empty | 251 | (this pointer must be initializated to NULL if the list is empty |
276 | as required by llist_add_to_end(llist_t **old_head, char *new_item).) | 252 | as required by llist_add_to_end(llist_t **old_head, char *new_item).) |
277 | 253 | ||
278 | opt_complementary = "e::"; | 254 | getopt32(argv, "^e:""\0""e::", &patterns); |
279 | getopt32(argv, "e:", &patterns); | ||
280 | 255 | ||
281 | $ grep -e user -e root /etc/passwd | 256 | $ grep -e user -e root /etc/passwd |
282 | root:x:0:0:root:/root:/bin/bash | 257 | root:x:0:0:root:/root:/bin/bash |
@@ -294,8 +269,7 @@ Special characters: | |||
294 | For example from "id" applet: | 269 | For example from "id" applet: |
295 | 270 | ||
296 | // Don't allow -n -r -rn -ug -rug -nug -rnug | 271 | // Don't allow -n -r -rn -ug -rug -nug -rnug |
297 | opt_complementary = "r?ug:n?ug:u--g:g--u"; | 272 | flags = getopt32(argv, "^rnug""\0""r?ug:n?ug:u--g:g--u"); |
298 | flags = getopt32(argv, "rnug"); | ||
299 | 273 | ||
300 | This example allowed only: | 274 | This example allowed only: |
301 | $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng | 275 | $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng |
@@ -306,8 +280,7 @@ Special characters: | |||
306 | For example from "start-stop-daemon" applet: | 280 | For example from "start-stop-daemon" applet: |
307 | 281 | ||
308 | // Don't allow -KS -SK, but -S or -K is required | 282 | // Don't allow -KS -SK, but -S or -K is required |
309 | opt_complementary = "K:S:K--S:S--K"; | 283 | flags = getopt32(argv, "^KS...""\0""K:S:K--S:S--K"); |
310 | flags = getopt32(argv, "KS...); | ||
311 | 284 | ||
312 | 285 | ||
313 | Don't forget to use ':'. For example, "?322-22-23X-x-a" | 286 | Don't forget to use ':'. For example, "?322-22-23X-x-a" |
@@ -322,8 +295,6 @@ Special characters: | |||
322 | 295 | ||
323 | const char *const bb_argv_dash[] = { "-", NULL }; | 296 | const char *const bb_argv_dash[] = { "-", NULL }; |
324 | 297 | ||
325 | const char *opt_complementary; | ||
326 | |||
327 | enum { | 298 | enum { |
328 | PARAM_STRING, | 299 | PARAM_STRING, |
329 | PARAM_LIST, | 300 | PARAM_LIST, |
@@ -341,58 +312,63 @@ typedef struct { | |||
341 | int *counter; | 312 | int *counter; |
342 | } t_complementary; | 313 | } t_complementary; |
343 | 314 | ||
344 | /* You can set applet_long_options for parse called long options */ | 315 | uint32_t option_mask32; |
345 | #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG | 316 | |
317 | #if ENABLE_LONG_OPTS | ||
346 | static const struct option bb_null_long_options[1] = { | 318 | static const struct option bb_null_long_options[1] = { |
347 | { 0, 0, 0, 0 } | 319 | { 0, 0, 0, 0 } |
348 | }; | 320 | }; |
349 | const char *applet_long_options; | 321 | #else |
322 | #define vgetopt32(argv,applet_opts,applet_long_options,p) \ | ||
323 | vgetopt32(argv,applet_opts,p) | ||
350 | #endif | 324 | #endif |
351 | 325 | ||
352 | uint32_t option_mask32; | 326 | /* Please keep getopt32 free from xmalloc */ |
353 | 327 | ||
354 | uint32_t FAST_FUNC | 328 | static uint32_t |
355 | getopt32(char **argv, const char *applet_opts, ...) | 329 | vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, va_list p) |
356 | { | 330 | { |
357 | int argc; | 331 | int argc; |
358 | unsigned flags = 0; | 332 | unsigned flags = 0; |
359 | unsigned requires = 0; | 333 | unsigned requires = 0; |
334 | unsigned len; | ||
360 | t_complementary complementary[33]; /* last stays zero-filled */ | 335 | t_complementary complementary[33]; /* last stays zero-filled */ |
361 | char first_char; | 336 | char dont_die_flag; |
362 | int c; | 337 | int c; |
363 | const unsigned char *s; | 338 | const unsigned char *s; |
339 | const char *opt_complementary; | ||
364 | t_complementary *on_off; | 340 | t_complementary *on_off; |
365 | va_list p; | 341 | #if ENABLE_LONG_OPTS |
366 | #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG | ||
367 | const struct option *l_o; | 342 | const struct option *l_o; |
368 | struct option *long_options = (struct option *) &bb_null_long_options; | 343 | struct option *long_options = (struct option *) &bb_null_long_options; |
369 | #endif | 344 | #endif |
370 | unsigned trigger; | 345 | unsigned trigger; |
371 | char **pargv; | ||
372 | int min_arg = 0; | 346 | int min_arg = 0; |
373 | int max_arg = -1; | 347 | int max_arg = -1; |
374 | |||
375 | #define SHOW_USAGE_IF_ERROR 1 | ||
376 | #define ALL_ARGV_IS_OPTS 2 | ||
377 | #define FIRST_ARGV_IS_OPT 4 | ||
378 | |||
379 | int spec_flgs = 0; | 348 | int spec_flgs = 0; |
380 | 349 | ||
381 | /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ | 350 | #define SHOW_USAGE_IF_ERROR 1 |
382 | argc = 1 + string_array_len(argv + 1); | ||
383 | |||
384 | va_start(p, applet_opts); | ||
385 | 351 | ||
386 | on_off = complementary; | 352 | on_off = complementary; |
387 | memset(on_off, 0, sizeof(complementary)); | 353 | memset(on_off, 0, sizeof(complementary)); |
388 | 354 | ||
389 | applet_opts = strcpy(alloca(strlen(applet_opts) + 1), applet_opts); | 355 | len = strlen(applet_opts); |
390 | 356 | ||
391 | /* skip bbox extension */ | 357 | /* skip bbox extension */ |
392 | first_char = applet_opts[0]; | 358 | opt_complementary = NULL; |
393 | if (first_char == '!') | 359 | if (applet_opts[0] == '^') { |
360 | applet_opts++; | ||
361 | /* point it past terminating NUL */ | ||
362 | opt_complementary = applet_opts + len; | ||
363 | } | ||
364 | |||
365 | /* skip another bbox extension */ | ||
366 | dont_die_flag = applet_opts[0]; | ||
367 | if (dont_die_flag == '!') | ||
394 | applet_opts++; | 368 | applet_opts++; |
395 | 369 | ||
370 | applet_opts = strcpy(alloca(len + 1), applet_opts); | ||
371 | |||
396 | /* skip GNU extension */ | 372 | /* skip GNU extension */ |
397 | s = (const unsigned char *)applet_opts; | 373 | s = (const unsigned char *)applet_opts; |
398 | if (*s == '+' || *s == '-') | 374 | if (*s == '+' || *s == '-') |
@@ -419,7 +395,7 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
419 | c++; | 395 | c++; |
420 | } | 396 | } |
421 | 397 | ||
422 | #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG | 398 | #if ENABLE_LONG_OPTS |
423 | if (applet_long_options) { | 399 | if (applet_long_options) { |
424 | const char *optstr; | 400 | const char *optstr; |
425 | unsigned i, count; | 401 | unsigned i, count; |
@@ -458,14 +434,11 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
458 | c++; | 434 | c++; |
459 | next_long: ; | 435 | next_long: ; |
460 | } | 436 | } |
461 | /* Make it unnecessary to clear applet_long_options | ||
462 | * by hand after each call to getopt32 | ||
463 | */ | ||
464 | applet_long_options = NULL; | ||
465 | } | 437 | } |
466 | #endif /* ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG */ | 438 | #endif /* ENABLE_LONG_OPTS */ |
467 | 439 | ||
468 | for (s = (const unsigned char *)opt_complementary; s && *s; s++) { | 440 | s = (const unsigned char *)opt_complementary; |
441 | if (s) for (; *s; s++) { | ||
469 | t_complementary *pair; | 442 | t_complementary *pair; |
470 | unsigned *pair_switch; | 443 | unsigned *pair_switch; |
471 | 444 | ||
@@ -482,13 +455,7 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
482 | continue; | 455 | continue; |
483 | } | 456 | } |
484 | if (*s == '-') { | 457 | if (*s == '-') { |
485 | if (c < '0' || c > '9') { | 458 | if (c >= '0' && c <= '9') { |
486 | if (c == '-') { | ||
487 | spec_flgs |= FIRST_ARGV_IS_OPT; | ||
488 | s++; | ||
489 | } else | ||
490 | spec_flgs |= ALL_ARGV_IS_OPTS; | ||
491 | } else { | ||
492 | min_arg = c - '0'; | 459 | min_arg = c - '0'; |
493 | s++; | 460 | s++; |
494 | } | 461 | } |
@@ -548,26 +515,6 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
548 | } | 515 | } |
549 | s--; | 516 | s--; |
550 | } | 517 | } |
551 | opt_complementary = NULL; | ||
552 | va_end(p); | ||
553 | |||
554 | if (spec_flgs & (FIRST_ARGV_IS_OPT | ALL_ARGV_IS_OPTS)) { | ||
555 | pargv = argv + 1; | ||
556 | while (*pargv) { | ||
557 | if (pargv[0][0] != '-' && pargv[0][0] != '\0') { | ||
558 | /* Can't use alloca: opts with params will | ||
559 | * return pointers to stack! | ||
560 | * NB: we leak these allocations... */ | ||
561 | char *pp = xmalloc(strlen(*pargv) + 2); | ||
562 | *pp = '-'; | ||
563 | strcpy(pp + 1, *pargv); | ||
564 | *pargv = pp; | ||
565 | } | ||
566 | if (!(spec_flgs & ALL_ARGV_IS_OPTS)) | ||
567 | break; | ||
568 | pargv++; | ||
569 | } | ||
570 | } | ||
571 | 518 | ||
572 | /* In case getopt32 was already called: | 519 | /* In case getopt32 was already called: |
573 | * reset the libc getopt() function, which keeps internal state. | 520 | * reset the libc getopt() function, which keeps internal state. |
@@ -576,11 +523,14 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
576 | */ | 523 | */ |
577 | GETOPT_RESET(); | 524 | GETOPT_RESET(); |
578 | 525 | ||
526 | /* skip 0: some applets cheat: they do not actually HAVE argv[0] */ | ||
527 | argc = 1 + string_array_len(argv + 1); | ||
528 | |||
579 | /* Note: just "getopt() <= 0" will not work well for | 529 | /* Note: just "getopt() <= 0" will not work well for |
580 | * "fake" short options, like this one: | 530 | * "fake" short options, like this one: |
581 | * wget $'-\203' "Test: test" http://kernel.org/ | 531 | * wget $'-\203' "Test: test" http://kernel.org/ |
582 | * (supposed to act as --header, but doesn't) */ | 532 | * (supposed to act as --header, but doesn't) */ |
583 | #if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG | 533 | #if ENABLE_LONG_OPTS |
584 | while ((c = getopt_long(argc, argv, applet_opts, | 534 | while ((c = getopt_long(argc, argv, applet_opts, |
585 | long_options, NULL)) != -1) { | 535 | long_options, NULL)) != -1) { |
586 | #else | 536 | #else |
@@ -637,7 +587,33 @@ getopt32(char **argv, const char *applet_opts, ...) | |||
637 | return flags; | 587 | return flags; |
638 | 588 | ||
639 | error: | 589 | error: |
640 | if (first_char != '!') | 590 | if (dont_die_flag != '!') |
641 | bb_show_usage(); | 591 | bb_show_usage(); |
642 | return (int32_t)-1; | 592 | return (int32_t)-1; |
643 | } | 593 | } |
594 | |||
595 | uint32_t FAST_FUNC | ||
596 | getopt32(char **argv, const char *applet_opts, ...) | ||
597 | { | ||
598 | uint32_t opt; | ||
599 | va_list p; | ||
600 | |||
601 | va_start(p, applet_opts); | ||
602 | opt = vgetopt32(argv, applet_opts, NULL, p); | ||
603 | va_end(p); | ||
604 | return opt; | ||
605 | } | ||
606 | |||
607 | #if ENABLE_LONG_OPTS | ||
608 | uint32_t FAST_FUNC | ||
609 | getopt32long(char **argv, const char *applet_opts, const char *longopts, ...) | ||
610 | { | ||
611 | uint32_t opt; | ||
612 | va_list p; | ||
613 | |||
614 | va_start(p, longopts); | ||
615 | opt = vgetopt32(argv, applet_opts, longopts, p); | ||
616 | va_end(p); | ||
617 | return opt; | ||
618 | } | ||
619 | #endif | ||