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