getopt32: add new syntax of 'o:+' and 'o:*' for -o NUM and -o LIST
In many cases, this aqllows to drop use of opt_complementary.
Approximately -400 bytes:
function old new delta
getopt32 1423 1502 +79
opt_string 17 18 +1
OPT_STR 24 25 +1
uniq_main 416 406 -10
timeout_main 279 269 -10
sulogin_main 270 260 -10
readprofile_main 1825 1815 -10
ps_main 543 533 -10
pidof_main 245 235 -10
pgrep_main 611 601 -10
od_main 2600 2590 -10
mkfs_minix_main 2684 2674 -10
mkfs_ext2_main 2603 2593 -10
microcom_main 712 702 -10
makemime_main 315 305 -10
ionice_main 282 272 -10
inetd_main 2074 2064 -10
ifplugd_main 1144 1134 -10
halt_main 353 343 -10
getopt_main 636 626 -10
fdisk_main 2854 2844 -10
env_main 206 196 -10
dmesg_main 319 309 -10
conspy_main 1214 1204 -10
awk_main 981 971 -10
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/22 up/down: 81/-220) Total: -139 bytes
text data bss dec hex filename
919373 906 14060 934339 e41c3 busybox_old
918969 906 14060 933935 e402f busybox_unstripped
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/libbb/getopt32.c b/libbb/getopt32.c
index d0e83d8..15b6efc 100644
--- a/libbb/getopt32.c
+++ b/libbb/getopt32.c
@@ -17,23 +17,20 @@
uint32_t
getopt32(char **argv, const char *applet_opts, ...)
- The command line options must be declared in const char
- *applet_opts as a string of chars, for example:
-
- flags = getopt32(argv, "rnug");
+ The command line options are passed as the applet_opts string.
If one of the given options is found, a flag value is added to
- the return value (an unsigned long).
+ the return value.
The flag value is determined by the position of the char in
- applet_opts string. For example, in the above case:
+ applet_opts string. For example:
flags = getopt32(argv, "rnug");
- "r" will add 1 (bit 0)
- "n" will add 2 (bit 1)
- "u" will add 4 (bit 2)
- "g" will add 8 (bit 3)
+ "r" will set 1 (bit 0)
+ "n" will set 2 (bit 1)
+ "u" will set 4 (bit 2)
+ "g" will set 8 (bit 3)
and so on. You can also look at the return value as a bit
field and each option sets one bit.
@@ -45,7 +42,7 @@
(options and their parameters will be moved into argv[]
positions prior to argv[optind]).
- ":" If one of the options requires an argument, then add a ":"
+ "o:" If one of the options requires an argument, then add a ":"
after the char in applet_opts and provide a pointer to store
the argument. For example:
@@ -58,15 +55,39 @@
&pointer_to_arg_for_a, &pointer_to_arg_for_b,
&pointer_to_arg_for_c, &pointer_to_arg_for_d);
- The type of the pointer (char* or llist_t*) may be controlled
- by the "::" special separator that is set in the external string
- opt_complementary (see below for more info).
+ The type of the pointer may be controlled by "o::" or "o+" in
+ the external string opt_complementary (see below for more info).
- "::" If option can have an *optional* argument, then add a "::"
+ "o::" If option can have an *optional* argument, then add a "::"
after its char in applet_opts and provide a pointer to store
the argument. Note that optional arguments _must_
immediately follow the option: -oparam, not -o param.
+ "o:+" This means that the parameter for this option is a nonnegative integer.
+ It will be processed with xatoi_positive() - allowed range
+ is 0..INT_MAX.
+
+ int param; // "unsigned param;" will also work
+ getopt32(argv, "p:+", ¶m);
+
+ "o:*" This means that the option can occur multiple times. Each occurrence
+ will be saved as a llist_t element instead of char*.
+
+ For example:
+ The grep applet can have one or more "-e pattern" arguments.
+ In this case you should use getopt32() as follows:
+
+ llist_t *patterns = NULL;
+
+ (this pointer must be initializated to NULL if the list is empty
+ as required by llist_add_to_end(llist_t **old_head, char *new_item).)
+
+ getopt32(argv, "e:*", &patterns);
+
+ $ grep -e user -e root /etc/passwd
+ root:x:0:0:root:/root:/bin/bash
+ user:x:500:500::/home/user:/bin/bash
+
"+" If the first character in the applet_opts string is a plus,
then option processing will stop as soon as a non-option is
encountered in the argv array. Useful for applets like env
@@ -82,7 +103,7 @@
This struct allows you to define long options:
static const char applet_longopts[] ALIGN1 =
- //"name\0" has_arg val
+ //"name\0" has_arg val
"verbose\0" No_argument "v"
;
applet_long_options = applet_longopts;
@@ -90,7 +111,7 @@
The last member of struct option (val) typically is set to
matching short option from applet_opts. If there is no matching
char in applet_opts, then:
- - return bit have next position after short options
+ - return bit has next position after short options
- if has_arg is not "No_argument", use ptr for arg also
- opt_complementary affects it too
@@ -139,8 +160,8 @@
llist_t *my_b = NULL;
int verbose_level = 0;
- opt_complementary = "vv:b::b-c:c-b";
- f = getopt32(argv, "vb:c", &my_b, &verbose_level);
+ opt_complementary = "vv:b-c:c-b";
+ f = getopt32(argv, "vb:*c", &my_b, &verbose_level);
if (f & 2) // -c after -b unsets -b flag
while (my_b) dosomething_with(llist_pop(&my_b));
if (my_b) // but llist is stored if -b is specified
@@ -233,7 +254,7 @@
"x--x" Variation of the above, it means that -x option should occur
at most once.
- "a+" A plus after a char in opt_complementary means that the parameter
+ "o+" A plus after a char in opt_complementary means that the parameter
for this option is a nonnegative integer. It will be processed
with xatoi_positive() - allowed range is 0..INT_MAX.
@@ -241,7 +262,7 @@
opt_complementary = "p+";
getopt32(argv, "p:", ¶m);
- "a::" A double colon after a char in opt_complementary means that the
+ "o::" A double colon after a char in opt_complementary means that the
option can occur multiple times. Each occurrence will be saved as
a llist_t element instead of char*.
@@ -255,12 +276,17 @@
as required by llist_add_to_end(llist_t **old_head, char *new_item).)
opt_complementary = "e::";
-
getopt32(argv, "e:", &patterns);
+
$ grep -e user -e root /etc/passwd
root:x:0:0:root:/root:/bin/bash
user:x:500:500::/home/user:/bin/bash
+ "o+" and "o::" can be handled by "o:+" and "o:*" specifiers
+ in option string (and it is preferred), but this does not work
+ for "long options only" cases, such as tar --exclude=PATTERN,
+ wget --header=HDR cases.
+
"a?b" A "?" between an option and a group of options means that
at least one of them is required to occur if the first option
occurs in preceding command line arguments.
@@ -359,10 +385,11 @@
va_start(p, applet_opts);
- c = 0;
on_off = complementary;
memset(on_off, 0, sizeof(complementary));
+ applet_opts = strcpy(alloca(strlen(applet_opts) + 1), applet_opts);
+
/* skip bbox extension */
first_char = applet_opts[0];
if (first_char == '!')
@@ -372,6 +399,7 @@
s = (const unsigned char *)applet_opts;
if (*s == '+' || *s == '-')
s++;
+ c = 0;
while (*s) {
if (c >= 32)
break;
@@ -379,6 +407,13 @@
on_off->switch_on = (1 << c);
if (*++s == ':') {
on_off->optarg = va_arg(p, void **);
+ if (s[1] == '+' || s[1] == '*') {
+ /* 'o:+' or 'o:*' */
+ on_off->param_type = (s[1] == '+') ?
+ PARAM_INT : PARAM_LIST;
+ overlapping_strcpy((char*)s + 1, (char*)s + 2);
+ }
+ /* skip possible 'o::' (or 'o:+:' !) */
while (*++s == ':')
continue;
}
@@ -431,6 +466,7 @@
applet_long_options = NULL;
}
#endif /* ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG */
+
for (s = (const unsigned char *)opt_complementary; s && *s; s++) {
t_complementary *pair;
unsigned *pair_switch;