]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
2552efceb1a80fb1597ab52fe041ad7e3355eb3f
[libucw.git] / ucw / opt.c
1 /*
2  *      UCW Library -- Parsing of command-line options
3  *
4  *      (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
5  *      (c) 2014 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include <ucw/lib.h>
12 #include <ucw/opt.h>
13 #include <ucw/opt-internal.h>
14 #include <ucw/stkstring.h>
15 #include <ucw/strtonum.h>
16
17 #include <alloca.h>
18 #include <math.h>
19 #include <stdbool.h>
20
21 /***
22  * Value flags defaults
23  * ~~~~~~~~~~~~~~~~~~~~
24  *
25  * OPT_NO_VALUE for OPT_BOOL, OPT_SWITCH and OPT_INC
26  * OPT_MAYBE_VALUE for OPT_STRING, OPT_UNS, OPT_INT
27  * Some of the value flags (OPT_NO_VALUE, OPT_MAYBE_VALUE, OPT_REQUIRED_VALUE)
28  * must be specified for OPT_CALL and OPT_USER.
29  ***/
30 static uns opt_default_value_flags[] = {
31     [OPT_CL_BOOL] = OPT_NO_VALUE,
32     [OPT_CL_STATIC] = OPT_MAYBE_VALUE,
33     [OPT_CL_SWITCH] = OPT_NO_VALUE,
34     [OPT_CL_INC] = OPT_NO_VALUE,
35     [OPT_CL_CALL] = 0,
36     [OPT_CL_USER] = 0,
37     [OPT_CL_SECTION] = 0,
38     [OPT_CL_HELP] = 0
39 };
40
41 struct opt_context {
42   struct opt_precomputed * opts;
43   struct opt_precomputed ** shortopt;
44   struct opt_item ** hooks_before_arg;
45   struct opt_item ** hooks_before_value;
46   struct opt_item ** hooks_after_value;
47   short opt_count;
48   short hooks_before_arg_count;
49   short hooks_before_value_count;
50   short hooks_after_value_count;
51   int positional_max;
52   int positional_count;
53   bool stop_parsing;
54 };
55
56 void opt_failure(const char * mesg, ...) {
57   va_list args;
58   va_start(args, mesg);
59   vfprintf(stderr, mesg, args);
60   fprintf(stderr, "\n");
61   opt_usage();
62   exit(OPT_EXIT_BAD_ARGS);
63 }
64
65 static char *opt_name(struct opt_context *oc, struct opt_precomputed *opt)
66 {
67   struct opt_item *item = opt->item;
68   char *res;
69   if (item->letter >= OPT_POSITIONAL_TAIL)
70     res = stk_printf("positional argument #%d", oc->positional_count);
71   else if (opt->flags & OPT_SEEN_AS_LONG)
72     res = stk_printf("--%s", opt->name);
73   else
74     res = stk_printf("-%c", item->letter);
75   return xstrdup(res);
76 }
77
78 #define THIS_OPT opt_name(oc, opt)
79
80 void opt_precompute(struct opt_precomputed *opt, struct opt_item *item)
81 {
82   opt->item = item;
83   opt->count = 0;
84   opt->name = item->name;
85   uns flags = item->flags;
86
87   if (item->letter >= OPT_POSITIONAL_TAIL) {
88     flags &= ~OPT_VALUE_FLAGS;
89     flags |= OPT_REQUIRED_VALUE;
90   }
91   if (!(flags & OPT_VALUE_FLAGS)) {
92     ASSERT(item->cls != OPT_CL_CALL && item->cls != OPT_CL_USER);
93     flags |= opt_default_value_flags[item->cls];
94   }
95
96   opt->flags = flags;
97 }
98
99 static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, char * str) {
100   uns len = strlen(str);
101   struct opt_precomputed * candidate = NULL;
102
103   for (int i = 0; i < oc->opt_count; i++) {
104     struct opt_precomputed *opt = &oc->opts[i];
105     if (!opt->name)
106       continue;
107
108     if (!strncmp(opt->name, str, len)) {
109       if (strlen(opt->name) == len)
110         return opt;
111     } else if (opt->item->cls == OPT_CL_BOOL && !strncmp("no-", str, 3) && !strncmp(opt->name, str+3, len-3)) {
112       if (strlen(opt->name) == len-3)
113         return opt;
114     } else
115       continue;
116
117     if (candidate)
118       opt_failure("Ambiguous option --%s: matches both --%s and --%s.", str, candidate->name, opt->name);
119     else
120       candidate = opt;
121   }
122
123   if (candidate)
124     return candidate;
125
126   opt_failure("Invalid option --%s.", str);
127 }
128
129 // FIXME: Use simple-lists?
130 #define OPT_PTR(type) ({                        \
131   type * ptr;                                   \
132   if (item->flags & OPT_MULTIPLE) {             \
133     struct {                                    \
134       cnode n;                                  \
135       type v;                                   \
136     } * n = xmalloc(sizeof(*n));                \
137     clist_add_tail(item->ptr, &(n->n));         \
138     ptr = &(n->v);                              \
139   } else                                        \
140     ptr = item->ptr;                            \
141   ptr; })
142
143 static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value) {
144   struct opt_item * item = opt->item;
145
146   if (opt->count++ && (opt->flags & OPT_SINGLE))
147     opt_failure("Option %s must be specified at most once.", THIS_OPT);
148
149   if (opt->flags & OPT_LAST_ARG)
150     oc->stop_parsing = 1;
151
152   for (int i = 0; i < oc->hooks_before_value_count; i++)
153     oc->hooks_before_value[i]->u.call(item, value, oc->hooks_before_value[i]->ptr);
154
155   switch (item->cls) {
156     case OPT_CL_BOOL:
157       if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
158         *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
159       else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
160         *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
161       else
162         opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): 1/0, y/n, yes/no, true/false.", THIS_OPT);
163       break;
164     case OPT_CL_STATIC:
165       {
166         char * e = NULL;
167         switch (item->type) {
168           case CT_INT:
169             if (!value)
170               *OPT_PTR(int) = 0;
171             else
172               e = cf_parse_int(value, OPT_PTR(int));
173             if (e)
174               opt_failure("Integer value parsing failed for %s: %s", THIS_OPT, e);
175             break;
176           case CT_U64:
177             if (!value)
178               *OPT_PTR(u64) = 0;
179             else
180               e = cf_parse_u64(value, OPT_PTR(u64));
181             if (e)
182               opt_failure("Unsigned 64-bit value parsing failed for %s: %s", THIS_OPT, e);
183             break;
184           case CT_DOUBLE:
185             if (!value)
186               *OPT_PTR(double) = NAN;
187             else
188               e = cf_parse_double(value, OPT_PTR(double));
189             if (e)
190               opt_failure("Floating-point value parsing failed for %s: %s", THIS_OPT, e);
191             break;
192           case CT_IP:
193             if (!value)
194               *OPT_PTR(u32) = 0;
195             else
196               e = cf_parse_ip(value, OPT_PTR(u32));
197             if (e)
198               opt_failure("IP address parsing failed for %s: %s", THIS_OPT, e);
199             break;
200           case CT_STRING:
201             if (!value)
202               *OPT_PTR(const char *) = NULL;
203             else
204               *OPT_PTR(const char *) = xstrdup(value);
205             break;
206           default:
207             ASSERT(0);
208         }
209         break;
210       }
211     case OPT_CL_SWITCH:
212       if ((opt->flags & OPT_SINGLE) && *((int *)item->ptr) != -1)
213         opt_failure("Multiple switches: %s", THIS_OPT);
214       else
215         *((int *)item->ptr) = item->u.value;
216       break;
217     case OPT_CL_INC:
218       if (opt->flags & OPT_NEGATIVE)
219         (*((int *)item->ptr))--;
220       else
221         (*((int *)item->ptr))++;
222       break;
223     case OPT_CL_CALL:
224       item->u.call(item, value, item->ptr);
225       break;
226     case OPT_CL_USER:
227       {
228         char * e = NULL;
229         e = item->u.utype->parser(value, OPT_PTR(void*));
230         if (e)
231           opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e);
232         break;
233       }
234     default:
235       ASSERT(0);
236   }
237
238   for (int i = 0;i < oc->hooks_after_value_count; i++)
239     oc->hooks_after_value[i]->u.call(item, value, oc->hooks_after_value[i]->ptr);
240 }
241
242 static int opt_longopt(struct opt_context * oc, char ** argv, int index) {
243   int eaten = 0;
244   char * name_in = argv[index] + 2; // skipping the -- on the beginning
245   uns pos = strchrnul(name_in, '=') - name_in;
246   struct opt_precomputed * opt = opt_find_item_longopt(oc, strndupa(name_in, pos));
247   char * value = NULL;
248
249   opt->flags |= OPT_SEEN_AS_LONG;
250
251   if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3)) {
252     if (name_in[pos])
253       opt_failure("Option --%s must not have any value.", name_in);
254     value = "n";
255   } else if (opt->flags & OPT_REQUIRED_VALUE) {
256     if (name_in[pos])
257       value = name_in + pos + 1;
258     else {
259       value = argv[index+1];
260       if (!value)
261         opt_failure("Option %s must have a value, but nothing supplied.", THIS_OPT);
262       eaten++;
263     }
264   } else if (opt->flags & OPT_MAYBE_VALUE) {
265     if (name_in[pos])
266       value = name_in + pos + 1;
267   } else {
268     if (name_in[pos])
269       opt_failure("Option %s must have no value.", THIS_OPT);
270   }
271   opt_parse_value(oc, opt, value);
272   return eaten;
273 }
274
275 static int opt_shortopt(struct opt_context * oc, char ** argv, int index) {
276   int chr = 0;
277   struct opt_precomputed * opt;
278   int o;
279
280   while (o = argv[index][++chr]) {
281     if (o < 0 || o >= 128)
282       opt_failure("Invalid character 0x%02x in option name. Only ASCII is allowed.", o & 0xff);
283     opt = oc->shortopt[o];
284
285     if (!opt)
286       opt_failure("Unknown option -%c.", o);
287
288     opt->flags &= ~OPT_SEEN_AS_LONG;
289
290     if (opt->flags & OPT_NO_VALUE)
291       opt_parse_value(oc, opt, NULL);
292     else if (opt->flags & OPT_REQUIRED_VALUE) {
293       if (argv[index][chr+1]) {
294         opt_parse_value(oc, opt, argv[index] + chr + 1);
295         return 0;
296       } else if (!argv[index+1])
297         opt_failure("Option -%c must have a value, but nothing supplied.", o);
298       else {
299         opt_parse_value(oc, opt, argv[index+1]);
300         return 1;
301       }
302     } else if (opt->flags & OPT_MAYBE_VALUE) {
303       if (argv[index][chr+1]) {
304         opt_parse_value(oc, opt, argv[index] + chr + 1);
305         return 0;
306       } else
307         opt_parse_value(oc, opt, NULL);
308     } else {
309       ASSERT(0);
310     }
311   }
312
313   return 0;
314 }
315
316 static void opt_positional(struct opt_context * oc, char * value) {
317   oc->positional_count++;
318   uns id = oc->positional_count > oc->positional_max ? OPT_POSITIONAL_TAIL : OPT_POSITIONAL(oc->positional_count);
319   struct opt_precomputed * opt = oc->shortopt[id];
320   if (!opt)
321     opt_failure("Too many positional arguments.");
322   else {
323     opt->flags &= OPT_SEEN_AS_LONG;
324     opt_parse_value(oc, opt, value);
325   }
326 }
327
328 static void opt_count_items(struct opt_context *oc, const struct opt_section *sec)
329 {
330   for (const struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
331     if (item->cls == OPT_CL_SECTION)
332       opt_count_items(oc, item->u.section);
333     else if (item->cls == OPT_CL_HOOK) {
334       if (item->flags & OPT_HOOK_BEFORE_ARG)
335         oc->hooks_before_arg_count++;
336       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
337         oc->hooks_before_value_count++;
338       else if (item->flags & OPT_HOOK_AFTER_VALUE)
339         oc->hooks_after_value_count++;
340       else
341         ASSERT(0);
342     } else if (item->letter || item->name) {
343       oc->opt_count++;
344       if (item->letter > OPT_POSITIONAL_TAIL)
345         oc->positional_max++;
346     }
347   }
348 }
349
350 static void opt_prepare_items(struct opt_context *oc, const struct opt_section *sec)
351 {
352   for (struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
353     if (item->cls == OPT_CL_SECTION)
354       opt_prepare_items(oc, item->u.section);
355     else if (item->cls == OPT_CL_HOOK) {
356       if (item->flags & OPT_HOOK_BEFORE_ARG)
357         oc->hooks_before_arg[oc->hooks_before_arg_count++] = item;
358       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
359         oc->hooks_before_value[oc->hooks_before_value_count++] = item;
360       else if (item->flags & OPT_HOOK_AFTER_VALUE)
361         oc->hooks_after_value[oc->hooks_after_value_count++] = item;
362       else
363         ASSERT(0);
364     } else if (item->letter || item->name) {
365       struct opt_precomputed * opt = &oc->opts[oc->opt_count++];
366       opt_precompute(opt, item);
367       if (item->letter)
368         oc->shortopt[(int) item->letter] = opt;
369     }
370   }
371 }
372
373 static void opt_check_required(struct opt_context *oc)
374 {
375   for (int i = 0; i < oc->opt_count; i++) {
376     struct opt_precomputed *opt = &oc->opts[i];
377     if (!opt->count && (opt->flags & OPT_REQUIRED)) {
378       struct opt_item *item = opt->item;
379       if (item->letter > OPT_POSITIONAL_TAIL)
380         opt_failure("Required positional argument #%d not found.", item->letter - OPT_POSITIONAL_TAIL);
381       else if (item->letter == OPT_POSITIONAL_TAIL)
382         opt_failure("Required positional argument not found.");
383       else if (item->letter && item->name)
384         opt_failure("Required option -%c/--%s not found.", item->letter, item->name);
385       else if (item->letter)
386         opt_failure("Required option -%c not found.", item->letter);
387       else
388         opt_failure("Required option --%s not found.", item->name);
389     }
390   }
391 }
392
393 int opt_parse(const struct opt_section * options, char ** argv) {
394   struct opt_context * oc = alloca(sizeof(*oc));
395   memset(oc, 0, sizeof (*oc));
396
397   opt_count_items(oc, options);
398   oc->opts = alloca(sizeof(*oc->opts) * oc->opt_count);
399   oc->shortopt = alloca(sizeof(*oc->shortopt) * (oc->positional_max + 257));
400   memset(oc->shortopt, 0, sizeof(*oc->shortopt) * (oc->positional_max + 257));
401   oc->hooks_before_arg = alloca(sizeof (*oc->hooks_before_arg) * oc->hooks_before_arg_count);
402   oc->hooks_before_value = alloca(sizeof (*oc->hooks_before_value) * oc->hooks_before_value_count);
403   oc->hooks_after_value = alloca(sizeof (*oc->hooks_after_value) * oc->hooks_after_value_count);
404
405   oc->opt_count = 0;
406   oc->hooks_before_arg_count = 0;
407   oc->hooks_before_value_count = 0;
408   oc->hooks_after_value_count = 0;
409   opt_prepare_items(oc, options);
410
411   int force_positional = 0;
412   int i;
413   for (i=0; argv[i] && !oc->stop_parsing; i++) {
414     char *arg = argv[i];
415     for (int j = 0; j < oc->hooks_before_arg_count; j++)
416       oc->hooks_before_arg[j]->u.call(NULL, NULL, oc->hooks_before_arg[j]->ptr);
417     if (arg[0] != '-' || force_positional)
418       opt_positional(oc, arg);
419     else {
420       if (arg[1] == '-') {
421         if (arg[2] == '\0')
422           force_positional++;
423         else
424           i += opt_longopt(oc, argv, i);
425       } else if (arg[1])
426         i += opt_shortopt(oc, argv, i);
427       else
428         opt_positional(oc, arg);
429     }
430   }
431
432   opt_check_required(oc);
433   return i;
434 }