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