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