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