]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: Simpified and corrected processing of OPT_SINGLE
[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   if (opt->count++ && (opt->flags & OPT_SINGLE))
259     opt_failure("Option %s must be specified at most once.", THIS_OPT);
260
261   for (int i = 0; i < oc->hooks_before_value_count; i++)
262     oc->hooks_before_value[i]->u.call(item, value, oc->hooks_before_value[i]->ptr);
263
264   switch (item->cls) {
265     case OPT_CL_BOOL:
266       if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
267         *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
268       else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
269         *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
270       else
271         opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): 1/0, y/n, yes/no, true/false.", THIS_OPT);
272       break;
273     case OPT_CL_STATIC:
274       {
275         char * e = NULL;
276         switch (item->type) {
277           case CT_INT:
278             if (!value)
279               *OPT_PTR(int) = 0;
280             else
281               e = cf_parse_int(value, OPT_PTR(int));
282             if (e)
283               opt_failure("Integer value parsing failed for %s: %s", THIS_OPT, e);
284             break;
285           case CT_U64:
286             if (!value)
287               *OPT_PTR(u64) = 0;
288             else
289               e = cf_parse_u64(value, OPT_PTR(u64));
290             if (e)
291               opt_failure("Unsigned 64-bit value parsing failed for %s: %s", THIS_OPT, e);
292             break;
293           case CT_DOUBLE:
294             if (!value)
295               *OPT_PTR(double) = NAN;
296             else
297               e = cf_parse_double(value, OPT_PTR(double));
298             if (e)
299               opt_failure("Floating-point value parsing failed for %s: %s", THIS_OPT, e);
300             break;
301           case CT_IP:
302             if (!value)
303               *OPT_PTR(u32) = 0;
304             else
305               e = cf_parse_ip(value, OPT_PTR(u32));
306             if (e)
307               opt_failure("IP address parsing failed for %s: %s", THIS_OPT, e);
308             break;
309           case CT_STRING:
310             if (!value)
311               *OPT_PTR(const char *) = NULL;
312             else
313               *OPT_PTR(const char *) = xstrdup(value);
314             break;
315           default:
316             ASSERT(0);
317         }
318         break;
319       }
320     case OPT_CL_SWITCH:
321       if ((opt->flags & OPT_SINGLE) && *((int *)item->ptr) != -1)
322         opt_failure("Multiple switches: %s", THIS_OPT);
323       else
324         *((int *)item->ptr) = item->u.value;
325       break;
326     case OPT_CL_INC:
327       if (opt->flags & OPT_NEGATIVE)
328         (*((int *)item->ptr))--;
329       else
330         (*((int *)item->ptr))++;
331       break;
332     case OPT_CL_CALL:
333       item->u.call(item, value, item->ptr);
334       break;
335     case OPT_CL_USER:
336       {
337         char * e = NULL;
338         e = item->u.utype->parser(value, OPT_PTR(void*));
339         if (e)
340           opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e);
341         break;
342       }
343     default:
344       ASSERT(0);
345   }
346
347   for (int i = 0;i < oc->hooks_after_value_count; i++)
348     oc->hooks_after_value[i]->u.call(item, value, oc->hooks_after_value[i]->ptr);
349 }
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   opt->flags |= OPT_SEEN_AS_LONG;
359
360   if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3)) {
361     if (name_in[pos])
362       opt_failure("Option --%s must not have any value.", name_in);
363     value = "n";
364   } else if (opt->flags & OPT_REQUIRED_VALUE) {
365     if (name_in[pos])
366       value = name_in + pos + 1;
367     else {
368       value = argv[index+1];
369       if (!value)
370         opt_failure("Option %s must have a value, but nothing supplied.", THIS_OPT);
371       eaten++;
372     }
373   } else if (opt->flags & OPT_MAYBE_VALUE) {
374     if (name_in[pos])
375       value = name_in + pos + 1;
376   } else {
377     if (name_in[pos])
378       opt_failure("Option %s must have no value.", THIS_OPT);
379   }
380   opt_parse_value(oc, opt, value);
381   return eaten;
382 }
383
384 static int opt_shortopt(struct opt_context * oc, char ** argv, int index) {
385   int chr = 0;
386   struct opt_precomputed * opt;
387   int o;
388
389   while (o = argv[index][++chr]) {
390     if (o < 0 || o >= 128)
391       opt_failure("Invalid character 0x%02x in option name. Only ASCII is allowed.", o & 0xff);
392     opt = oc->shortopt[o];
393
394     if (!opt)
395       opt_failure("Unknown option -%c.", o);
396
397     opt->flags &= ~OPT_SEEN_AS_LONG;
398
399     if (opt->flags & OPT_NO_VALUE)
400       opt_parse_value(oc, opt, NULL);
401     else if (opt->flags & OPT_REQUIRED_VALUE) {
402       if (argv[index][chr+1]) {
403         opt_parse_value(oc, opt, argv[index] + chr + 1);
404         return 0;
405       } else if (!argv[index+1])
406         opt_failure("Option -%c must have a value, but nothing supplied.", o);
407       else {
408         opt_parse_value(oc, opt, argv[index+1]);
409         return 1;
410       }
411     } else if (opt->flags & OPT_MAYBE_VALUE) {
412       if (argv[index][chr+1]) {
413         opt_parse_value(oc, opt, argv[index] + chr + 1);
414         return 0;
415       } else
416         opt_parse_value(oc, opt, NULL);
417     } else {
418       ASSERT(0);
419     }
420   }
421
422   return 0;
423 }
424
425 static void opt_positional(struct opt_context * oc, char * value) {
426   oc->positional_count++;
427   uns id = oc->positional_count > oc->positional_max ? OPT_POSITIONAL_TAIL : OPT_POSITIONAL(oc->positional_count);
428   struct opt_precomputed * opt = oc->shortopt[id];
429   if (!opt)
430     opt_failure("Too many positional arguments.");
431   else {
432     opt->flags &= OPT_SEEN_AS_LONG;
433     opt_parse_value(oc, opt, value);
434   }
435 }
436
437 static void opt_count_items(struct opt_context *oc, const struct opt_section *sec)
438 {
439   for (const struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
440     if (item->cls == OPT_CL_SECTION)
441       opt_count_items(oc, item->u.section);
442     else if (item->cls == OPT_CL_HOOK) {
443       if (item->flags & OPT_HOOK_BEFORE_ARG)
444         oc->hooks_before_arg_count++;
445       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
446         oc->hooks_before_value_count++;
447       else if (item->flags & OPT_HOOK_AFTER_VALUE)
448         oc->hooks_after_value_count++;
449       else
450         ASSERT(0);
451     } else if (item->letter || item->name) {
452       oc->opt_count++;
453       if (item->letter > OPT_POSITIONAL_TAIL)
454         oc->positional_max++;
455     }
456   }
457 }
458
459 static void opt_add_default_flags(struct opt_precomputed *opt)
460 {
461   struct opt_item *item = opt->item;
462   uns flags = opt->flags;
463
464   if (item->letter >= OPT_POSITIONAL_TAIL) {
465     flags &= ~OPT_VALUE_FLAGS;
466     flags |= OPT_REQUIRED_VALUE;
467   }
468   if (!(flags & OPT_VALUE_FLAGS)) {
469     ASSERT(item->cls != OPT_CL_CALL && item->cls != OPT_CL_USER);
470     flags |= opt_default_value_flags[item->cls];
471   }
472
473   opt->flags = flags;
474 }
475
476 static void opt_prepare_items(struct opt_context *oc, const struct opt_section *sec)
477 {
478   for (struct opt_item *item = sec->opt; item->cls != OPT_CL_END; item++) {
479     if (item->cls == OPT_CL_SECTION)
480       opt_prepare_items(oc, item->u.section);
481     else if (item->cls == OPT_CL_HOOK) {
482       if (item->flags & OPT_HOOK_BEFORE_ARG)
483         oc->hooks_before_arg[oc->hooks_before_arg_count++] = item;
484       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
485         oc->hooks_before_value[oc->hooks_before_value_count++] = item;
486       else if (item->flags & OPT_HOOK_AFTER_VALUE)
487         oc->hooks_after_value[oc->hooks_after_value_count++] = item;
488       else
489         ASSERT(0);
490     } else if (item->letter || item->name) {
491       struct opt_precomputed * opt = &oc->opts[oc->opt_count++];
492       opt->item = item;
493       opt->flags = item->flags;
494       opt->count = 0;
495       opt->name = item->name;
496       if (item->letter)
497         oc->shortopt[(int) item->letter] = opt;
498       opt_add_default_flags(opt);
499     }
500   }
501 }
502
503 static void opt_check_required(struct opt_context *oc)
504 {
505   for (int i = 0; i < oc->opt_count; i++) {
506     struct opt_precomputed *opt = &oc->opts[i];
507     if (!opt->count && (opt->flags & OPT_REQUIRED)) {
508       struct opt_item *item = opt->item;
509       if (item->letter > OPT_POSITIONAL_TAIL)
510         opt_failure("Required positional argument #%d not found.", item->letter - OPT_POSITIONAL_TAIL);
511       else if (item->letter == OPT_POSITIONAL_TAIL)
512         opt_failure("Required positional argument not found.");
513       else if (item->letter && item->name)
514         opt_failure("Required option -%c/--%s not found.", item->letter, item->name);
515       else if (item->letter)
516         opt_failure("Required option -%c not found.", item->letter);
517       else
518         opt_failure("Required option --%s not found.", item->name);
519     }
520   }
521 }
522
523 void opt_parse(const struct opt_section * options, char ** argv) {
524   struct opt_context * oc = alloca(sizeof(*oc));
525   memset(oc, 0, sizeof (*oc));
526
527   opt_count_items(oc, options);
528   oc->opts = alloca(sizeof(*oc->opts) * oc->opt_count);
529   oc->shortopt = alloca(sizeof(*oc->shortopt) * (oc->positional_max + 257));
530   memset(oc->shortopt, 0, sizeof(*oc->shortopt) * (oc->positional_max + 257));
531   oc->hooks_before_arg = alloca(sizeof (*oc->hooks_before_arg) * oc->hooks_before_arg_count);
532   oc->hooks_before_value = alloca(sizeof (*oc->hooks_before_value) * oc->hooks_before_value_count);
533   oc->hooks_after_value = alloca(sizeof (*oc->hooks_after_value) * oc->hooks_after_value_count);
534
535   oc->opt_count = 0;
536   oc->hooks_before_arg_count = 0;
537   oc->hooks_before_value_count = 0;
538   oc->hooks_after_value_count = 0;
539   opt_prepare_items(oc, options);
540
541   int force_positional = 0;
542   for (int i = 0; argv[i]; i++) {
543     char *arg = argv[i];
544     for (int j = 0; j < oc->hooks_before_arg_count; j++)
545       oc->hooks_before_arg[j]->u.call(NULL, NULL, oc->hooks_before_arg[j]->ptr);
546     if (arg[0] != '-' || force_positional)
547       opt_positional(oc, arg);
548     else {
549       if (arg[1] == '-') {
550         if (arg[2] == '\0')
551           force_positional++;
552         else
553           i += opt_longopt(oc, argv, i);
554       } else if (arg[1])
555         i += opt_shortopt(oc, argv, i);
556       else
557         opt_positional(oc, arg);
558     }
559   }
560
561   opt_check_required(oc);
562 }
563
564 static void opt_conf_end_of_options(struct cf_context *cc) {
565   cf_load_default(cc);
566   if (cc->postpone_commit && cf_close_group())
567     opt_failure("Loading of configuration failed");
568 }
569
570 void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) {
571   struct cf_context *cc = cf_get_context();
572   switch (opt->letter) {
573     case 'S':
574       cf_load_default(cc);
575       if (cf_set(value))
576         opt_failure("Cannot set %s", value);
577       break;
578     case 'C':
579       if (cf_load(value))
580         opt_failure("Cannot load config file %s", value);
581       break;
582 #ifdef CONFIG_UCW_DEBUG
583     case '0':
584       opt_conf_end_of_options(cc);
585       struct fastbuf *b = bfdopen(1, 4096);
586       cf_dump_sections(b);
587       bclose(b);
588       exit(0);
589       break;
590 #endif
591   }
592 }
593
594 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
595   static enum {
596     OPT_CONF_HOOK_BEGIN,
597     OPT_CONF_HOOK_CONFIG,
598     OPT_CONF_HOOK_OTHERS
599   } state = OPT_CONF_HOOK_BEGIN;
600
601   int confopt = 0;
602
603   if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
604     confopt = 1;
605
606   switch (state) {
607     case OPT_CONF_HOOK_BEGIN:
608       if (confopt)
609         state = OPT_CONF_HOOK_CONFIG;
610       else {
611         opt_conf_end_of_options(cf_get_context());
612         state = OPT_CONF_HOOK_OTHERS;
613       }
614       break;
615     case OPT_CONF_HOOK_CONFIG:
616       if (!confopt) {
617         opt_conf_end_of_options(cf_get_context());
618         state = OPT_CONF_HOOK_OTHERS;
619       }
620       break;
621     case OPT_CONF_HOOK_OTHERS:
622       if (confopt)
623         opt_failure("Config options (-C, -S) must stand before other options.");
624       break;
625     default:
626       ASSERT(0);
627   }
628 }