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:+", &param);
+
+ "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:", &param);
 
- "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;
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index 1590d9a..4084397 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -42,8 +42,8 @@
 	int mintokens = 0, ntokens = 128;
 	unsigned noout;
 
-	opt_complementary = "-1:n+:m+:f+";
-	noout = 1 & getopt32(argv, "xn:m:d:f:", &ntokens, &mintokens, &delims, &flags);
+	opt_complementary = "-1";
+	noout = 1 & getopt32(argv, "xn:+m:+d:f:+", &ntokens, &mintokens, &delims, &flags);
 	//argc -= optind;
 	argv += optind;