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