]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: Start of positional arguments is not hard-wired
[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 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
75
76 static inline uns uns_min(uns x, uns y)
77 {
78   return MIN(x, y);
79 }
80
81 void opt_help(const struct opt_section * help) {
82   int sections_cnt = 0;
83   int lines_cnt = 0;
84
85   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
86     if (item->flags & OPT_NO_HELP) continue;
87     if (item->cls == OPT_CL_SECTION) {
88       sections_cnt++;
89       continue;
90     }
91     if (!*(item->help)) {
92       lines_cnt++;
93       continue;
94     }
95     FOREACHLINE(item->help)
96       lines_cnt++;
97   }
98
99   struct opt_sectlist {
100     int pos;
101     struct opt_section * sect;
102   } sections[sections_cnt];
103   int s = 0;
104
105   const char *lines[lines_cnt][3];
106   memset(lines, 0, sizeof(lines));
107   int line = 0;
108
109   int linelengths[3] = { -1, -1, -1 };
110
111   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
112     if (item->flags & OPT_NO_HELP) continue;
113
114     if (item->cls == OPT_CL_HELP) {
115       if (!*(item->help)) {
116         line++;
117         continue;
118       }
119 #define SPLITLINES(text) do { \
120       FOREACHLINE(text) { \
121         int cell = 0; \
122         for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \
123           lines[line][cell] = b; \
124           if (cell >= 2) \
125             break; \
126           else \
127             if (*e == '\t' && linelengths[cell] < (e - b)) \
128               linelengths[cell] = e-b; \
129           cell++; \
130         } \
131         line++; \
132       } } while (0)
133       SPLITLINES(item->help);
134       continue;
135     }
136
137     if (item->cls == OPT_CL_SECTION) {
138       sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
139       continue;
140     }
141
142     uns valoff = strchrnul(item->help, '\t') - item->help;
143     uns eol = strchrnul(item->help, '\n') - item->help;
144     if (valoff > eol)
145       valoff = eol;
146 #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)))
147     if (item->name) {
148       lines[line][1] = stk_printf("--%s%s", item->name, VAL(item));
149       if (linelengths[1] < (int) strlen(lines[line][1]))
150         linelengths[1] = strlen(lines[line][1]);
151       lines[line][0] = "";
152       if (linelengths[0] < 0)
153         linelengths[0] = 0;
154     }
155     if (item->letter) {
156       lines[line][0] = stk_printf("-%c,", item->letter);
157       if (linelengths[0] < (int) strlen(lines[line][0]))
158         linelengths[0] = strlen(lines[line][0]);
159     }
160 #undef VAL
161
162     if (eol > valoff) {
163       lines[line][2] = item->help + valoff + 1;
164     }
165
166     line++;
167
168     if (*(item->help + eol))
169       SPLITLINES(item->help + eol + 1);
170   }
171 #undef SPLITLINES
172
173   s = 0;
174 #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]
175 #define LASTFIELD(k) uns_min(strchrnul(lines[i][k], '\t') - lines[i][k], strchrnul(lines[i][k], '\n') - lines[i][k]), lines[i][k]
176   for (int i=0;i<line;i++) {
177     while (s < sections_cnt && sections[s].pos == i) {
178       opt_help(sections[s].sect);
179       s++;
180     }
181     if (lines[i][0] == NULL)
182       printf("\n");
183     else if (linelengths[0] == -1 || lines[i][1] == NULL)
184       printf("%.*s\n", LASTFIELD(0));
185     else if (linelengths[1] == -1 || lines[i][2] == NULL)
186       printf("%-*.*s  %.*s\n", FIELD(0), LASTFIELD(1));
187     else
188       printf("%-*.*s  %-*.*s  %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
189   }
190   while (s < sections_cnt && sections[s].pos == line) {
191     opt_help(sections[s].sect);
192     s++;
193   }
194 }
195
196 static struct opt_precomputed * opt_find_item_shortopt(struct opt_context * oc, int chr) {
197   struct opt_precomputed * candidate = oc->shortopt[chr];
198   if (!candidate)
199     opt_failure("Invalid option -%c", chr);
200   if (candidate->count++ && (candidate->flags & OPT_SINGLE))
201     opt_failure("Option -%c appeared the second time.", candidate->item->letter);
202   return candidate;
203 }
204
205 static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, char * str) {
206   uns len = strlen(str);
207   struct opt_precomputed * candidate = NULL;
208
209   for (int i=0; i<oc->opt_count; i++) {
210     if (!oc->opts[i].name)
211       continue;
212     if (!strncmp(oc->opts[i].name, str, len)) {
213       if (strlen(oc->opts[i].name) == len) {
214         if (oc->opts[i].count++ && (oc->opts[i].flags & OPT_SINGLE))
215           opt_failure("Option %s appeared the second time.", oc->opts[i].name);
216
217         return &oc->opts[i];
218       }
219       if (candidate)
220         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, oc->opts[i].name);
221       else
222         candidate = &oc->opts[i];
223     }
224     if (!strncmp("no-", str, 3) && !strncmp(oc->opts[i].name, str+3, len-3)) {
225       if (strlen(oc->opts[i].name) == len-3) {
226         if (oc->opts[i].count++ && (oc->opts[i].flags & OPT_SINGLE))
227           opt_failure("Option %s appeared the second time.", oc->opts[i].name);
228
229         return &oc->opts[i];
230       }
231       if (candidate)
232         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, oc->opts[i].name);
233       else
234         candidate = &oc->opts[i];
235     }
236   }
237
238   if (candidate)
239     return candidate;
240
241   opt_failure("Invalid option %s.", str);
242 }
243
244 #define OPT_PTR(type) ({ \
245   type * ptr; \
246   if (item->flags & OPT_MULTIPLE) { \
247     struct { \
248       cnode n; \
249       type v; \
250     } * n = xmalloc(sizeof(*n)); \
251     clist_add_tail(item->ptr, &(n->n)); \
252     ptr = &(n->v); \
253   } else \
254     ptr = item->ptr; \
255   ptr; })
256
257 #define OPT_NAME (longopt == 2 ? stk_printf("positional arg #%d", oc->positional_count) : (longopt == 1 ? stk_printf("--%s", opt->name) : stk_printf("-%c", item->letter)))
258 static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value, int longopt) {
259   struct opt_item * item = opt->item;
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): y/n, yes/no, true/false.", OPT_NAME);
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", OPT_NAME, 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", OPT_NAME, 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("Double value parsing failed for %s: %s", OPT_NAME, e);
299             break;
300           case CT_IP:
301             if (!value)
302               e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
303             else
304               e = cf_parse_ip(value, OPT_PTR(u32));
305             if (e)
306               opt_failure("IP parsing failed for %s: %s", OPT_NAME, 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 (*((int *)item->ptr) != -1)
321         opt_failure("Multiple switches: %s", OPT_NAME);
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("User defined type value parsing failed for %s: %s", OPT_NAME, 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 #undef OPT_NAME
350
351 static int opt_longopt(struct opt_context * oc, char ** argv, int index) {
352   int eaten = 0;
353   char * name_in = argv[index] + 2; // skipping the -- on the beginning
354   uns pos = strchrnul(name_in, '=') - name_in;
355   struct opt_precomputed * opt = opt_find_item_longopt(oc, strndupa(name_in, pos));
356   char * value = NULL;
357
358   if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
359     value = "n";
360   else if (opt->flags & OPT_REQUIRED_VALUE) {
361     if (pos < strlen(name_in))
362       value = name_in + pos + 1;
363     else {
364       value = argv[index+1];
365       if (!value)
366         opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
367       eaten++;
368     }
369   }
370   else if (opt->flags & OPT_MAYBE_VALUE) {
371     if (pos < strlen(name_in))
372       value = name_in + pos + 1;
373   }
374   else {
375     if (pos < strlen(name_in))
376       opt_failure("Argument --%s must not have any value.", opt->name);
377   }
378   opt_parse_value(oc, opt, value, 1);
379   return eaten;
380 }
381
382 static int opt_shortopt(struct opt_context * oc, char ** argv, int index) {
383   int chr = 0;
384   struct opt_precomputed * opt;
385   while (argv[index][++chr] && (opt = opt_find_item_shortopt(oc, argv[index][chr]))) {
386     if (opt->flags & OPT_NO_VALUE) {
387       opt_parse_value(oc, opt, NULL, 0);
388     }
389     else if (opt->flags & OPT_REQUIRED_VALUE) {
390       if (chr == 1 && argv[index][2]) {
391         opt_parse_value(oc, opt, argv[index] + 2, 0);
392         return 0;
393       }
394       else if (argv[index][chr+1])
395         opt_failure("Option -%c must have a value but found inside a bunch of short opts.", opt->item->letter);
396       else if (!argv[index+1])
397         opt_failure("Option -%c must have a value but nothing supplied.", opt->item->letter);
398       else {
399         opt_parse_value(oc, opt, argv[index+1], 0);
400         return 1;
401       }
402     }
403     else if (opt->flags & OPT_MAYBE_VALUE) {
404       if (chr == 1 && argv[index][2]) {
405         opt_parse_value(oc, opt, argv[index] + 2, 0);
406         return 0;
407       }
408       else
409         opt_parse_value(oc, opt, NULL, 0);
410     }
411     else {
412       ASSERT(0);
413     }
414   }
415
416   if (argv[index][chr])
417     opt_failure("Unknown option -%c.", argv[index][chr]);
418
419   return 0;
420 }
421
422 static void opt_positional(struct opt_context * oc, char * value) {
423   oc->positional_count++;
424   uns id = oc->positional_count > oc->positional_max ? OPT_POSITIONAL_TAIL : OPT_POSITIONAL(oc->positional_count);
425   struct opt_precomputed * opt = opt_find_item_shortopt(oc, id);
426   if (opt)
427     opt_parse_value(oc, opt, value, 2);
428   else {
429     ASSERT(oc->positional_count > oc->positional_max);
430     opt_failure("Too many positional arguments.");
431   }
432 }
433
434 static void opt_count_items(struct opt_context *oc, const struct opt_section *sec)
435 {
436   for (const struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
437     if (item->cls == OPT_CL_SECTION)
438       opt_count_items(oc, item->u.section);
439     else if (item->cls == OPT_CL_HOOK) {
440       if (item->flags & OPT_HOOK_BEFORE_ARG)
441         oc->hooks_before_arg_count++;
442       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
443         oc->hooks_before_value_count++;
444       else if (item->flags & OPT_HOOK_AFTER_VALUE)
445         oc->hooks_after_value_count++;
446       else
447         ASSERT(0);
448     } else if (item->letter || item->name) {
449       oc->opt_count++;
450       if (item->letter > OPT_POSITIONAL_TAIL)
451         oc->positional_max++;
452     }
453   }
454 }
455
456 static void opt_add_default_flags(struct opt_precomputed *opt)
457 {
458   struct opt_item *item = opt->item;
459   uns flags = opt->flags;
460
461   if (item->letter >= OPT_POSITIONAL_TAIL) {
462     flags &= ~OPT_VALUE_FLAGS;
463     flags |= OPT_REQUIRED_VALUE;
464   }
465   if (!(flags & OPT_VALUE_FLAGS)) {
466     ASSERT(item->cls != OPT_CL_CALL && item->cls != OPT_CL_USER);
467     flags |= opt_default_value_flags[item->cls];
468   }
469
470   opt->flags = flags;
471 }
472
473 static void opt_prepare_items(struct opt_context *oc, const struct opt_section *sec)
474 {
475   for (struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
476     if (item->cls == OPT_CL_SECTION)
477       opt_prepare_items(oc, item->u.section);
478     else if (item->cls == OPT_CL_HOOK) {
479       if (item->flags & OPT_HOOK_BEFORE_ARG)
480         oc->hooks_before_arg[oc->hooks_before_arg_count++] = item;
481       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
482         oc->hooks_before_value[oc->hooks_before_value_count++] = item;
483       else if (item->flags & OPT_HOOK_AFTER_VALUE)
484         oc->hooks_after_value[oc->hooks_after_value_count++] = item;
485       else
486         ASSERT(0);
487     } else if (item->letter || item->name) {
488       struct opt_precomputed * opt = &oc->opts[oc->opt_count++];
489       opt->item = item;
490       opt->flags = item->flags;
491       opt->count = 0;
492       opt->name = item->name;
493       if (item->letter)
494         oc->shortopt[(int) item->letter] = opt;
495       opt_add_default_flags(opt);
496     }
497   }
498 }
499
500 static void opt_check_required(struct opt_context *oc)
501 {
502   for (int i = 0; i < oc->opt_count; i++) {
503     struct opt_precomputed *opt = &oc->opts[i];
504     if (!opt->count && (opt->flags & OPT_REQUIRED)) {
505       struct opt_item *item = opt->item;
506       if (item->letter > OPT_POSITIONAL_TAIL)
507         opt_failure("Required positional argument #%d not found.", i - OPT_POSITIONAL_TAIL);
508       else if (item->letter == OPT_POSITIONAL_TAIL)
509         opt_failure("Required positional argument not found.");
510       else if (item->letter && item->name)
511         opt_failure("Required option -%c/--%s not found.", item->letter, item->name);
512       else if (item->letter)
513         opt_failure("Required option -%c not found.", item->letter);
514       else
515         opt_failure("Required option --%s not found.", item->name);
516     }
517   }
518 }
519
520 void opt_parse(const struct opt_section * options, char ** argv) {
521   struct opt_context * oc = alloca(sizeof(*oc));
522   memset(oc, 0, sizeof (*oc));
523
524   opt_count_items(oc, options);
525   oc->opts = alloca(sizeof(*oc->opts) * oc->opt_count);
526   oc->shortopt = alloca(sizeof(*oc->shortopt) * (oc->positional_max + 257));
527   memset(oc->shortopt, 0, sizeof(*oc->shortopt) * (oc->positional_max + 257));
528   oc->hooks_before_arg = alloca(sizeof (*oc->hooks_before_arg) * oc->hooks_before_arg_count);
529   oc->hooks_before_value = alloca(sizeof (*oc->hooks_before_value) * oc->hooks_before_value_count);
530   oc->hooks_after_value = alloca(sizeof (*oc->hooks_after_value) * oc->hooks_after_value_count);
531
532   oc->opt_count = 0;
533   oc->hooks_before_arg_count = 0;
534   oc->hooks_before_value_count = 0;
535   oc->hooks_after_value_count = 0;
536   opt_prepare_items(oc, options);
537
538   int force_positional = 0;
539   for (int i = 0; argv[i]; i++) {
540     char *arg = argv[i];
541     for (int j = 0; j < oc->hooks_before_arg_count; j++)
542       oc->hooks_before_arg[j]->u.call(NULL, NULL, oc->hooks_before_arg[j]->ptr);
543     if (arg[0] != '-' || force_positional)
544       opt_positional(oc, arg);
545     else {
546       if (arg[1] == '-') {
547         if (arg[2] == '\0')
548           force_positional++;
549         else
550           i += opt_longopt(oc, argv, i);
551       } else if (arg[1])
552         i += opt_shortopt(oc, argv, i);
553       else
554         opt_positional(oc, arg);
555     }
556   }
557
558   opt_check_required(oc);
559 }
560
561 static void opt_conf_end_of_options(struct cf_context *cc) {
562   cf_load_default(cc);
563   if (cc->postpone_commit && cf_close_group())
564     opt_failure("Loading of configuration failed");
565 }
566
567 void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) {
568   struct cf_context *cc = cf_get_context();
569   switch (opt->letter) {
570     case 'S':
571       cf_load_default(cc);
572       if (cf_set(value))
573         opt_failure("Cannot set %s", value);
574       break;
575     case 'C':
576       if (cf_load(value))
577         opt_failure("Cannot load config file %s", value);
578       break;
579 #ifdef CONFIG_UCW_DEBUG
580     case '0':
581       opt_conf_end_of_options(cc);
582       struct fastbuf *b = bfdopen(1, 4096);
583       cf_dump_sections(b);
584       bclose(b);
585       exit(0);
586       break;
587 #endif
588   }
589 }
590
591 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
592   static enum {
593     OPT_CONF_HOOK_BEGIN,
594     OPT_CONF_HOOK_CONFIG,
595     OPT_CONF_HOOK_OTHERS
596   } state = OPT_CONF_HOOK_BEGIN;
597
598   int confopt = 0;
599
600   if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
601     confopt = 1;
602
603   switch (state) {
604     case OPT_CONF_HOOK_BEGIN:
605       if (confopt)
606         state = OPT_CONF_HOOK_CONFIG;
607       else {
608         opt_conf_end_of_options(cf_get_context());
609         state = OPT_CONF_HOOK_OTHERS;
610       }
611       break;
612     case OPT_CONF_HOOK_CONFIG:
613       if (!confopt) {
614         opt_conf_end_of_options(cf_get_context());
615         state = OPT_CONF_HOOK_OTHERS;
616       }
617       break;
618     case OPT_CONF_HOOK_OTHERS:
619       if (confopt)
620         opt_failure("Config options (-C, -S) must stand before other options.");
621       break;
622     default:
623       ASSERT(0);
624   }
625 }