2 * UCW Library -- Parsing of command line options
4 * (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
5 * (c) 2014 Martin Mares <mj@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
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>
25 * Value flags defaults
26 * ~~~~~~~~~~~~~~~~~~~~
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.
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,
44 struct opt_precomputed {
45 struct opt_item * item;
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;
58 short hooks_before_arg_count;
59 short hooks_before_value_count;
60 short hooks_after_value_count;
65 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
66 static void opt_failure(const char * mesg, ...) {
69 vfprintf(stderr, mesg, args);
70 fprintf(stderr, "\n");
72 exit(OPT_EXIT_BAD_ARGS);
75 static char *opt_name(struct opt_context *oc, struct opt_precomputed *opt)
77 struct opt_item *item = opt->item;
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);
84 res = stk_printf("-%c", item->letter);
88 #define THIS_OPT opt_name(oc, opt)
90 static void opt_precompute(struct opt_precomputed *opt, struct opt_item *item)
94 opt->name = item->name;
95 uns flags = item->flags;
97 if (item->letter >= OPT_POSITIONAL_TAIL) {
98 flags &= ~OPT_VALUE_FLAGS;
99 flags |= OPT_REQUIRED_VALUE;
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];
109 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
112 struct mempool *pool;
113 struct help_line *lines; // A growing array of lines
121 static void opt_help_scan_item(struct help *h, struct opt_precomputed *opt)
123 struct opt_item *item = opt->item;
125 if (opt->flags & OPT_NO_HELP)
128 if (item->cls == OPT_CL_HELP) {
129 struct help_line *l = GARY_PUSH(h->lines, 1);
130 l->extra = item->help ? : "";
134 if (item->letter >= OPT_POSITIONAL_TAIL)
137 struct help_line *first = GARY_PUSH(h->lines, 1);
139 char *text = mp_strdup(h->pool, item->help);
140 struct help_line *l = first;
142 char *eol = strchr(text, '\n');
146 int field = (l == first ? 1 : 0);
149 char *tab = strchr(f, '\t');
153 l->fields[field++] = f;
159 l = GARY_PUSH(h->lines, 1);
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);
174 first->fields[0] = mp_printf(h->pool, "-%c, ", item->letter);
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;
185 static void opt_help_scan(struct help *h, const struct opt_section *sec)
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);
191 struct opt_precomputed opt;
192 opt_precompute(&opt, item);
193 opt_help_scan_item(h, &opt);
198 void opt_help(const struct opt_section * sec) {
201 h.pool = mp_new(4096);
202 GARY_INIT_ZERO(h.lines, 0);
203 opt_help_scan(&h, sec);
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++) {
213 uns w = strlen(l->fields[f]);
214 widths[f] = MAX(widths[f], w);
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.
222 widths[1] = MAX(widths[1], widths[0] - 4);
228 for (uns i=0; i<n; i++) {
229 struct help_line *l = &h.lines[i];
234 for (uns f=0; f<3; f++) {
236 t -= printf("%s", l->fields[f]);
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;
255 for (int i = 0; i < oc->opt_count; i++) {
256 struct opt_precomputed *opt = &oc->opts[i];
260 if (!strncmp(opt->name, str, len)) {
261 if (strlen(opt->name) == len)
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)
270 opt_failure("Ambiguous option --%s: matches both --%s and --%s.", str, candidate->name, opt->name);
278 opt_failure("Invalid option --%s.", str);
281 // FIXME: Use simple-lists?
282 #define OPT_PTR(type) ({ \
284 if (item->flags & OPT_MULTIPLE) { \
288 } * n = xmalloc(sizeof(*n)); \
289 clist_add_tail(item->ptr, &(n->n)); \
295 static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value) {
296 struct opt_item * item = opt->item;
298 if (opt->count++ && (opt->flags & OPT_SINGLE))
299 opt_failure("Option %s must be specified at most once.", THIS_OPT);
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);
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));
311 opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): 1/0, y/n, yes/no, true/false.", THIS_OPT);
316 switch (item->type) {
321 e = cf_parse_int(value, OPT_PTR(int));
323 opt_failure("Integer value parsing failed for %s: %s", THIS_OPT, e);
329 e = cf_parse_u64(value, OPT_PTR(u64));
331 opt_failure("Unsigned 64-bit value parsing failed for %s: %s", THIS_OPT, e);
335 *OPT_PTR(double) = NAN;
337 e = cf_parse_double(value, OPT_PTR(double));
339 opt_failure("Floating-point value parsing failed for %s: %s", THIS_OPT, e);
345 e = cf_parse_ip(value, OPT_PTR(u32));
347 opt_failure("IP address parsing failed for %s: %s", THIS_OPT, e);
351 *OPT_PTR(const char *) = NULL;
353 *OPT_PTR(const char *) = xstrdup(value);
361 if ((opt->flags & OPT_SINGLE) && *((int *)item->ptr) != -1)
362 opt_failure("Multiple switches: %s", THIS_OPT);
364 *((int *)item->ptr) = item->u.value;
367 if (opt->flags & OPT_NEGATIVE)
368 (*((int *)item->ptr))--;
370 (*((int *)item->ptr))++;
373 item->u.call(item, value, item->ptr);
378 e = item->u.utype->parser(value, OPT_PTR(void*));
380 opt_failure("Cannot parse the value of %s: %s", THIS_OPT, e);
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);
391 static int opt_longopt(struct opt_context * oc, char ** argv, int index) {
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));
398 opt->flags |= OPT_SEEN_AS_LONG;
400 if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3)) {
402 opt_failure("Option --%s must not have any value.", name_in);
404 } else if (opt->flags & OPT_REQUIRED_VALUE) {
406 value = name_in + pos + 1;
408 value = argv[index+1];
410 opt_failure("Option %s must have a value, but nothing supplied.", THIS_OPT);
413 } else if (opt->flags & OPT_MAYBE_VALUE) {
415 value = name_in + pos + 1;
418 opt_failure("Option %s must have no value.", THIS_OPT);
420 opt_parse_value(oc, opt, value);
424 static int opt_shortopt(struct opt_context * oc, char ** argv, int index) {
426 struct opt_precomputed * opt;
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];
435 opt_failure("Unknown option -%c.", o);
437 opt->flags &= ~OPT_SEEN_AS_LONG;
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);
445 } else if (!argv[index+1])
446 opt_failure("Option -%c must have a value, but nothing supplied.", o);
448 opt_parse_value(oc, opt, argv[index+1]);
451 } else if (opt->flags & OPT_MAYBE_VALUE) {
452 if (argv[index][chr+1]) {
453 opt_parse_value(oc, opt, argv[index] + chr + 1);
456 opt_parse_value(oc, opt, NULL);
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];
470 opt_failure("Too many positional arguments.");
472 opt->flags &= OPT_SEEN_AS_LONG;
473 opt_parse_value(oc, opt, value);
477 static void opt_count_items(struct opt_context *oc, const struct opt_section *sec)
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++;
491 } else if (item->letter || item->name) {
493 if (item->letter > OPT_POSITIONAL_TAIL)
494 oc->positional_max++;
499 static void opt_prepare_items(struct opt_context *oc, const struct opt_section *sec)
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;
513 } else if (item->letter || item->name) {
514 struct opt_precomputed * opt = &oc->opts[oc->opt_count++];
515 opt_precompute(opt, item);
517 oc->shortopt[(int) item->letter] = opt;
522 static void opt_check_required(struct opt_context *oc)
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);
537 opt_failure("Required option --%s not found.", item->name);
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));
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);
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);
560 int force_positional = 0;
561 for (int i = 0; argv[i]; 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);
572 i += opt_longopt(oc, argv, i);
574 i += opt_shortopt(oc, argv, i);
576 opt_positional(oc, arg);
580 opt_check_required(oc);
583 static void opt_conf_end_of_options(struct cf_context *cc) {
585 if (cc->postpone_commit && cf_close_group())
586 opt_failure("Loading of configuration failed");
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) {
595 opt_failure("Cannot set %s", value);
599 opt_failure("Cannot load config file %s", value);
601 #ifdef CONFIG_UCW_DEBUG
603 opt_conf_end_of_options(cc);
604 struct fastbuf *b = bfdopen(1, 4096);
613 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
616 OPT_CONF_HOOK_CONFIG,
618 } state = OPT_CONF_HOOK_BEGIN;
622 if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
626 case OPT_CONF_HOOK_BEGIN:
628 state = OPT_CONF_HOOK_CONFIG;
630 opt_conf_end_of_options(cf_get_context());
631 state = OPT_CONF_HOOK_OTHERS;
634 case OPT_CONF_HOOK_CONFIG:
636 opt_conf_end_of_options(cf_get_context());
637 state = OPT_CONF_HOOK_OTHERS;
640 case OPT_CONF_HOOK_OTHERS:
642 opt_failure("Config options (-C, -S) must stand before other options.");