]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
454fbd94eded1acc91129e78fde2b93374382b25
[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 void opt_help_internal(const struct opt_section * help) {
59   int sections_cnt = 0;
60   int lines_cnt = 0;
61
62   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
63     if (item->flags & OPT_NO_HELP) continue;
64     if (item->cls == OPT_CL_SECTION) {
65       sections_cnt++;
66       continue;
67     }
68     if (!*(item->help)) {
69       lines_cnt++;
70       continue;
71     }
72     FOREACHLINE(item->help)
73       lines_cnt++;
74   }
75
76   struct opt_sectlist {
77     int pos;
78     struct opt_section * sect;
79   } sections[sections_cnt];
80   int s = 0;
81
82   const char *lines[lines_cnt][3];
83   memset(lines, 0, sizeof(lines));
84   int line = 0;
85
86   int linelengths[3] = { -1, -1, -1 };
87
88   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
89     if (item->flags & OPT_NO_HELP) continue;
90
91     if (item->cls == OPT_CL_HELP) {
92       if (!*(item->help)) {
93         line++;
94         continue;
95       }
96 #define SPLITLINES(text) do { \
97       FOREACHLINE(text) { \
98         int cell = 0; \
99         for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \
100           lines[line][cell] = b; \
101           if (cell >= 2) \
102             break; \
103           else \
104             if (*e == '\t' && linelengths[cell] < (e - b)) \
105               linelengths[cell] = e-b; \
106           cell++; \
107         } \
108         line++; \
109       } } while (0)
110       SPLITLINES(item->help);
111       continue;
112     }
113
114     if (item->cls == OPT_CL_SECTION) {
115       sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
116       continue;
117     }
118
119     uns valoff = strchrnul(item->help, '\t') - item->help;
120     uns eol = strchrnul(item->help, '\n') - item->help;
121     if (valoff > eol)
122       valoff = eol;
123 #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)))
124     if (item->name) {
125       lines[line][1] = stk_printf("--%s%s", item->name, VAL(item));
126       if (linelengths[1] < (int) strlen(lines[line][1]))
127         linelengths[1] = strlen(lines[line][1]);
128       lines[line][0] = "";
129       if (linelengths[0] < 0)
130         linelengths[0] = 0;
131     }
132     if (item->letter) {
133       lines[line][0] = stk_printf("-%c,", item->letter);
134       if (linelengths[0] < (int) strlen(lines[line][0]))
135         linelengths[0] = strlen(lines[line][0]);
136     }
137 #undef VAL
138
139     if (eol > valoff) {
140       lines[line][2] = item->help + valoff + 1;
141     }
142
143     line++;
144
145     if (*(item->help + eol))
146       SPLITLINES(item->help + eol + 1);
147   }
148 #undef SPLITLINES
149
150   s = 0;
151 #define FIELD(k) linelengths[k], MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k]
152 #define LASTFIELD(k) MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k]
153   for (int i=0;i<line;i++) {
154     while (s < sections_cnt && sections[s].pos == i) {
155       opt_help_internal(sections[s].sect);
156       s++;
157     }
158     if (lines[i][0] == NULL)
159       printf("\n");
160     else if (linelengths[0] == -1 || lines[i][1] == NULL)
161       printf("%.*s\n", LASTFIELD(0));
162     else if (linelengths[1] == -1 || lines[i][2] == NULL)
163       printf("%-*.*s  %.*s\n", FIELD(0), LASTFIELD(1));
164     else
165       printf("%-*.*s  %-*.*s  %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
166   }
167   while (s < sections_cnt && sections[s].pos == line) {
168     opt_help_internal(sections[s].sect);
169     s++;
170   }
171 }
172
173 static int opt_positional_max = 0;
174 static int opt_positional_count = 0;
175
176 struct opt_precomputed {
177   struct opt_precomputed_option {
178     struct opt_precomputed *pre;
179     struct opt_item * item;
180     const char * name;
181     short flags;
182     short count;
183   } ** opts;
184   struct opt_precomputed_option ** shortopt;
185   struct opt_item ** hooks_before_arg;
186   struct opt_item ** hooks_before_value;
187   struct opt_item ** hooks_after_value;
188   short opt_count;
189   short hooks_before_arg_count;
190   short hooks_before_value_count;
191   short hooks_after_value_count;
192 };
193
194 static struct opt_precomputed_option * opt_find_item_shortopt(int chr, struct opt_precomputed * pre) {
195   struct opt_precomputed_option * candidate = pre->shortopt[chr];
196   if (!candidate)
197     opt_failure("Invalid option -%c", chr);
198   if (candidate->count++ && (candidate->flags & OPT_SINGLE))
199     opt_failure("Option -%c appeared the second time.", candidate->item->letter);
200   return candidate;
201 }
202
203 static struct opt_precomputed_option * opt_find_item_longopt(char * str, struct opt_precomputed * pre) {
204   uns len = strlen(str);
205   struct opt_precomputed_option * candidate = NULL;
206
207   for (int i=0; i<pre->opt_count; i++) {
208     if (!pre->opts[i]->name)
209       continue;
210     if (!strncmp(pre->opts[i]->name, str, len)) {
211       if (strlen(pre->opts[i]->name) == len) {
212         if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
213           opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
214
215         return pre->opts[i];
216       }
217       if (candidate)
218         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
219       else
220         candidate = pre->opts[i];
221     }
222     if (!strncmp("no-", str, 3) && !strncmp(pre->opts[i]->name, str+3, len-3)) {
223       if (strlen(pre->opts[i]->name) == len-3) {
224         if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
225           opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
226
227         return pre->opts[i];
228       }
229       if (candidate)
230         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
231       else
232         candidate = pre->opts[i];
233     }
234   }
235
236   if (candidate)
237     return candidate;
238
239   opt_failure("Invalid option %s.", str);
240 }
241
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 #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)))
256 static void opt_parse_value(struct opt_precomputed_option * opt, char * value, int longopt) {
257   struct opt_item * item = opt->item;
258   struct opt_precomputed * pre = opt->pre;
259   for (int i=0;i<pre->hooks_before_value_count;i++)
260     pre->hooks_before_value[i]->u.call(item, value, pre->hooks_before_value[i]->ptr);
261
262   switch (item->cls) {
263     case OPT_CL_BOOL:
264       if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
265         *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
266       else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
267         *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
268       else
269         opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
270       break;
271     case OPT_CL_STATIC:
272       {
273         char * e = NULL;
274         switch (item->type) {
275           case CT_INT:
276             if (!value)
277               *OPT_PTR(int) = 0;
278             else
279               e = cf_parse_int(value, OPT_PTR(int));
280             if (e)
281               opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
282             break;
283           case CT_U64:
284             if (!value)
285               *OPT_PTR(u64) = 0;
286             else
287               e = cf_parse_u64(value, OPT_PTR(u64));
288             if (e)
289               opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
290             break;
291           case CT_DOUBLE:
292             if (!value)
293               *OPT_PTR(double) = NAN;
294             else
295               e = cf_parse_double(value, OPT_PTR(double));
296             if (e)
297               opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
298             break;
299           case CT_IP:
300             if (!value)
301               e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
302             else
303               e = cf_parse_ip(value, OPT_PTR(u32));
304             if (e)
305               opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
306             break;
307           case CT_STRING:
308             if (!value)
309               *OPT_PTR(const char *) = NULL;
310             else
311               *OPT_PTR(const char *) = xstrdup(value);
312             break;
313           default:
314             ASSERT(0);
315         }
316         break;
317       }
318     case OPT_CL_SWITCH:
319       if (*((int *)item->ptr) != -1)
320         opt_failure("Multiple switches: %s", OPT_NAME);
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("User defined type value parsing failed for %s: %s", OPT_NAME, e);
339         break;
340       }
341     default:
342       ASSERT(0);
343   }
344   opt_parsed_count++;
345
346   for (int i=0;i<pre->hooks_after_value_count;i++)
347     pre->hooks_after_value[i]->u.call(item, value, pre->hooks_after_value[i]->ptr);
348 }
349 #undef OPT_NAME
350
351 static int opt_longopt(char ** argv, int index, struct opt_precomputed * pre) {
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_option * opt = opt_find_item_longopt(strndupa(name_in, pos), pre);
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(opt, value, 1);
379   return eaten;
380 }
381
382 static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
383   int chr = 0;
384   struct opt_precomputed_option * opt;
385   while (argv[index][++chr] && (opt = opt_find_item_shortopt(argv[index][chr], pre))) {
386     if (opt->flags & OPT_NO_VALUE) {
387       opt_parse_value(opt, NULL, 0);
388     }
389     else if (opt->flags & OPT_REQUIRED_VALUE) {
390       if (chr == 1 && argv[index][2]) {
391         opt_parse_value(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(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(opt, argv[index] + 2, 0);
406         return 0;
407       }
408       else
409         opt_parse_value(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(char * value, struct opt_precomputed * pre) {
423   opt_positional_count++;
424   struct opt_precomputed_option * opt = opt_find_item_shortopt((opt_positional_count > opt_positional_max ? 256 : opt_positional_count + 256), pre);
425   if (!opt) {
426     ASSERT(opt_positional_count > opt_positional_max);
427     opt_failure("Too many positional args.");
428   }
429
430   opt_parse_value(opt, value, 2);
431 }
432
433 #define OPT_TRAVERSE_SECTIONS \
434     while (item->cls == OPT_CL_SECTION) { \
435       if (stk->next) \
436         stk = stk->next; \
437       else { \
438         struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
439         new_stk->prev = stk; \
440         stk->next = new_stk; \
441         stk = new_stk; \
442       } \
443       stk->this = item; \
444       item = item->u.section->opt; \
445     } \
446     if (item->cls == OPT_CL_END) { \
447       if (!stk->prev) break; \
448       item = stk->this; \
449       stk = stk->prev; \
450       continue; \
451     }
452
453 void opt_parse(const struct opt_section * options, char ** argv) {
454   opt_section_root = options;
455
456   struct opt_stack {
457     struct opt_item * this;
458     struct opt_stack * prev;
459     struct opt_stack * next;
460   } * stk = alloca(sizeof(*stk));
461   stk->this = NULL;
462   stk->prev = NULL;
463   stk->next = NULL;
464
465   struct opt_precomputed * pre = alloca(sizeof(*pre));
466   memset(pre, 0, sizeof (*pre));
467
468   int count = 0;
469   int hooks = 0;
470
471   for (struct opt_item * item = options->opt; ; item++) {
472     OPT_TRAVERSE_SECTIONS;
473     if (item->letter || item->name)
474       count++;
475     if (item->cls == OPT_CL_BOOL)
476       count++;
477     if (item->letter > 256)
478       opt_positional_max++;
479     if (item->cls == OPT_CL_HOOK)
480       hooks++;
481   }
482
483   pre->opts = alloca(sizeof(*pre->opts) * count);
484   pre->shortopt = alloca(sizeof(*pre->shortopt) * (opt_positional_max + 257));
485   memset(pre->shortopt, 0, sizeof(*pre->shortopt) * (opt_positional_max + 257));
486   pre->hooks_before_arg = alloca(sizeof (*pre->hooks_before_arg) * hooks);
487   pre->hooks_before_value = alloca(sizeof (*pre->hooks_before_value) * hooks);
488   pre->hooks_after_value = alloca(sizeof (*pre->hooks_after_value) * hooks);
489
490   pre->hooks_before_arg_count = 0;
491   pre->hooks_before_value_count = 0;
492   pre->hooks_after_value_count = 0;
493
494   pre->opt_count = 0;
495
496   for (struct opt_item * item = options->opt; ; item++) {
497     OPT_TRAVERSE_SECTIONS;
498     if (item->letter || item->name) {
499       struct opt_precomputed_option * opt = xmalloc(sizeof(*opt));
500       opt->pre = pre;
501       opt->item = item;
502       opt->flags = item->flags;
503       opt->count = 0;
504       opt->name = item->name;
505       pre->opts[pre->opt_count++] = opt;
506       if (item->letter)
507         pre->shortopt[(int) item->letter] = opt;
508       OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
509     }
510     if (item->cls == OPT_CL_HOOK) {
511       if (item->flags & OPT_HOOK_BEFORE_ARG)
512         pre->hooks_before_arg[pre->hooks_before_arg_count++] = item;
513       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
514         pre->hooks_before_value[pre->hooks_before_value_count++] = item;
515       else if (item->flags & OPT_HOOK_AFTER_VALUE)
516         pre->hooks_after_value[pre->hooks_after_value_count++] = item;
517       else
518         ASSERT(0);
519     }
520   }
521
522   int force_positional = 0;
523   for (int i=0;argv[i];i++) {
524     for (int j=0;j<pre->hooks_before_arg_count;j++)
525       pre->hooks_before_arg[j]->u.call(NULL, NULL, pre->hooks_before_arg[j]->ptr);
526     if (argv[i][0] != '-' || force_positional) {
527       opt_positional(argv[i], pre);
528     }
529     else {
530       if (argv[i][1] == '-') {
531         if (argv[i][2] == '\0')
532           force_positional++;
533         else
534           i += opt_longopt(argv, i, pre);
535       }
536       else if (argv[i][1])
537         i += opt_shortopt(argv, i, pre);
538       else
539         opt_positional(argv[i], pre);
540     }
541   }
542
543   for (int i=0;i<opt_positional_max+257;i++) {
544     if (!pre->shortopt[i])
545       continue;
546     if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags & OPT_REQUIRED))
547       if (i < 256)
548         opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
549       else
550         opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
551   }
552
553   for (int i=0;i<pre->opt_count;i++) {
554     if (!pre->opts[i])
555       continue;
556     if (!pre->opts[i]->count && (pre->opts[i]->flags & OPT_REQUIRED))
557       opt_failure("Required option --%s not found.\n", pre->opts[i]->item->name);
558   }
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   opt_conf_parsed_count++;
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 }