rename bb_default_error_retval -> xfunc_error_retval
[oweals/busybox.git] / libbb / getopt_ulflags.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * universal getopt_ulflags implementation for busybox
4  *
5  * Copyright (C) 2003-2005  Vladimir Oleynik  <dzo@simtreas.ru>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 #include "libbb.h"
11 #include <getopt.h>
12
13 /*      Documentation
14
15 unsigned long
16 bb_getopt_ulflags(int argc, char **argv, const char *applet_opts, ...)
17
18         The command line options must be declared in const char
19         *applet_opts as a string of chars, for example:
20
21         flags = bb_getopt_ulflags(argc, argv, "rnug");
22
23         If one of the given options is found, a flag value is added to
24         the return value (an unsigned long).
25
26         The flag value is determined by the position of the char in
27         applet_opts string.  For example, in the above case:
28
29         flags = bb_getopt_ulflags(argc, argv, "rnug");
30
31         "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)
35
36         and so on.  You can also look at the return value as a bit
37         field and each option sets one bit.
38
39         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
46  ":"    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:
49
50         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;
54
55         flags = bb_getopt_ulflags(argc, argv, "a:b:c:d:",
56                         &pointer_to_arg_for_a, &pointer_to_arg_for_b,
57                         &pointer_to_arg_for_c, &pointer_to_arg_for_d);
58
59         The type of the pointer (char* or llist_t*) may be controlled
60         by the "::" special separator that is set in the external string
61         bb_opt_complementally (see below for more info).
62
63  "::"   If option can have an *optional* argument, then add a "::"
64         after its char in applet_opts and provide a pointer to store
65         the argument.  Note that optional arguments _must_
66         immediately follow the option: -oparam, not -o param.
67
68  "+"    If the first character in the applet_opts string is a plus,
69         then option processing will stop as soon as a non-option is
70         encountered in the argv array.  Useful for applets like env
71         which should not process arguments to subprograms:
72         env -i ls -d /
73         Here we want env to process just the '-i', not the '-d'.
74
75 const struct option *bb_applet_long_options
76
77         This struct allows you to define long options.  The syntax for
78         declaring the array is just like that of getopt's longopts.
79         (see getopt(3))
80
81         static const struct option applet_long_options[] = {
82                 //name,has_arg,flag,val 
83                 { "verbose", 0, 0, 'v' },
84                 { 0, 0, 0, 0 }
85         };
86         bb_applet_long_options = applet_long_options;
87
88         The last member of struct option (val) typically is set to
89         matching short option from applet_opts. If there is no matching
90         char in applet_opts, then:
91         - return bit have next position after short options
92         - if has_arg is not "no_argument", use ptr for arg also
93         - bb_opt_complementally affects it too
94
95         Note: a good applet will make long options configurable via the
96         config process and not a required feature.  The current standard
97         is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
98
99 const char *bb_opt_complementally
100
101         this should be bb_opt_complementary, but we'll just keep it as
102         bb_opt_complementally due to the Russian origins
103
104  ":"    The colon (":") is used to separate groups of two or more chars
105         and/or groups of chars and special characters (stating some
106         conditions to be checked).
107
108  "abc"  If groups of two or more chars are specified, the first char
109         is the main option and the other chars are secondary options.
110         Their flags will be turned on if the main option is found even
111         if they are not specifed on the command line.  For example:
112
113         bb_opt_complementally = "abc";
114         flags = bb_getopt_ulflags(argc, argv, "abcd")
115
116         If getopt() finds "-a" on the command line, then
117         bb_getopt_ulflags's return value will be as if "-a -b -c" were
118         found.
119
120  "ww"   Adjacent double options have a counter associated which indicates
121         the number of occurences of the option.
122         For example the ps applet needs:
123         if w is given once, GNU ps sets the width to 132,
124         if w is given more than once, it is "unlimited"
125
126         int w_counter = 0;
127         bb_opt_complementally = "ww";
128         bb_getopt_ulflags(argc, argv, "w", &w_counter);
129         if (w_counter)
130                 width = (w_counter == 1) ? 132 : INT_MAX;
131         else
132                 get_terminal_width(...&width...);
133
134         w_counter is a pointer to an integer. It has to be passed to
135         bb_getopt_ulflags() after all other option argument sinks.
136
137         For example: accept multiple -v to indicate the level of verbosity
138         and for each -b optarg, add optarg to my_b. Finally, if b is given,
139         turn off c and vice versa:
140
141         llist_t *my_b = NULL;
142         int verbose_level = 0;
143         bb_opt_complementally = "vv:b::b-c:c-b";
144         f = bb_getopt_ulflags(argc, argv, "vb:c", &my_b, &verbose_level);
145         if (f & 2)       // -c after -b unsets -b flag
146                 while (my_b) { dosomething_with(my_b->data); my_b = my_b->link; }
147         if (my_b)        // but llist is stored if -b is specified
148                 free_llist(my_b);
149         if (verbose_level) bb_printf("verbose level is %d\n", verbose_level);
150
151 Special characters:
152
153  "-"    A dash between two options causes the second of the two
154         to be unset (and ignored) if it is given on the command line.
155
156         [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
157
158         For example:
159         The du applet has the options "-s" and "-d depth".  If
160         bb_getopt_ulflags finds -s, then -d is unset or if it finds -d
161         then -s is unset.  (Note:  busybox implements the GNU
162         "--max-depth" option as "-d".)  To obtain this behavior, you
163         set bb_opt_complementally = "s-d:d-s".  Only one flag value is
164         added to bb_getopt_ulflags's return value depending on the
165         position of the options on the command line.  If one of the
166         two options requires an argument pointer (":" in applet_opts
167         as in "d:") optarg is set accordingly.
168
169         char *smax_print_depth;
170
171         bb_opt_complementally = "s-d:d-s:x-x";
172         opt = bb_getopt_ulflags(argc, argv, "sd:x", &smax_print_depth);
173
174         if (opt & 2)
175                 max_print_depth = atoi(smax_print_depth);
176         if (opt & 4)
177                 printf("Detected odd -x usage\n");
178
179  "-"    A dash as the first char in a bb_opt_complementally group forces
180         all arguments to be treated as options, even if they have
181         no leading dashes. Next char in this case can't be a digit (0-9),
182         use ':' or end of line. For example:
183
184         bb_opt_complementally = "-:w-x:x-w";
185         bb_getopt_ulflags(argc, argv, "wx");
186
187         Allows any arguments to be given without a dash (./program w x)
188         as well as with a dash (./program -x).
189
190  "-N"   A dash as the first char in a bb_opt_complementally group followed
191         by a single digit (0-9) means that at least N non-option
192         arguments must be present on the command line
193
194  "V-"   An option with dash before colon or end-of-line results in
195         bb_show_usage being called if this option is encountered.
196         This is typically used to implement "print verbose usage message
197         and exit" option.
198
199  "--"   A double dash between two options, or between an option and a group
200         of options, means that they are mutually exclusive.  Unlike
201         the "-" case above, an error will be forced if the options
202         are used together.
203
204         For example:
205         The cut applet must have only one type of list specified, so
206         -b, -c and -f are mutally exclusive and should raise an error
207         if specified together.  In this case you must set
208         bb_opt_complementally = "b--cf:c--bf:f--bc".  If two of the
209         mutually exclusive options are found, bb_getopt_ulflags's
210         return value will have the error flag set (BB_GETOPT_ERROR) so
211         that we can check for it:
212
213         if (flags & BB_GETOPT_ERROR)
214                 bb_show_usage();
215
216  "x--x" Variation of the above, it means that -x option should occur
217         at most once.
218
219  "?"    A "?" as the first char in a bb_opt_complementally group means:
220         if BB_GETOPT_ERROR is detected, don't return, call bb_show_usage
221         and exit instead. Next char after '?' can't be a digit.
222
223  "?N"   A "?" as the first char in a bb_opt_complementally group followed
224         by a single digit (0-9) means that at most N arguments must be present
225         on the command line.
226
227  "::"   A double colon after a char in bb_opt_complementally means that the
228         option can occur multiple times. Each occurrence will be saved as
229         a llist_t element instead of char*.
230
231         For example:
232         The grep applet can have one or more "-e pattern" arguments.
233         In this case you should use bb_getopt_ulflags() as follows:
234
235         llist_t *patterns = NULL;
236
237         (this pointer must be initializated to NULL if the list is empty
238         as required by *llist_add_to(llist_t *old_head, char *new_item).)
239
240         bb_opt_complementally = "e::";
241
242         bb_getopt_ulflags(argc, argv, "e:", &patterns);
243         $ grep -e user -e root /etc/passwd
244         root:x:0:0:root:/root:/bin/bash
245         user:x:500:500::/home/user:/bin/bash
246
247  "--"   A double dash at the beginning of bb_opt_complementally means the
248         argv[1] string should always be treated as options, even if it isn't
249         prefixed with a "-".  This is useful for special syntax in applets
250         such as "ar" and "tar":
251         tar xvf foo.tar
252
253  "?"    An "?" between an option and a group of options means that
254         at least one of them is required to occur if the first option
255         occurs in preceding command line arguments.
256
257         For example from "id" applet:
258
259         // Don't allow -n -r -rn -ug -rug -nug -rnug
260         bb_opt_complementally = "r?ug:n?ug:?u--g:g--u";
261         flags = bb_getopt_ulflags(argc, argv, "rnug");
262
263         This example allowed only:
264         $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
265
266  "X"    A bb_opt_complementally group with just a single letter means
267         that this option is required. If more than one such group exists,
268         at least one option is required to occur (not all of them).
269         For example from "start-stop-daemon" applet:
270
271         // Don't allow -KS -SK, but -S or -K is required
272         bb_opt_complementally = "K:S:?K--S:S--K";
273         flags = bb_getopt_ulflags(argc, argv, "KS...);
274
275
276         Don't forget to use ':'. For example, "?322-22-23X-x-a"
277         is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" -
278         max 3 args; count uses of '-2'; min 2 args; if there is
279         a '-2' option then unset '-3', '-X' and '-a'; if there is
280         a '-2' and after it a '-x' then error out.
281 */
282
283 /* this should be bb_opt_complementary, but we'll just keep it as
284    bb_opt_complementally due to the Russian origins */
285 const char *bb_opt_complementally;
286
287 typedef struct {
288         int opt;
289         int list_flg;
290         unsigned long switch_on;
291         unsigned long switch_off;
292         unsigned long incongruously;
293         unsigned long requires;
294         void **optarg;               /* char **optarg or llist_t **optarg */
295         int *counter;
296 } t_complementally;
297
298 /* You can set bb_applet_long_options for parse called long options */
299 #if ENABLE_GETOPT_LONG
300 static const struct option bb_default_long_options[] = {
301 /*      { "help", 0, NULL, '?' }, */
302         { 0, 0, 0, 0 }
303 };
304
305 const struct option *bb_applet_long_options = bb_default_long_options;
306 #endif
307
308 unsigned long
309 bb_getopt_ulflags(int argc, char **argv, const char *applet_opts, ...)
310 {
311         unsigned long flags = 0;
312         unsigned long requires = 0;
313         t_complementally complementally[sizeof(flags) * 8 + 1];
314         int c;
315         const unsigned char *s;
316         t_complementally *on_off;
317         va_list p;
318 #if ENABLE_GETOPT_LONG
319         const struct option *l_o;
320 #endif
321         unsigned long trigger;
322         char **pargv = NULL;
323         int min_arg = 0;
324         int max_arg = -1;
325
326 #define SHOW_USAGE_IF_ERROR     1
327 #define ALL_ARGV_IS_OPTS        2
328 #define FIRST_ARGV_IS_OPT       4
329 #define FREE_FIRST_ARGV_IS_OPT  8
330         int spec_flgs = 0;
331
332         va_start(p, applet_opts);
333
334         c = 0;
335         on_off = complementally;
336         memset(on_off, 0, sizeof(complementally));
337
338         /* skip GNU extension */
339         s = (const unsigned char *)applet_opts;
340         if (*s == '+' || *s == '-')
341                 s++;
342         for (; *s; s++) {
343                 if (c >= (int)(sizeof(flags)*8))
344                         break;
345                 on_off->opt = *s;
346                 on_off->switch_on = (1 << c);
347                 if (s[1] == ':') {
348                         on_off->optarg = va_arg(p, void **);
349                         do
350                                 s++;
351                         while (s[1] == ':');
352                 }
353                 on_off++;
354                 c++;
355         }
356
357 #if ENABLE_GETOPT_LONG
358         for (l_o = bb_applet_long_options; l_o->name; l_o++) {
359                 if (l_o->flag)
360                         continue;
361                 for (on_off = complementally; on_off->opt != 0; on_off++)
362                         if (on_off->opt == l_o->val)
363                                 break;
364                 if (on_off->opt == 0) {
365                         if (c >= (int)(sizeof(flags)*8))
366                                 break;
367                         on_off->opt = l_o->val;
368                         on_off->switch_on = (1 << c);
369                         if (l_o->has_arg != no_argument)
370                                 on_off->optarg = va_arg(p, void **);
371                         c++;
372                 }
373         }
374 #endif /* ENABLE_GETOPT_LONG */
375         for (s = (const unsigned char *)bb_opt_complementally; s && *s; s++) {
376                 t_complementally *pair;
377                 unsigned long *pair_switch;
378
379                 if (*s == ':')
380                         continue;
381                 c = s[1];
382                 if (*s == '?') {
383                         if (c < '0' || c > '9') {
384                                 spec_flgs |= SHOW_USAGE_IF_ERROR;
385                         } else {
386                                 max_arg = c - '0';
387                                 s++;
388                         }
389                         continue;
390                 }
391                 if (*s == '-') {
392                         if (c < '0' || c > '9') {
393                                 if (c == '-') {
394                                         spec_flgs |= FIRST_ARGV_IS_OPT;
395                                         s++;
396                                 } else
397                                         spec_flgs |= ALL_ARGV_IS_OPTS;
398                         } else {
399                                 min_arg = c - '0';
400                                 s++;
401                         }
402                         continue;
403                 }
404                 for (on_off = complementally; on_off->opt; on_off++)
405                         if (on_off->opt == *s)
406                                 break;
407                 if (c == ':' && s[2] == ':') {
408                         on_off->list_flg++;
409                         continue;
410                 }
411                 if (c == ':' || c == '\0') {
412                         requires |= on_off->switch_on;
413                         continue;
414                 }
415                 if (c == '-' && (s[2] == ':' || s[2] == '\0')) {
416                         flags |= on_off->switch_on;
417                         on_off->incongruously |= on_off->switch_on;
418                         s++;
419                         continue;
420                 }
421                 if (c == *s) {
422                         on_off->counter = va_arg(p, int *);
423                         s++;
424                 }
425                 pair = on_off;
426                 pair_switch = &(pair->switch_on);
427                 for (s++; *s && *s != ':'; s++) {
428                         if (*s == '?') {
429                                 pair_switch = &(pair->requires);
430                         } else if (*s == '-') {
431                                 if (pair_switch == &(pair->switch_off))
432                                         pair_switch = &(pair->incongruously);
433                                 else
434                                         pair_switch = &(pair->switch_off);
435                         } else {
436                                 for (on_off = complementally; on_off->opt; on_off++)
437                                         if (on_off->opt == *s) {
438                                                 *pair_switch |= on_off->switch_on;
439                                                 break;
440                                         }
441                         }
442                 }
443                 s--;
444         }
445         va_end (p);
446
447 #if ENABLE_AR || ENABLE_TAR
448         if (spec_flgs & FIRST_ARGV_IS_OPT) {
449                 if (argv[1] && argv[1][0] != '-' && argv[1][0] != '\0') {
450                         argv[1] = xasprintf("-%s", argv[1]);
451                         if (ENABLE_FEATURE_CLEAN_UP)
452                                 spec_flgs |= FREE_FIRST_ARGV_IS_OPT;
453                 }
454         }
455 #endif
456 #if ENABLE_GETOPT_LONG
457         while ((c = getopt_long(argc, argv, applet_opts,
458                                  bb_applet_long_options, NULL)) >= 0) {
459 #else
460         while ((c = getopt(argc, argv, applet_opts)) >= 0) {
461 #endif /* ENABLE_GETOPT_LONG */
462 loop_arg_is_opt:
463                 for (on_off = complementally; on_off->opt != c; on_off++) {
464                         /* c==0 if long opt have non NULL flag */
465                         if (on_off->opt == 0 && c != 0)
466                                 bb_show_usage();
467                 }
468                 if (flags & on_off->incongruously) {
469                         if ((spec_flgs & SHOW_USAGE_IF_ERROR))
470                                 bb_show_usage();
471                         flags |= BB_GETOPT_ERROR;
472                 }
473                 trigger = on_off->switch_on & on_off->switch_off;
474                 flags &= ~(on_off->switch_off ^ trigger);
475                 flags |= on_off->switch_on ^ trigger;
476                 flags ^= trigger;
477                 if (on_off->counter)
478                         (*(on_off->counter))++;
479                 if (on_off->list_flg) {
480                         llist_add_to((llist_t **)(on_off->optarg), optarg);
481                 } else if (on_off->optarg) {
482                         *(char **)(on_off->optarg) = optarg;
483                 }
484                 if (pargv != NULL)
485                         break;
486         }
487
488         if (spec_flgs & ALL_ARGV_IS_OPTS) {
489                 /* process argv is option, for example "ps" applet */
490                 if (pargv == NULL)
491                         pargv = argv + optind;
492                 while (*pargv) {
493                         c = **pargv;
494                         if (c == '\0') {
495                                 pargv++;
496                         } else {
497                                 (*pargv)++;
498                                 goto loop_arg_is_opt;
499                         }
500                 }
501         }
502
503 #if (ENABLE_AR || ENABLE_TAR) && ENABLE_FEATURE_CLEAN_UP
504         if (spec_flgs & FREE_FIRST_ARGV_IS_OPT)
505                 free(argv[1]);
506 #endif
507         /* check depending requires for given options */
508         for (on_off = complementally; on_off->opt; on_off++) {
509                 if (on_off->requires && (flags & on_off->switch_on) &&
510                                         (flags & on_off->requires) == 0)
511                         bb_show_usage();
512         }
513         if (requires && (flags & requires) == 0)
514                 bb_show_usage();
515         argc -= optind;
516         if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
517                 bb_show_usage();
518         return flags;
519 }