blob: e0dc1371f1841e7e7e0e7c0c28d6edd437f6ae7b [file] [log] [blame]
Manuel Novoa III cad53642003-03-19 09:13:01 +00001/* vi: set sw=4 ts=4: */
2/*
Eric Andersen8876fb22003-06-20 09:01:58 +00003 * universal getopt_ulflags implementation for busybox
Manuel Novoa III cad53642003-03-19 09:13:01 +00004 *
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +00005 * Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru>
Manuel Novoa III cad53642003-03-19 09:13:01 +00006 *
"Robert P. J. Day"5d8843e2006-07-10 11:41:19 +00007 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Manuel Novoa III cad53642003-03-19 09:13:01 +00008 */
9
Manuel Novoa III cad53642003-03-19 09:13:01 +000010#include "libbb.h"
Rob Landleyd921b2e2006-08-03 15:41:12 +000011#include <getopt.h>
Manuel Novoa III cad53642003-03-19 09:13:01 +000012
Rob Landleyf76cd962006-05-03 21:23:15 +000013/* Documentation
Mike Frysinger2bf88a82005-04-18 22:42:58 +000014
Mike Frysingere5d0bde2005-05-10 23:48:35 +000015unsigned long
Mike Frysinger2bf88a82005-04-18 22:42:58 +000016bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
17
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000018 The command line options must be declared in const char
19 *applet_opts as a string of chars, for example:
Mike Frysinger2bf88a82005-04-18 22:42:58 +000020
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000021 flags = bb_getopt_ulflags(argc, argv, "rnug");
Mike Frysinger2bf88a82005-04-18 22:42:58 +000022
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000023 If one of the given options is found, a flag value is added to
24 the return value (an unsigned long).
Mike Frysinger2bf88a82005-04-18 22:42:58 +000025
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000026 The flag value is determined by the position of the char in
27 applet_opts string. For example, in the above case:
Mike Frysinger2bf88a82005-04-18 22:42:58 +000028
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000029 flags = bb_getopt_ulflags(argc, argv, "rnug");
Mike Frysinger2bf88a82005-04-18 22:42:58 +000030
Rob Landleyf76cd962006-05-03 21:23:15 +000031 "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)
Mike Frysinger2bf88a82005-04-18 22:42:58 +000035
Rob Landleyf76cd962006-05-03 21:23:15 +000036 and so on. You can also look at the return value as a bit
37 field and each option sets one bit.
Mike Frysinger2bf88a82005-04-18 22:42:58 +000038
Denis Vlasenko65dbd872006-09-03 12:27:25 +000039 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
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000046 ":" 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:
Mike Frysinger2bf88a82005-04-18 22:42:58 +000049
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000050 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;
Mike Frysinger2bf88a82005-04-18 22:42:58 +000054
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000055 flags = bb_getopt_ulflags(argc, argv, "a:b:c:d:",
Rob Landleyf76cd962006-05-03 21:23:15 +000056 &pointer_to_arg_for_a, &pointer_to_arg_for_b,
57 &pointer_to_arg_for_c, &pointer_to_arg_for_d);
Mike Frysinger2bf88a82005-04-18 22:42:58 +000058
Rob Landleyf76cd962006-05-03 21:23:15 +000059 The type of the pointer (char* or llist_t*) may be controlled
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +000060 by the "::" special separator that is set in the external string
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000061 bb_opt_complementally (see below for more info).
Mike Frysingere5d0bde2005-05-10 23:48:35 +000062
Mike Frysinger992a58c2006-02-22 22:56:30 +000063 "+" If the first character in the applet_opts string is a plus,
64 then option processing will stop as soon as a non-option is
65 encountered in the argv array. Useful for applets like env
66 which should not process arguments to subprograms:
67 env -i ls -d /
68 Here we want env to process just the '-i', not the '-d'.
69
Rob Landleyf76cd962006-05-03 21:23:15 +000070const struct option *bb_applet_long_options
Mike Frysingere5d0bde2005-05-10 23:48:35 +000071
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000072 This struct allows you to define long options. The syntax for
73 declaring the array is just like that of getopt's longopts.
74 (see getopt(3))
Mike Frysingere5d0bde2005-05-10 23:48:35 +000075
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000076 static const struct option applet_long_options[] = {
Rob Landleyf76cd962006-05-03 21:23:15 +000077 { "verbose", 0, 0, 'v' },
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000078 { 0, 0, 0, 0 }
79 };
80 bb_applet_long_options = applet_long_options;
Mike Frysingere5d0bde2005-05-10 23:48:35 +000081
Rob Landleyf76cd962006-05-03 21:23:15 +000082 The last member of struct option (val) typically is set to
83 matching short option from applet_opts. If there is no matching
84 char in applet_opts, then:
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000085 - return bit have next position after short options
86 - if has_arg is not "no_argument", use ptr for arg also
Rob Landleyf76cd962006-05-03 21:23:15 +000087 - bb_opt_complementally affects it too
Mike Frysingerfb6d22c2005-05-11 00:02:39 +000088
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000089 Note: a good applet will make long options configurable via the
90 config process and not a required feature. The current standard
91 is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
Mike Frysingerfb6d22c2005-05-11 00:02:39 +000092
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000093const char *bb_opt_complementally
Mike Frysinger57f4cb22006-02-21 00:50:37 +000094 this should be bb_opt_complementary, but we'll just keep it as
95 bb_opt_complementally due to the Russian origins
Mike Frysingerfb6d22c2005-05-11 00:02:39 +000096
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +000097 ":" The colon (":") is used to separate groups of two or more chars
98 and/or groups of chars and special characters (stating some
99 conditions to be checked).
Mike Frysingere5d0bde2005-05-10 23:48:35 +0000100
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000101 "abc" If groups of two or more chars are specified, the first char
102 is the main option and the other chars are secondary options.
103 Their flags will be turned on if the main option is found even
104 if they are not specifed on the command line. For example:
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000105
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000106 bb_opt_complementally = "abc";
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000107
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000108 flags = bb_getopt_ulflags(argc, argv, "abcd")
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000109
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000110 If getopt() finds "-a" on the command line, then
111 bb_getopt_ulflags's return value will be as if "-a -b -c" were
112 found.
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000113
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000114 "ww" Adjacent double options have a counter associated which indicates
Rob Landleyf76cd962006-05-03 21:23:15 +0000115 the number of occurences of the option.
"Vladimir N. Oleynik"d1b60782005-10-05 12:44:52 +0000116 For example the ps applet needs:
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000117 if w is given once, GNU ps sets the width to 132,
118 if w is given more than once, it is "unlimited"
119
120 int w_counter = 0;
121 bb_opt_complementally = "ww";
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000122 bb_getopt_ulflags(argc, argv, "w", &w_counter);
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000123
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000124 if(w_counter)
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000125 width = (w_counter == 1) ? 132 : INT_MAX;
126 else
127 get_terminal_width(...&width...);
128
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000129 w_counter is a pointer to an integer. It has to be passed to
130 bb_getopt_ulflags() after all other option argument sinks.
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000131 For example: accept multiple -v to indicate the level of verbosity
132 and for each -b optarg, add optarg to my_b. Finally, if b is given,
133 turn off c and vice versa:
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000134
135 llist_t *my_b = NULL;
136 int verbose_level = 0;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000137 bb_opt_complementally = "vv:b::b-c:c-b";
"Vladimir N. Oleynik"4a5ce082005-10-05 13:58:40 +0000138 f = bb_getopt_ulflags(argc, argv, "vb:c", &my_b, &verbose_level);
Rob Landleyf76cd962006-05-03 21:23:15 +0000139 if((f & 2)) // -c after -b unsets -b flag
140 while(my_b) { dosomething_with(my_b->data) ; my_b = my_b->link; }
141 if(my_b) // but llist is stored if -b is specified
"Vladimir N. Oleynik"4a5ce082005-10-05 13:58:40 +0000142 free_llist(my_b);
Rob Landleyf76cd962006-05-03 21:23:15 +0000143 if(verbose_level) bb_printf("verbose level is %d\n", verbose_level);
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000144
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000145Special characters:
146
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000147 "-" A dash between two options causes the second of the two
Rob Landleyf76cd962006-05-03 21:23:15 +0000148 to be unset (and ignored) if it is given on the command line.
149
150 [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000151
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000152 For example:
153 The du applet has the options "-s" and "-d depth". If
154 bb_getopt_ulflags finds -s, then -d is unset or if it finds -d
155 then -s is unset. (Note: busybox implements the GNU
156 "--max-depth" option as "-d".) To obtain this behavior, you
157 set bb_opt_complementally = "s-d:d-s". Only one flag value is
158 added to bb_getopt_ulflags's return value depending on the
159 position of the options on the command line. If one of the
160 two options requires an argument pointer (":" in applet_opts
161 as in "d:") optarg is set accordingly.
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000162
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000163 char *smax_print_depth;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000164
"Vladimir N. Oleynik"45a8ed82005-09-06 16:08:33 +0000165 bb_opt_complementally = "s-d:d-s:x-x";
166 opt = bb_getopt_ulflags(argc, argv, "sd:x", &smax_print_depth);
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000167
Rob Landleyf76cd962006-05-03 21:23:15 +0000168 if (opt & 2)
169 max_print_depth = atoi(smax_print_depth);
170 if (opt & 4)
171 printf("Detected odd -x usage\n");
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000172
Rob Landleyf76cd962006-05-03 21:23:15 +0000173 "-" A dash as the first char in a bb_opt_complementally group forces
174 all arguments to be treated as options, even if they have
175 no leading dashes. Next char in this case can't be a digit (0-9),
176 use ':' or end of line. For example:
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000177
"Vladimir N. Oleynik"d1b60782005-10-05 12:44:52 +0000178 bb_opt_complementally = "-:w-x:x-w";
179 bb_getopt_ulflags(argc, argv, "wx");
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000180
"Vladimir N. Oleynik"4a5ce082005-10-05 13:58:40 +0000181 Allows any arguments to be given without a dash (./program w x)
Rob Landleyf76cd962006-05-03 21:23:15 +0000182 as well as with a dash (./program -x).
Bernhard Reutner-Fischer43fb3fc2005-10-05 12:23:13 +0000183
Rob Landleyf76cd962006-05-03 21:23:15 +0000184 "-N" A dash as the first char in a bb_opt_complementally group followed
185 by a single digit (0-9) means that at least N non-option
186 arguments must be present on the command line
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000187
Rob Landleyf76cd962006-05-03 21:23:15 +0000188 "V-" An option with dash before colon or end-of-line results in
189 bb_show_usage being called if this option is encountered.
190 This is typically used to implement "print verbose usage message
191 and exit" option.
"Vladimir N. Oleynik"4fc92202006-02-02 14:48:54 +0000192
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000193 "--" A double dash between two options, or between an option and a group
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000194 of options, means that they are mutually exclusive. Unlike
195 the "-" case above, an error will be forced if the options
196 are used together.
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000197
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000198 For example:
199 The cut applet must have only one type of list specified, so
200 -b, -c and -f are mutally exclusive and should raise an error
201 if specified together. In this case you must set
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000202 bb_opt_complementally = "b--cf:c--bf:f--bc". If two of the
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000203 mutually exclusive options are found, bb_getopt_ulflags's
204 return value will have the error flag set (BB_GETOPT_ERROR) so
205 that we can check for it:
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000206
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000207 if (flags & BB_GETOPT_ERROR)
208 bb_show_usage();
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000209
Rob Landleyf76cd962006-05-03 21:23:15 +0000210 "?" A "?" as the first char in a bb_opt_complementally group means:
211 if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage
212 and exit instead. Next char after '?' can't be a digit.
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000213
Rob Landleyf76cd962006-05-03 21:23:15 +0000214 "?N" A "?" as the first char in a bb_opt_complementally group followed
215 by a single digit (0-9) means that at most N arguments must be present
216 on the command line.
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000217
218 "::" A double colon after a char in bb_opt_complementally means that the
Rob Landleyf76cd962006-05-03 21:23:15 +0000219 option can occur multiple times. Each occurrence will be saved as
220 a llist_t element instead of char*.
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000221
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000222 For example:
223 The grep applet can have one or more "-e pattern" arguments.
224 In this case you should use bb_getopt_ulflags() as follows:
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000225
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000226 llist_t *patterns = NULL;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000227
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000228 (this pointer must be initializated to NULL if the list is empty
229 as required by *llist_add_to(llist_t *old_head, char *new_item).)
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000230
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000231 bb_opt_complementally = "e::";
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000232
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000233 bb_getopt_ulflags(argc, argv, "e:", &patterns);
234 $ grep -e user -e root /etc/passwd
235 root:x:0:0:root:/root:/bin/bash
236 user:x:500:500::/home/user:/bin/bash
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000237
Mike Frysingere17c80e2006-02-21 00:37:42 +0000238 "--" A double dash at the beginning of bb_opt_complementally means the
239 argv[1] string should always be treated as options, even if it isn't
240 prefixed with a "-". This is to support the special syntax in applets
241 such as "ar" and "tar":
242 tar xvf foo.tar
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000243
Rob Landleyf76cd962006-05-03 21:23:15 +0000244 "?" An "?" between an option and a group of options means that
245 at least one of them is required to occur if the first option
246 occurs in preceding command line arguments.
247
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000248 For example from "id" applet:
249
250 // Don't allow -n -r -rn -ug -rug -nug -rnug
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000251 bb_opt_complementally = "r?ug:n?ug:?u--g:g--u";
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000252 flags = bb_getopt_ulflags(argc, argv, "rnug");
253
254 This example allowed only:
255 $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
256
Rob Landleyf76cd962006-05-03 21:23:15 +0000257 "X" A bb_opt_complementally group with just a single letter means
258 that this this option is required. If more than one such group exists,
259 at least one option is required to occur (not all of them).
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000260 For example from "start-stop-daemon" applet:
261
Rob Landleyf76cd962006-05-03 21:23:15 +0000262 // Don't allow -KS -SK, but -S or -K is required
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000263 bb_opt_complementally = "K:S:?K--S:S--K";
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000264 flags = bb_getopt_ulflags(argc, argv, "KS...);
265
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000266
267 "x--x" give error if double or more used -x option
268
Rob Landleyf76cd962006-05-03 21:23:15 +0000269 Don't forget to use ':'. For example "?322-22-23X-x-a" is interpreted as
270 "?3:22:-2:2-2:2-3Xa:2--x": max 3 args; count uses of '-2'; min 2 args;
271 if there is a '-2' option then unset '-3', '-X' and '-a'; if there is
272 a '-2' and after it a '-x' then error out.
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000273
Eric Andersen8876fb22003-06-20 09:01:58 +0000274*/
275
Mike Frysingere17c80e2006-02-21 00:37:42 +0000276/* this should be bb_opt_complementary, but we'll just keep it as
277 bb_opt_complementally due to the Russian origins */
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000278const char *bb_opt_complementally;
Eric Andersen8876fb22003-06-20 09:01:58 +0000279
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000280typedef struct {
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000281 int opt;
282 int list_flg;
Eric Andersen8876fb22003-06-20 09:01:58 +0000283 unsigned long switch_on;
284 unsigned long switch_off;
285 unsigned long incongruously;
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000286 unsigned long requires;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000287 void **optarg; /* char **optarg or llist_t **optarg */
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000288 int *counter;
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000289} t_complementally;
Eric Andersen8876fb22003-06-20 09:01:58 +0000290
291/* You can set bb_applet_long_options for parse called long options */
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000292#if ENABLE_GETOPT_LONG
Eric Andersen8876fb22003-06-20 09:01:58 +0000293static const struct option bb_default_long_options[] = {
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000294/* { "help", 0, NULL, '?' }, */
Eric Andersen8876fb22003-06-20 09:01:58 +0000295 { 0, 0, 0, 0 }
296};
297
298const struct option *bb_applet_long_options = bb_default_long_options;
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000299#endif
Eric Andersen8876fb22003-06-20 09:01:58 +0000300
Eric Andersen8876fb22003-06-20 09:01:58 +0000301unsigned long
302bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000303{
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000304 unsigned long flags = 0;
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000305 unsigned long requires = 0;
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000306 t_complementally complementally[sizeof(flags) * 8 + 1];
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000307 int c;
308 const unsigned char *s;
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000309 t_complementally *on_off;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000310 va_list p;
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000311#if ENABLE_GETOPT_LONG
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000312 const struct option *l_o;
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000313#endif
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000314 unsigned long trigger;
"Vladimir N. Oleynik"f01e1782006-01-09 13:28:31 +0000315#ifdef CONFIG_PS
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000316 char **pargv = NULL;
"Vladimir N. Oleynik"f01e1782006-01-09 13:28:31 +0000317#endif
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000318 int min_arg = 0;
319 int max_arg = -1;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000320
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000321#define SHOW_USAGE_IF_ERROR 1
322#define ALL_ARGV_IS_OPTS 2
323#define FIRST_ARGV_IS_OPT 4
324#define FREE_FIRST_ARGV_IS_OPT 8
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000325 int spec_flgs = 0;
326
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000327 va_start (p, applet_opts);
Eric Andersen8876fb22003-06-20 09:01:58 +0000328
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000329 c = 0;
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000330 on_off = complementally;
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000331 memset(on_off, 0, sizeof(complementally));
332
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000333 /* skip GNU extension */
"Vladimir N. Oleynik"bf968f72005-12-02 10:10:28 +0000334 s = (const unsigned char *)applet_opts;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000335 if(*s == '+' || *s == '-')
336 s++;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000337 for (; *s; s++) {
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000338 if(c >= (int)(sizeof(flags)*8))
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000339 break;
340 on_off->opt = *s;
341 on_off->switch_on = (1 << c);
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000342 if (s[1] == ':') {
343 on_off->optarg = va_arg (p, void **);
344 do
345 s++;
346 while (s[1] == ':');
347 }
348 on_off++;
349 c++;
Eric Andersen8876fb22003-06-20 09:01:58 +0000350 }
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000351
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000352#if ENABLE_GETOPT_LONG
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000353 for(l_o = bb_applet_long_options; l_o->name; l_o++) {
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000354 if(l_o->flag)
355 continue;
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000356 for(on_off = complementally; on_off->opt != 0; on_off++)
357 if(on_off->opt == l_o->val)
358 break;
359 if(on_off->opt == 0) {
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000360 if(c >= (int)(sizeof(flags)*8))
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000361 break;
362 on_off->opt = l_o->val;
363 on_off->switch_on = (1 << c);
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000364 if(l_o->has_arg != no_argument)
365 on_off->optarg = va_arg (p, void **);
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000366 c++;
367 }
368 }
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000369#endif /* ENABLE_GETOPT_LONG */
"Vladimir N. Oleynik"bf968f72005-12-02 10:10:28 +0000370 for (s = (const unsigned char *)bb_opt_complementally; s && *s; s++) {
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000371 t_complementally *pair;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000372 unsigned long *pair_switch;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000373
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000374 if (*s == ':')
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000375 continue;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000376 c = s[1];
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000377 if(*s == '?') {
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000378 if(c < '0' || c > '9') {
379 spec_flgs |= SHOW_USAGE_IF_ERROR;
380 } else {
381 max_arg = c - '0';
382 s++;
383 }
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000384 continue;
385 }
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000386 if(*s == '-') {
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000387 if(c < '0' || c > '9') {
388 if(c == '-') {
389 spec_flgs |= FIRST_ARGV_IS_OPT;
390 s++;
391 } else
392 spec_flgs |= ALL_ARGV_IS_OPTS;
393 } else {
394 min_arg = c - '0';
395 s++;
396 }
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000397 continue;
398 }
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000399 for (on_off = complementally; on_off->opt; on_off++)
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000400 if (on_off->opt == *s)
401 break;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000402 if(c == ':' && s[2] == ':') {
403 on_off->list_flg++;
404 continue;
405 }
406 if(c == ':' || c == '\0') {
407 requires |= on_off->switch_on;
408 continue;
409 }
"Vladimir N. Oleynik"4fc92202006-02-02 14:48:54 +0000410 if(c == '-' && (s[2] == ':' || s[2] == '\0')) {
411 flags |= on_off->switch_on;
412 on_off->incongruously |= on_off->switch_on;
413 s++;
414 continue;
415 }
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000416 if(c == *s) {
417 on_off->counter = va_arg (p, int *);
418 s++;
419 }
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000420 pair = on_off;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000421 pair_switch = &(pair->switch_on);
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000422 for(s++; *s && *s != ':'; s++) {
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000423 if(*s == '?') {
424 pair_switch = &(pair->requires);
425 } else if (*s == '-') {
426 if(pair_switch == &(pair->switch_off))
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000427 pair_switch = &(pair->incongruously);
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000428 else
429 pair_switch = &(pair->switch_off);
430 } else {
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000431 for (on_off = complementally; on_off->opt; on_off++)
432 if (on_off->opt == *s) {
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000433 *pair_switch |= on_off->switch_on;
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000434 break;
435 }
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000436 }
437 }
438 s--;
Eric Andersen8876fb22003-06-20 09:01:58 +0000439 }
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000440 va_end (p);
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000441
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000442#if defined(CONFIG_AR) || defined(CONFIG_TAR)
443 if((spec_flgs & FIRST_ARGV_IS_OPT)) {
444 if(argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') {
Rob Landleyd921b2e2006-08-03 15:41:12 +0000445 argv[1] = xasprintf("-%s", argv[1]);
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000446 if(ENABLE_FEATURE_CLEAN_UP)
447 spec_flgs |= FREE_FIRST_ARGV_IS_OPT;
448 }
449 }
450#endif
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000451#if ENABLE_GETOPT_LONG
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000452 while ((c = getopt_long (argc, argv, applet_opts,
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000453 bb_applet_long_options, NULL)) >= 0) {
Bernhard Reutner-Fischerf9437aa2006-05-31 14:12:51 +0000454#else
455 while ((c = getopt (argc, argv, applet_opts)) >= 0) {
456#endif /* ENABLE_GETOPT_LONG */
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000457#ifdef CONFIG_PS
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000458loop_arg_is_opt:
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000459#endif
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000460 for (on_off = complementally; on_off->opt != c; on_off++) {
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000461 /* c==0 if long opt have non NULL flag */
462 if(on_off->opt == 0 && c != 0)
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000463 bb_show_usage ();
464 }
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000465 if(flags & on_off->incongruously) {
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000466 if((spec_flgs & SHOW_USAGE_IF_ERROR))
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000467 bb_show_usage ();
Mike Frysinger348e84c2005-05-11 00:39:03 +0000468 flags |= BB_GETOPT_ERROR;
"Vladimir N. Oleynik"27421a12005-09-05 14:46:07 +0000469 }
"Vladimir N. Oleynik"45a8ed82005-09-06 16:08:33 +0000470 trigger = on_off->switch_on & on_off->switch_off;
471 flags &= ~(on_off->switch_off ^ trigger);
472 flags |= on_off->switch_on ^ trigger;
473 flags ^= trigger;
"Vladimir N. Oleynik"be0ed3d2005-10-04 16:48:26 +0000474 if(on_off->counter)
475 (*(on_off->counter))++;
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000476 if(on_off->list_flg) {
Rob Landley8bb50782006-05-26 23:44:51 +0000477 llist_add_to((llist_t **)(on_off->optarg), optarg);
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000478 } else if (on_off->optarg) {
479 *(char **)(on_off->optarg) = optarg;
480 }
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000481#ifdef CONFIG_PS
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000482 if(pargv != NULL)
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000483 break;
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000484#endif
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000485 }
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000486
487#ifdef CONFIG_PS
488 if((spec_flgs & ALL_ARGV_IS_OPTS)) {
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000489 /* process argv is option, for example "ps" applet */
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000490 if(pargv == NULL)
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000491 pargv = argv + optind;
"Vladimir N. Oleynik"35939d92005-10-05 10:52:47 +0000492 while(*pargv) {
493 c = **pargv;
494 if(c == '\0') {
495 pargv++;
496 } else {
497 (*pargv)++;
498 goto loop_arg_is_opt;
499 }
500 }
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000501 }
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000502#endif
503
504#if (defined(CONFIG_AR) || defined(CONFIG_TAR)) && \
505 defined(CONFIG_FEATURE_CLEAN_UP)
506 if((spec_flgs & FREE_FIRST_ARGV_IS_OPT))
507 free(argv[1]);
508#endif
"Vladimir N. Oleynik"064f04e2005-10-11 14:38:01 +0000509 /* check depending requires for given options */
510 for (on_off = complementally; on_off->opt; on_off++) {
511 if(on_off->requires && (flags & on_off->switch_on) &&
512 (flags & on_off->requires) == 0)
513 bb_show_usage ();
514 }
515 if(requires && (flags & requires) == 0)
516 bb_show_usage ();
"Vladimir N. Oleynik"f704b272005-10-14 09:56:52 +0000517 argc -= optind;
518 if(argc < min_arg || (max_arg >= 0 && argc > max_arg))
519 bb_show_usage ();
Mike Frysinger2bf88a82005-04-18 22:42:58 +0000520 return flags;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000521}