]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: exit() does not return, no need to call va_end() afterwards
[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/conf.h>
14 #include <ucw/conf-internal.h>
15 #include <ucw/fastbuf.h>
16 #include <ucw/stkstring.h>
17 #include <ucw/strtonum.h>
18
19 #include <alloca.h>
20 #include <math.h>
21
22 /***
23  * Value flags defaults
24  * ~~~~~~~~~~~~~~~~~~~~
25  *
26  * OPT_NO_VALUE for OPT_BOOL, OPT_SWITCH and OPT_INC
27  * OPT_MAYBE_VALUE for OPT_STRING, OPT_UNS, OPT_INT
28  * Some of the value flags (OPT_NO_VALUE, OPT_MAYBE_VALUE, OPT_REQUIRED_VALUE)
29  * must be specified for OPT_CALL and OPT_USER.
30  ***/
31 static uns opt_default_value_flags[] = {
32     [OPT_CL_BOOL] = OPT_NO_VALUE,
33     [OPT_CL_STATIC] = OPT_MAYBE_VALUE,
34     [OPT_CL_SWITCH] = OPT_NO_VALUE,
35     [OPT_CL_INC] = OPT_NO_VALUE,
36     [OPT_CL_CALL] = 0,
37     [OPT_CL_USER] = 0,
38     [OPT_CL_SECTION] = 0,
39     [OPT_CL_HELP] = 0
40 };
41
42 struct opt_precomputed {
43   struct opt_item * item;
44   const char * name;
45   short flags;
46   short count;
47 };
48
49 struct opt_context {
50   struct opt_precomputed * opts;
51   struct opt_precomputed ** shortopt;
52   struct opt_item ** hooks_before_arg;
53   struct opt_item ** hooks_before_value;
54   struct opt_item ** hooks_after_value;
55   short opt_count;
56   short hooks_before_arg_count;
57   short hooks_before_value_count;
58   short hooks_after_value_count;
59   int positional_max;
60   int positional_count;
61 };
62
63 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
64 static void opt_failure(const char * mesg, ...) {
65   va_list args;
66   va_start(args, mesg);
67   vfprintf(stderr, mesg, args);
68   fprintf(stderr, "\n");
69   opt_usage();
70   exit(OPT_EXIT_BAD_ARGS);
71 }
72
73 static char *opt_name(struct opt_context *oc, struct opt_precomputed *opt)
74 {
75   struct opt_item *item = opt->item;
76   char *res;
77   if (item->letter >= OPT_POSITIONAL_TAIL)
78     res = stk_printf("positional argument #%d", oc->positional_count);
79   else if (opt->flags & OPT_SEEN_AS_LONG)
80     res = stk_printf("--%s", opt->name);
81   else
82     res = stk_printf("-%c", item->letter);
83   return xstrdup(res);
84 }
85
86 #define THIS_OPT opt_name(oc, opt)
87
88 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
89
90 static inline uns uns_min(uns x, uns y)
91 {
92   return MIN(x, y);
93 }
94
95 void opt_help(const struct opt_section * help) {
96   int sections_cnt = 0;
97   int lines_cnt = 0;
98
99   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
100     if (item->flags & OPT_NO_HELP) continue;
101     if (item->cls == OPT_CL_SECTION) {
102       sections_cnt++;
103       continue;
104     }
105     if (!*(item->help)) {
106       lines_cnt++;
107       continue;
108     }
109     FOREACHLINE(item->help)
110       lines_cnt++;
111   }
112
113   struct opt_sectlist {
114     int pos;
115     struct opt_section * sect;
116   } sections[sections_cnt];
117   int s = 0;
118
119   const char *lines[lines_cnt][3];
120   memset(lines, 0, sizeof(lines));
121   int line = 0;
122
123   int linelengths[3] = { -1, -1, -1 };
124
125   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
126     if (item->flags & OPT_NO_HELP) continue;
127
128     if (item->cls == OPT_CL_HELP) {
129       if (!*(item->help)) {
130         line++;
131         continue;
132       }
133 #define SPLITLINES(text) do { \
134       FOREACHLINE(text) { \
135         int cell = 0; \
136         for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \
137           lines[line][cell] = b; \
138           if (cell >= 2) \
139             break; \
140           else \
141             if (*e == '\t' && linelengths[cell] < (e - b)) \
142               linelengths[cell] = e-b; \
143           cell++; \
144         } \
145         line++; \
146       } } while (0)
147       SPLITLINES(item->help);
148       continue;
149     }
150
151     if (item->cls == OPT_CL_SECTION) {
152       sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
153       continue;
154     }
155
156     uns valoff = strchrnul(item->help, '\t') - item->help;
157     uns eol = strchrnul(item->help, '\n') - item->help;
158     if (valoff > eol)
159       valoff = eol;
160 #define VAL(it) ((it->flags & OPT_REQUIRED_VALUE) ? stk_printf("=%.*s", valoff, item->help)  : ((it->flags & OPT_NO_VALUE) ? "" : stk_printf("(=%.*s)", valoff, item->help)))
161     if (item->name) {
162       lines[line][1] = stk_printf("--%s%s", item->name, VAL(item));
163       if (linelengths[1] < (int) strlen(lines[line][1]))
164         linelengths[1] = strlen(lines[line][1]);
165       lines[line][0] = "";
166       if (linelengths[0] < 0)
167         linelengths[0] = 0;
168     }
169     if (item->letter) {
170       lines[line][0] = stk_printf("-%c,", item->letter);
171       if (linelengths[0] < (int) strlen(lines[line][0]))
172         linelengths[0] = strlen(lines[line][0]);
173     }
174 #undef VAL
175
176     if (eol > valoff) {
177       lines[line][2] = item->help + valoff + 1;
178     }
179
180     line++;
181
182     if (*(item->help + eol))
183       SPLITLINES(item->help + eol + 1);
184   }
185 #undef SPLITLINES
186
187   s = 0;
188 #define FIELD(k) linelengths[k], uns_min(strchrnul(lines[i][k], '\t') - lines[i][k], strchrnul(lines[i][k], '\n') - lines[i][k]), lines[i][k]
189 #define LASTFIELD(k) uns_min(strchrnul(lines[i][k], '\t') - lines[i][k], strchrnul(lines[i][k], '\n') - lines[i][k]), lines[i][k]
190   for (int i=0;i<line;i++) {
191     while (s < sections_cnt && sections[s].pos == i) {
192       opt_help(sections[s].sect);
193       s++;
194     }
195     if (lines[i][0] == NULL)
196       printf("\n");
197     else if (linelengths[0] == -1 || lines[i][1] == NULL)
198       printf("%.*s\n", LASTFIELD(0));
199     else if (linelengths[1] == -1 || lines[i][2] == NULL)
200       printf("%-*.*s  %.*s\n", FIELD(0), LASTFIELD(1));
201     else
202       printf("%-*.*s  %-*.*s  %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
203   }
204   while (s < sections_cnt && sections[s].pos == line) {
205     opt_help(sections[s].sect);
206     s++;
207   }
208 }
209
210 static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, char * str) {
211   uns len = strlen(str);
212   struct opt_precomputed * candidate = NULL;
213
214   for (int i = 0; i < oc->opt_count; i++) {
215     struct opt_precomputed *opt = &oc->opts[i];
216     if (!opt->name)
217       continue;
218
219     if (!strncmp(opt->name, str, len)) {
220       if (strlen(opt->name) == len)
221         return opt;
222     } else if (opt->item->cls == OPT_CL_BOOL && !strncmp("no-", str, 3) && !strncmp(opt->name, str+3, len-3)) {
223       if (strlen(opt->name) == len-3)
224         return opt;
225     } else
226       continue;
227
228     if (candidate)
229       opt_failure("Ambiguous option --%s: matches both --%s and --%s.", str, candidate->name, opt->name);
230     else
231       candidate = opt;
232   }
233
234   if (candidate)
235     return candidate;
236
237   opt_failure("Invalid option --%s.", str);
238 }
239
240 // FIXME: Use simple-lists?
241 #define OPT_PTR(type) ({                        \
242   type * ptr;                                   \
243   if (item->flags & OPT_MULTIPLE) {             \
244     struct {                                    \
245       cnode n;                                  \
246       type v;                                   \
247     } * n = xmalloc(sizeof(*n));                \
248     clist_add_tail(item->ptr, &(n->n));         \
249     ptr = &(n->v);                              \
250   } else                                        \
251     ptr = item->ptr;                            \
252   ptr; })
253
254 static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value) {
255   struct opt_item * item = opt->item;
256
257   if (opt->count++ && (opt->flags & OPT_SINGLE))
258     opt_failure("Option %s must be specified at most once.", THIS_OPT);
259
260   for (int i = 0; i < oc->hooks_before_value_count; i++)
261     oc->hooks_before_value[i]->u.call(item, value, oc->hooks_before_value[i]->ptr);
262
263   switch (item->cls) {
264     case OPT_CL_BOOL:
265       if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
266         *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
267       else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
268         *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
269       else
270         opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): 1/0, y/n, yes/no, true/false.", THIS_OPT);
271       break;
272     case OPT_CL_STATIC:
273       {
274         char * e = NULL;
275         switch (item->type) {
276           case CT_INT:
277             if (!value)
278               *OPT_PTR(int) = 0;
279             else
280               e = cf_parse_int(value, OPT_PTR(int));
281             if (e)
282               opt_failure("Integer value parsing failed for %s: %s", THIS_OPT, e);
283             break;
284           case CT_U64:
285             if (!value)
286               *OPT_PTR(u64) = 0;
287             else
288               e = cf_parse_u64(value, OPT_PTR(u64));
289             if (e)
290               opt_failure("Unsigned 64-bit value parsing failed for %s: %s", THIS_OPT, e);
291             break;
292           case CT_DOUBLE:
293             if (!value)
294               *OPT_PTR(double) = NAN;
295             else
296               e = cf_parse_double(value, OPT_PTR(double));
297             if (e)
298               opt_failure("Floating-point value parsing failed for %s: %s", THIS_OPT, e);
299             break;
300           case CT_IP:
301             if (!value)
302               *OPT_PTR(u32) = 0;
303             else
304               e = cf_parse_ip(value, OPT_PTR(u32));
305             if (e)
306               opt_failure("IP address parsing failed for %s: %s", THIS_OPT, e);
307             break;
308           case CT_STRING:
309             if (!value)
310               *OPT_PTR(const char *) = NULL;
311             else
312               *OPT_PTR(const char *) = xstrdup(value);
313             break;
314           default:
315             ASSERT(0);
316         }
317         break;
318       }
319     case OPT_CL_SWITCH:
320       if ((opt->flags & OPT_SINGLE) && *((int *)item->ptr) != -1)
321         opt_failure("Multiple switches: %s", THIS_OPT);
322       else
323         *((int *)item->ptr) = item->u.value;
324       break;
325     case OPT_CL_INC:
326       if (opt->flags & OPT_NEGATIVE)
327         (*((int *)item->ptr))--;
328       else
329         (*((int *)item->ptr))++;
330       break;
331     case OPT_CL_CALL:
332       item->u.call(item, value, item->ptr);
333       break;
334     case OPT_CL_USER:
335       {
336         char * e = NULL;
337         e = item->u.utype->parser(value, OPT_PTR(void*));
338         if (e)
339           opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e);
340         break;
341       }
342     default:
343       ASSERT(0);
344   }
345
346   for (int i = 0;i < oc->hooks_after_value_count; i++)
347     oc->hooks_after_value[i]->u.call(item, value, oc->hooks_after_value[i]->ptr);
348 }
349
350 static int opt_longopt(struct opt_context * oc, char ** argv, int index) {
351   int eaten = 0;
352   char * name_in = argv[index] + 2; // skipping the -- on the beginning
353   uns pos = strchrnul(name_in, '=') - name_in;
354   struct opt_precomputed * opt = opt_find_item_longopt(oc, strndupa(name_in, pos));
355   char * value = NULL;
356
357   opt->flags |= OPT_SEEN_AS_LONG;
358
359   if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3)) {
360     if (name_in[pos])
361       opt_failure("Option --%s must not have any value.", name_in);
362     value = "n";
363   } else if (opt->flags & OPT_REQUIRED_VALUE) {
364     if (name_in[pos])
365       value = name_in + pos + 1;
366     else {
367       value = argv[index+1];
368       if (!value)
369         opt_failure("Option %s must have a value, but nothing supplied.", THIS_OPT);
370       eaten++;
371     }
372   } else if (opt->flags & OPT_MAYBE_VALUE) {
373     if (name_in[pos])
374       value = name_in + pos + 1;
375   } else {
376     if (name_in[pos])
377       opt_failure("Option %s must have no value.", THIS_OPT);
378   }
379   opt_parse_value(oc, opt, value);
380   return eaten;
381 }
382
383 static int opt_shortopt(struct opt_context * oc, char ** argv, int index) {
384   int chr = 0;
385   struct opt_precomputed * opt;
386   int o;
387
388   while (o = argv[index][++chr]) {
389     if (o < 0 || o >= 128)
390       opt_failure("Invalid character 0x%02x in option name. Only ASCII is allowed.", o & 0xff);
391     opt = oc->shortopt[o];
392
393     if (!opt)
394       opt_failure("Unknown option -%c.", o);
395
396     opt->flags &= ~OPT_SEEN_AS_LONG;
397
398     if (opt->flags & OPT_NO_VALUE)
399       opt_parse_value(oc, opt, NULL);
400     else if (opt->flags & OPT_REQUIRED_VALUE) {
401       if (argv[index][chr+1]) {
402         opt_parse_value(oc, opt, argv[index] + chr + 1);
403         return 0;
404       } else if (!argv[index+1])
405         opt_failure("Option -%c must have a value, but nothing supplied.", o);
406       else {
407         opt_parse_value(oc, opt, argv[index+1]);
408         return 1;
409       }
410     } else if (opt->flags & OPT_MAYBE_VALUE) {
411       if (argv[index][chr+1]) {
412         opt_parse_value(oc, opt, argv[index] + chr + 1);
413         return 0;
414       } else
415         opt_parse_value(oc, opt, NULL);
416     } else {
417       ASSERT(0);
418     }
419   }
420
421   return 0;
422 }
423
424 static void opt_positional(struct opt_context * oc, char * value) {
425   oc->positional_count++;
426   uns id = oc->positional_count > oc->positional_max ? OPT_POSITIONAL_TAIL : OPT_POSITIONAL(oc->positional_count);
427   struct opt_precomputed * opt = oc->shortopt[id];
428   if (!opt)
429     opt_failure("Too many positional arguments.");
430   else {
431     opt->flags &= OPT_SEEN_AS_LONG;
432     opt_parse_value(oc, opt, value);
433   }
434 }
435
436 static void opt_count_items(struct opt_context *oc, const struct opt_section *sec)
437 {
438   for (const struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
439     if (item->cls == OPT_CL_SECTION)
440       opt_count_items(oc, item->u.section);
441     else if (item->cls == OPT_CL_HOOK) {
442       if (item->flags & OPT_HOOK_BEFORE_ARG)
443         oc->hooks_before_arg_count++;
444       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
445         oc->hooks_before_value_count++;
446       else if (item->flags & OPT_HOOK_AFTER_VALUE)
447         oc->hooks_after_value_count++;
448       else
449         ASSERT(0);
450     } else if (item->letter || item->name) {
451       oc->opt_count++;
452       if (item->letter > OPT_POSITIONAL_TAIL)
453         oc->positional_max++;
454     }
455   }
456 }
457
458 static void opt_add_default_flags(struct opt_precomputed *opt)
459 {
460   struct opt_item *item = opt->item;
461   uns flags = opt->flags;
462
463   if (item->letter >= OPT_POSITIONAL_TAIL) {
464     flags &= ~OPT_VALUE_FLAGS;
465     flags |= OPT_REQUIRED_VALUE;
466   }
467   if (!(flags & OPT_VALUE_FLAGS)) {
468     ASSERT(item->cls != OPT_CL_CALL && item->cls != OPT_CL_USER);
469     flags |= opt_default_value_flags[item->cls];
470   }
471
472   opt->flags = flags;
473 }
474
475 static void opt_prepare_items(struct opt_context *oc, const struct opt_section *sec)
476 {
477   for (struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
478     if (item->cls == OPT_CL_SECTION)
479       opt_prepare_items(oc, item->u.section);
480     else if (item->cls == OPT_CL_HOOK) {
481       if (item->flags & OPT_HOOK_BEFORE_ARG)
482         oc->hooks_before_arg[oc->hooks_before_arg_count++] = item;
483       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
484         oc->hooks_before_value[oc->hooks_before_value_count++] = item;
485       else if (item->flags & OPT_HOOK_AFTER_VALUE)
486         oc->hooks_after_value[oc->hooks_after_value_count++] = item;
487       else
488         ASSERT(0);
489     } else if (item->letter || item->name) {
490       struct opt_precomputed * opt = &oc->opts[oc->opt_count++];
491       opt->item = item;
492       opt->flags = item->flags;
493       opt->count = 0;
494       opt->name = item->name;
495       if (item->letter)
496         oc->shortopt[(int) item->letter] = opt;
497       opt_add_default_flags(opt);
498     }
499   }
500 }
501
502 static void opt_check_required(struct opt_context *oc)
503 {
504   for (int i = 0; i < oc->opt_count; i++) {
505     struct opt_precomputed *opt = &oc->opts[i];
506     if (!opt->count && (opt->flags & OPT_REQUIRED)) {
507       struct opt_item *item = opt->item;
508       if (item->letter > OPT_POSITIONAL_TAIL)
509         opt_failure("Required positional argument #%d not found.", item->letter - OPT_POSITIONAL_TAIL);
510       else if (item->letter == OPT_POSITIONAL_TAIL)
511         opt_failure("Required positional argument not found.");
512       else if (item->letter && item->name)
513         opt_failure("Required option -%c/--%s not found.", item->letter, item->name);
514       else if (item->letter)
515         opt_failure("Required option -%c not found.", item->letter);
516       else
517         opt_failure("Required option --%s not found.", item->name);
518     }
519   }
520 }
521
522 void opt_parse(const struct opt_section * options, char ** argv) {
523   struct opt_context * oc = alloca(sizeof(*oc));
524   memset(oc, 0, sizeof (*oc));
525
526   opt_count_items(oc, options);
527   oc->opts = alloca(sizeof(*oc->opts) * oc->opt_count);
528   oc->shortopt = alloca(sizeof(*oc->shortopt) * (oc->positional_max + 257));
529   memset(oc->shortopt, 0, sizeof(*oc->shortopt) * (oc->positional_max + 257));
530   oc->hooks_before_arg = alloca(sizeof (*oc->hooks_before_arg) * oc->hooks_before_arg_count);
531   oc->hooks_before_value = alloca(sizeof (*oc->hooks_before_value) * oc->hooks_before_value_count);
532   oc->hooks_after_value = alloca(sizeof (*oc->hooks_after_value) * oc->hooks_after_value_count);
533
534   oc->opt_count = 0;
535   oc->hooks_before_arg_count = 0;
536   oc->hooks_before_value_count = 0;
537   oc->hooks_after_value_count = 0;
538   opt_prepare_items(oc, options);
539
540   int force_positional = 0;
541   for (int i = 0; argv[i]; i++) {
542     char *arg = argv[i];
543     for (int j = 0; j < oc->hooks_before_arg_count; j++)
544       oc->hooks_before_arg[j]->u.call(NULL, NULL, oc->hooks_before_arg[j]->ptr);
545     if (arg[0] != '-' || force_positional)
546       opt_positional(oc, arg);
547     else {
548       if (arg[1] == '-') {
549         if (arg[2] == '\0')
550           force_positional++;
551         else
552           i += opt_longopt(oc, argv, i);
553       } else if (arg[1])
554         i += opt_shortopt(oc, argv, i);
555       else
556         opt_positional(oc, arg);
557     }
558   }
559
560   opt_check_required(oc);
561 }
562
563 static void opt_conf_end_of_options(struct cf_context *cc) {
564   cf_load_default(cc);
565   if (cc->postpone_commit && cf_close_group())
566     opt_failure("Loading of configuration failed");
567 }
568
569 void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) {
570   struct cf_context *cc = cf_get_context();
571   switch (opt->letter) {
572     case 'S':
573       cf_load_default(cc);
574       if (cf_set(value))
575         opt_failure("Cannot set %s", value);
576       break;
577     case 'C':
578       if (cf_load(value))
579         opt_failure("Cannot load config file %s", value);
580       break;
581 #ifdef CONFIG_UCW_DEBUG
582     case '0':
583       opt_conf_end_of_options(cc);
584       struct fastbuf *b = bfdopen(1, 4096);
585       cf_dump_sections(b);
586       bclose(b);
587       exit(0);
588       break;
589 #endif
590   }
591 }
592
593 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
594   static enum {
595     OPT_CONF_HOOK_BEGIN,
596     OPT_CONF_HOOK_CONFIG,
597     OPT_CONF_HOOK_OTHERS
598   } state = OPT_CONF_HOOK_BEGIN;
599
600   int confopt = 0;
601
602   if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
603     confopt = 1;
604
605   switch (state) {
606     case OPT_CONF_HOOK_BEGIN:
607       if (confopt)
608         state = OPT_CONF_HOOK_CONFIG;
609       else {
610         opt_conf_end_of_options(cf_get_context());
611         state = OPT_CONF_HOOK_OTHERS;
612       }
613       break;
614     case OPT_CONF_HOOK_CONFIG:
615       if (!confopt) {
616         opt_conf_end_of_options(cf_get_context());
617         state = OPT_CONF_HOOK_OTHERS;
618       }
619       break;
620     case OPT_CONF_HOOK_OTHERS:
621       if (confopt)
622         opt_failure("Config options (-C, -S) must stand before other options.");
623       break;
624     default:
625       ASSERT(0);
626   }
627 }