fix printf warning
[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  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 bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
32
33           The command line options must be declared in const char
34           *applet_opts as a string of chars, for example:
35
36           flags = bb_getopt_ulflags(argc, argv, "rnug");
37
38           If one of the given options is found a flag value is added to
39           the unsigned long returned by bb_getopt_ulflags.
40
41           The value of this flag is given by the position of the char in
42           const char *applet_opts so for example in this case:
43
44           flags = bb_getopt_ulflags(argc, argv, "rnug");
45
46           "r" will add 1
47           "n" will add 2
48           "u  will add 4
49           "g" will add 8
50
51            and so on.
52
53            If an argument is required by one of the options add a ":"
54            after the char in const char *applet_opts and provide a pointer
55            where the arg could be stored if it is found, for example:
56
57            char *pointer_to_arg_for_a;
58            char *pointer_to_arg_for_b;
59            char *pointer_to_arg_for_c;
60            char *pointer_to_arg_for_d;
61
62            flags = bb_getopt_ulflags(argc, argv, "a:b:c:d:",
63                             &pointer_to_arg_for_a, &pointer_to_arg_for_b,
64                             &pointer_to_arg_for_c, &pointer_to_arg_for_d);
65
66            The type of the pointer (char* or llist_t *) can be influenced
67            by the "*" special character that can be set in const char
68            *bb_opt_complementaly (see below).
69
70 const char *bb_opt_complementaly
71
72    ":"     The colon (":") is used in bb_opt_complementaly as separator
73            between groups of two or more chars and/or groups of chars and
74            special characters (stating some conditions to be checked).
75
76    "abc"   If groups of two or more chars are specified the first char
77            is the main option and the other chars are secondary options
78            whose flags will be turned on if the main option is found even
79            if they are not specifed on the command line, for example:
80
81            bb_opt_complementaly = "abc";
82
83            flags = bb_getopt_ulflags(argc, argv, "abcd")
84
85            If getopt() finds "-a" on the command line, then
86            bb_getopt_ulflags's return value will be as if "-a -b -c" were
87            found.
88
89 Special characters:
90
91    "-"     A dash between two options causes the second of the two
92            to be unset (and ignored) if it is given on the command line.
93
94            For example:
95            The du applet can have the options "-s" and "-d depth", if
96            bb_getopt_ulflags finds -s then -d is unset or if it finds -d
97            then -s is unset.  (Note:  busybox implements the GNU
98            "--max-depth" option as "-d".)  In this case bb_getopt_ulflags's
99            return value has no error flag set (0x80000000UL).  To achieve
100            this result you must set bb_opt_complementaly = "s-d:d-s".
101            Only one flag value is added to bb_getopt_ulflags's return
102            value depending on the position of the options on the command
103            line.  If one of the two options requires an argument pointer
104            (":" in const char *applet_opts as in "d:") optarg is set
105            accordingly.
106
107            char *smax_print_depth;
108
109            bb_opt_complementaly = "s-d:d-s";
110            opt = bb_getopt_ulflags(argc, argv, "sd:" , &smax_print_depth);
111
112            if (opt & 2) {
113                     max_print_depth = bb_xgetularg10_bnd(smax_print_depth,
114                                 0, INT_MAX);
115            }
116
117    "~"     A tilde between two options or between an option and a group
118            of options means that they are mutually exclusive.  Unlike
119            the "-" case above, an error will be forced if the options
120            are used together.
121
122            For example:
123            The cut applet must have only one type of list specified, so
124            -b, -c and -f are mutally exclusive and should raise an error
125            if specified together.  In this case you must set
126            bb_opt_complementaly = "b~cf:c~bf:f~bc".  If two of the
127            mutually exclusive options are found, bb_getopt_ulflags's
128            return value will have the error flag set (0x80000000UL) so
129            that we can check for it:
130
131            if ((flags & 0x80000000UL)
132                    bb_show_usage();
133
134    "*"     A star after a char in bb_opt_complementaly means that the
135            option can occur multiple times:
136
137            For example:
138            The grep applet can have one or more "-e pattern" arguments.
139            In this case you should use bb_getopt_ulflags() as follows:
140
141            llist_t *patterns=NULL;
142
143            (this pointer must be initializated to NULL if the list is empty
144            as required by *llist_add_to(llist_t *old_head, char *new_item).)
145
146            bb_opt_complementaly = "e*";
147
148            bb_getopt_ulflags (argc, argv, "e:", &patterns);
149            grep -e user -e root /etc/passwd
150            root:x:0:0:root:/root:/bin/bash
151            user:x:500:500::/home/user:/bin/bash
152
153 */
154
155 const char *bb_opt_complementaly;
156
157 typedef struct {
158         unsigned char opt;
159         char list_flg;
160         unsigned long switch_on;
161         unsigned long switch_off;
162         unsigned long incongruously;
163         void **optarg;               /* char **optarg or llist_t **optarg */
164 } t_complementaly;
165
166 /* You can set bb_applet_long_options for parse called long options */
167
168 static const struct option bb_default_long_options[] = {
169 /*      { "help", 0, NULL, '?' }, */
170         { 0, 0, 0, 0 }
171 };
172
173 const struct option *bb_applet_long_options = bb_default_long_options;
174
175 unsigned long
176 bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
177 {
178         unsigned long flags = 0;
179         t_complementaly complementaly[sizeof(flags) * 8 + 1];
180         int c;
181         const unsigned char *s;
182         t_complementaly *on_off;
183         va_list p;
184
185         va_start (p, applet_opts);
186
187         /* skip GNU extension */
188         s = applet_opts;
189         if(*s == '+' || *s == '-')
190                 s++;
191
192         c = 0;
193         on_off = complementaly;
194         for (; *s; s++) {
195                 if(c >= (sizeof(flags)*8))
196                         break;
197                 on_off->opt = *s;
198                 on_off->switch_on = (1 << c);
199                 on_off->list_flg = 0;
200                 on_off->switch_off = 0;
201                 on_off->incongruously = 0;
202                 on_off->optarg = NULL;
203                 if (s[1] == ':') {
204                         on_off->optarg = va_arg (p, void **);
205                         do
206                                 s++;
207                         while (s[1] == ':');
208                 }
209                 on_off++;
210                 c++;
211         }
212         on_off->opt = 0;
213         c = 0;
214         for (s = bb_opt_complementaly; s && *s; s++) {
215                 t_complementaly *pair;
216
217                 if (*s == ':') {
218                         c = 0;
219                         continue;
220                 }
221                 if (c)
222                         continue;
223                 for (on_off = complementaly; on_off->opt; on_off++)
224                         if (on_off->opt == *s)
225                                 break;
226                 pair = on_off;
227                 for(s++; *s && *s != ':'; s++) {
228                         if (*s == '-' || *s == '~') {
229                                 c = *s;
230                         } else if(*s == '*') {
231                                 pair->list_flg++;
232                         } else {
233                                 unsigned long *pair_switch = &(pair->switch_on);
234                                 if(c)
235                                         pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously);
236                                 for (on_off = complementaly; on_off->opt; on_off++)
237                                         if (on_off->opt == *s) {
238                                                 *pair_switch |= on_off->switch_on;
239                                                 break;
240                                         }
241                         }
242                 }
243                 s--;
244         }
245
246         while ((c = getopt_long (argc, argv, applet_opts,
247                                  bb_applet_long_options, NULL)) > 0) {
248                 for (on_off = complementaly; on_off->opt != c; on_off++) {
249                         if(!on_off->opt)
250                                 bb_show_usage ();
251                 }
252                 if(flags & on_off->incongruously)
253                         flags |= 0x80000000UL;
254                 flags &= ~on_off->switch_off;
255                 flags |= on_off->switch_on;
256                 if(on_off->list_flg) {
257                         *(llist_t **)(on_off->optarg) =
258                                 llist_add_to(*(llist_t **)(on_off->optarg), optarg);
259                 } else if (on_off->optarg) {
260                         *(char **)(on_off->optarg) = optarg;
261                 }
262         }
263
264         return flags;
265 }