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/networking/arping.c b/networking/arping.c
index 4f207ea..46bd65e 100644
--- a/networking/arping.c
+++ b/networking/arping.c
@@ -295,8 +295,8 @@
 		/* Dad also sets quit_on_reply.
 		 * Advert also sets unsolicited.
 		 */
-		opt_complementary = "=1:Df:AU:c+";
-		opt = getopt32(argv, "DUAqfbc:w:I:s:",
+		opt_complementary = "=1:Df:AU";
+		opt = getopt32(argv, "DUAqfbc:+w:I:s:",
 				&count, &str_timeout, &device, &source);
 		if (opt & 0x80) /* -w: timeout */
 			timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
diff --git a/networking/ftpd.c b/networking/ftpd.c
index 360d1e6..4cbb9b6 100644
--- a/networking/ftpd.c
+++ b/networking/ftpd.c
@@ -1130,11 +1130,11 @@
 	abs_timeout = 1 * 60 * 60;
 	verbose_S = 0;
 	G.timeout = 2 * 60;
-	opt_complementary = "t+:T+:vv:SS";
+	opt_complementary = "vv:SS";
 #if BB_MMU
-	opts = getopt32(argv,    "vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
+	opts = getopt32(argv,    "vS" IF_FEATURE_FTP_WRITE("w") "t:+T:+", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
 #else
-	opts = getopt32(argv, "l1AvS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
+	opts = getopt32(argv, "l1AvS" IF_FEATURE_FTP_WRITE("w") "t:+T:+", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
 	if (opts & (OPT_l|OPT_1)) {
 		/* Our secret backdoor to ls */
 /* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */
diff --git a/networking/ifplugd.c b/networking/ifplugd.c
index f0defb5..28c49e2 100644
--- a/networking/ifplugd.c
+++ b/networking/ifplugd.c
@@ -107,9 +107,9 @@
 #endif
 };
 #if ENABLE_FEATURE_PIDFILE
-# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk"
+# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:Mk"
 #else
-# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
+# define OPTION_STR "+ansfFi:r:It:+u:+d:+m:pqlx:M"
 #endif
 
 enum { // interface status
@@ -560,7 +560,6 @@
 
 	INIT_G();
 
-	opt_complementary = "t+:u+:d+";
 	opts = getopt32(argv, OPTION_STR,
 		&G.iface, &G.script_name, &G.poll_time, &G.delay_up,
 		&G.delay_down, &G.api_mode, &G.extra_arg);
diff --git a/networking/inetd.c b/networking/inetd.c
index 8d44b51..f9295e3 100644
--- a/networking/inetd.c
+++ b/networking/inetd.c
@@ -1153,8 +1153,8 @@
 	if (real_uid != 0) /* run by non-root user */
 		config_filename = NULL;
 
-	opt_complementary = "R+:q+"; /* -q N, -R N */
-	opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
+	/* -q N, -R N */
+	opt = getopt32(argv, "R:+feq:+", &max_concurrency, &global_queuelen);
 	argv += optind;
 	//argc -= optind;
 	if (argv[0])
diff --git a/networking/nc_bloaty.c b/networking/nc_bloaty.c
index 471ae1a..192e42f 100644
--- a/networking/nc_bloaty.c
+++ b/networking/nc_bloaty.c
@@ -794,8 +794,8 @@
  e_found:
 
 	// -g -G -t -r deleted, unimplemented -a deleted too
-	opt_complementary = "?2:vv:ll:w+"; /* max 2 params; -v and -l are counters; -w N */
-	getopt32(argv, "np:s:uvw:" IF_NC_SERVER("lk")
+	opt_complementary = "?2:vv:ll"; /* max 2 params; -v and -l are counters; -w N */
+	getopt32(argv, "np:s:uvw:+" IF_NC_SERVER("lk")
 			IF_NC_EXTRA("i:o:z"),
 			&str_p, &str_s, &o_wait
 			IF_NC_EXTRA(, &str_i, &str_o), &o_verbose IF_NC_SERVER(, &cnt_l));
diff --git a/networking/ntpd.c b/networking/ntpd.c
index 8e71750..130cef0 100644
--- a/networking/ntpd.c
+++ b/networking/ntpd.c
@@ -2197,11 +2197,11 @@
 
 	/* Parse options */
 	peers = NULL;
-	opt_complementary = "dd:p::wn"         /* -d: counter; -p: list; -w implies -n */
+	opt_complementary = "dd:wn"  /* -d: counter; -p: list; -w implies -n */
 		IF_FEATURE_NTPD_SERVER(":Il"); /* -I implies -l */
 	opts = getopt32(argv,
 			"nqNx" /* compat */
-			"wp:S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
+			"wp:*S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
 			IF_FEATURE_NTPD_SERVER("I:") /* compat */
 			"d" /* compat */
 			"46aAbgL", /* compat, ignored */
diff --git a/networking/ping.c b/networking/ping.c
index d8767a3..82d5b7a 100644
--- a/networking/ping.c
+++ b/networking/ping.c
@@ -341,7 +341,7 @@
 
 /* Full(er) version */
 
-#define OPT_STRING ("qvc:s:t:w:W:I:np:4" IF_PING6("6"))
+#define OPT_STRING ("qvc:+s:t:+w:+W:+I:np:4" IF_PING6("6"))
 enum {
 	OPT_QUIET = 1 << 0,
 	OPT_VERBOSE = 1 << 1,
@@ -865,7 +865,7 @@
 	INIT_G();
 
 	/* exactly one argument needed; -v and -q don't mix; -c NUM, -t NUM, -w NUM, -W NUM */
-	opt_complementary = "=1:q--v:v--q:c+:t+:w+:W+";
+	opt_complementary = "=1:q--v:v--q";
 	opt |= getopt32(argv, OPT_STRING, &pingcount, &str_s, &opt_ttl, &deadline, &timeout, &str_I, &str_p);
 	if (opt & OPT_s)
 		datalen = xatou16(str_s); // -s
diff --git a/networking/tcpudp.c b/networking/tcpudp.c
index 31bc704..fbd1f1c 100644
--- a/networking/tcpudp.c
+++ b/networking/tcpudp.c
@@ -232,9 +232,9 @@
 	tcp = (applet_name[0] == 't');
 
 	/* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
-	opt_complementary = "-3:i--i:ph:vv:b+:c+";
+	opt_complementary = "-3:i--i:ph:vv";
 #ifdef SSLSVD
-	opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
+	opts = getopt32(argv, "+c:+C:i:x:u:l:Eb:+hpt:vU:/:Z:K:",
 		&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
 		&backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
 	);
diff --git a/networking/telnetd.c b/networking/telnetd.c
index 13c36aa..2fbdc3b 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -496,12 +496,12 @@
 	INIT_G();
 
 	/* -w NUM, and implies -F. -w and -i don't mix */
-	IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
+	IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:i--w:w--i";)
 	/* Even if !STANDALONE, we accept (and ignore) -i, thus people
 	 * don't need to guess whether it's ok to pass -i to us */
 	opt = getopt32(argv, "f:l:Ki"
 			IF_FEATURE_TELNETD_STANDALONE("p:b:F")
-			IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
+			IF_FEATURE_TELNETD_INETD_WAIT("Sw:+"),
 			&G.issuefile, &G.loginpath
 			IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
 			IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
diff --git a/networking/traceroute.c b/networking/traceroute.c
index eee4f88..e43a36d 100644
--- a/networking/traceroute.c
+++ b/networking/traceroute.c
@@ -294,7 +294,7 @@
 
 #define OPT_STRING \
 	"FIlnrdvxt:i:m:p:q:s:w:z:f:" \
-	IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:") \
+	IF_FEATURE_TRACEROUTE_SOURCE_ROUTE("g:*") \
 	"4" IF_TRACEROUTE6("6")
 enum {
 	OPT_DONT_FRAGMNT = (1 << 0),    /* F */
@@ -819,7 +819,7 @@
 	INIT_G();
 
 	/* minimum 1 arg */
-	opt_complementary = "-1:x-x" IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(":g::");
+	opt_complementary = "-1:x-x";
 	op |= getopt32(argv, OPT_STRING
 		, &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
 		, &source, &waittime_str, &pausemsecs_str, &first_ttl_str
diff --git a/networking/udhcp/d6_dhcpc.c b/networking/udhcp/d6_dhcpc.c
index 12f8f11..6ff040d 100644
--- a/networking/udhcp/d6_dhcpc.c
+++ b/networking/udhcp/d6_dhcpc.c
@@ -944,9 +944,9 @@
 
 	/* Parse command line */
 	/* O,x: list; -T,-t,-A take numeric param */
-	opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
+	IF_UDHCP_VERBOSE(opt_complementary = "vv";)
 	IF_LONG_OPTS(applet_long_options = udhcpc6_longopts;)
-	opt = getopt32(argv, "i:np:qRr:s:T:t:SA:O:ox:f"
+	opt = getopt32(argv, "i:np:qRr:s:T:+t:+SA:+O:*ox:*f"
 		USE_FOR_MMU("b")
 		///IF_FEATURE_UDHCPC_ARPING("a")
 		IF_FEATURE_UDHCP_PORT("P:")
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c
index e58acba..8a16e98 100644
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
@@ -1283,9 +1283,9 @@
 
 	/* Parse command line */
 	/* O,x: list; -T,-t,-A take numeric param */
-	opt_complementary = "O::x::T+:t+:A+" IF_UDHCP_VERBOSE(":vv") ;
+	IF_UDHCP_VERBOSE(opt_complementary = "vv";)
 	IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
-	opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB"
+	opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB"
 		USE_FOR_MMU("b")
 		IF_FEATURE_UDHCPC_ARPING("a::")
 		IF_FEATURE_UDHCP_PORT("P:")
diff --git a/networking/wget.c b/networking/wget.c
index 28c1254..37950ed 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -1268,9 +1268,8 @@
 	applet_long_options = wget_longopts;
 #endif
 	opt_complementary = "-1" /* at least one URL */
-		IF_FEATURE_WGET_TIMEOUT(":T+") /* -T NUM */
 		IF_FEATURE_WGET_LONG_OPTIONS(":\xff::"); /* --header is a list */
-	getopt32(argv, "csqO:P:Y:U:T:"
+	getopt32(argv, "csqO:P:Y:U:T:+"
 		/*ignored:*/ "t:"
 		/*ignored:*/ "n::"
 		/* wget has exactly four -n<letter> opts, all of which we can ignore:
diff --git a/networking/whois.c b/networking/whois.c
index 6ba8dfd..c9dfcf5 100644
--- a/networking/whois.c
+++ b/networking/whois.c
@@ -167,8 +167,8 @@
 	int port = 43;
 	const char *host = "whois.iana.org";
 
-	opt_complementary = "-1:p+";
-	getopt32(argv, "ih:p:", &host, &port);
+	opt_complementary = "-1";
+	getopt32(argv, "ih:p:+", &host, &port);
 	argv += optind;
 
 	do {