2 * UCW Library -- Parsing of command line options
4 * (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
13 #include <ucw/conf-internal.h>
14 #include <ucw/fastbuf.h>
15 #include <ucw/stkstring.h>
16 #include <ucw/strtonum.h>
21 int opt_parsed_count = 0;
22 int opt_conf_parsed_count = 0;
24 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
25 static void opt_failure(const char * mesg, ...) {
28 vfprintf(stderr, mesg, args);
29 fprintf(stderr, "\n");
31 exit(OPT_EXIT_BAD_ARGS);
35 #define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \
37 if (item->letter >= 256) { \
38 if (flags & OPT_VALUE_FLAGS) \
39 flags &= ~OPT_VALUE_FLAGS; \
40 flags |= OPT_REQUIRED_VALUE; \
42 if (!(flags & OPT_VALUE_FLAGS) && \
43 (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER)) { \
44 fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name); \
47 else if (!(flags & OPT_VALUE_FLAGS)) \
48 flags |= opt_default_value_flags[item->cls]; \
50 #define OPT_ITEM_FLAGS(item) ((item->flags & OPT_VALUE_FLAGS) ? item->flags : item->flags | opt_default_value_flags[item->cls])
52 const struct opt_section * opt_section_root;
54 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
56 void opt_help_internal(const struct opt_section * help) {
60 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
61 if (item->flags & OPT_NO_HELP) continue;
62 if (item->cls == OPT_CL_SECTION) {
70 FOREACHLINE(item->help)
76 struct opt_section * sect;
77 } sections[sections_cnt];
80 const char *lines[lines_cnt][3];
81 memset(lines, 0, sizeof(lines));
84 int linelengths[3] = { -1, -1, -1 };
86 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
87 if (item->flags & OPT_NO_HELP) continue;
89 if (item->cls == OPT_CL_HELP) {
94 #define SPLITLINES(text) do { \
97 for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \
98 lines[line][cell] = b; \
102 if (*e == '\t' && linelengths[cell] < (e - b)) \
103 linelengths[cell] = e-b; \
108 SPLITLINES(item->help);
112 if (item->cls == OPT_CL_SECTION) {
113 sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
117 uns valoff = strchrnul(item->help, '\t') - item->help;
118 uns eol = strchrnul(item->help, '\n') - item->help;
121 #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)))
123 lines[line][1] = stk_printf("--%s%s", item->name, VAL(item));
124 if (linelengths[1] < (int) strlen(lines[line][1]))
125 linelengths[1] = strlen(lines[line][1]);
127 if (linelengths[0] < 0)
131 lines[line][0] = stk_printf("-%c,", item->letter);
132 if (linelengths[0] < (int) strlen(lines[line][0]))
133 linelengths[0] = strlen(lines[line][0]);
138 lines[line][2] = item->help + valoff + 1;
143 if (*(item->help + eol))
144 SPLITLINES(item->help + eol + 1);
149 #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]
150 #define LASTFIELD(k) MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k]
151 for (int i=0;i<line;i++) {
152 while (s < sections_cnt && sections[s].pos == i) {
153 opt_help_internal(sections[s].sect);
156 if (lines[i][0] == NULL)
158 else if (linelengths[0] == -1 || lines[i][1] == NULL)
159 printf("%.*s\n", LASTFIELD(0));
160 else if (linelengths[1] == -1 || lines[i][2] == NULL)
161 printf("%-*.*s %.*s\n", FIELD(0), LASTFIELD(1));
163 printf("%-*.*s %-*.*s %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
165 while (s < sections_cnt && sections[s].pos == line) {
166 opt_help_internal(sections[s].sect);
171 static int opt_positional_max = 0;
172 static int opt_positional_count = 0;
174 struct opt_precomputed {
175 struct opt_precomputed_option {
176 struct opt_item * item;
181 struct opt_precomputed_option ** shortopt;
182 struct opt_item ** hooks_before_arg;
183 struct opt_item ** hooks_before_value;
184 struct opt_item ** hooks_after_value;
186 short hooks_before_arg_count;
187 short hooks_before_value_count;
188 short hooks_after_value_count;
191 static struct opt_precomputed_option * opt_find_item_shortopt(int chr, struct opt_precomputed * pre) {
192 struct opt_precomputed_option * candidate = pre->shortopt[chr];
194 opt_failure("Invalid option -%c", chr);
195 if (candidate->count++ && (candidate->flags & OPT_SINGLE))
196 opt_failure("Option -%c appeared the second time.", candidate->item->letter);
200 static struct opt_precomputed_option * opt_find_item_longopt(char * str, struct opt_precomputed * pre) {
201 uns len = strlen(str);
202 struct opt_precomputed_option * candidate = NULL;
204 for (int i=0; i<pre->opt_count; i++) {
205 if (!strncmp(pre->opts[i]->name, str, len)) {
206 if (strlen(pre->opts[i]->name) == len) {
207 if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
208 opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
213 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
215 candidate = pre->opts[i];
217 if (!strncmp("no-", str, 3) && !strncmp(pre->opts[i]->name, str+3, len-3)) {
218 if (strlen(pre->opts[i]->name) == len-3) {
219 if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
220 opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
225 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
227 candidate = pre->opts[i];
234 opt_failure("Invalid option %s.", str);
237 #define OPT_PTR(type) ({ \
239 if (item->flags & OPT_MULTIPLE) { \
243 } * n = xmalloc(sizeof(*n)); \
244 clist_add_tail(item->ptr, &(n->n)); \
250 #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)))
251 static void opt_parse_value(struct opt_precomputed_option * opt, char * value, int longopt) {
252 struct opt_item * item = opt->item;
256 if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
257 *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
258 else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
259 *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
261 opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
266 switch (item->type) {
271 e = cf_parse_int(value, OPT_PTR(int));
273 opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
279 e = cf_parse_u64(value, OPT_PTR(u64));
281 opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
285 *OPT_PTR(double) = NAN;
287 e = cf_parse_double(value, OPT_PTR(double));
289 opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
293 e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
295 e = cf_parse_ip(value, OPT_PTR(u32));
297 opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
301 *OPT_PTR(const char *) = NULL;
303 *OPT_PTR(const char *) = xstrdup(value);
311 if (*((int *)item->ptr) != -1)
312 opt_failure("Multiple switches: %s", OPT_NAME);
314 *((int *)item->ptr) = item->u.value;
317 if (opt->flags & OPT_NEGATIVE)
318 (*((int *)item->ptr))--;
320 (*((int *)item->ptr))++;
323 item->u.call(item, value, item->ptr);
328 e = item->u.utype->parser(value, OPT_PTR(void*));
330 opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
340 static int opt_longopt(char ** argv, int index, struct opt_precomputed * pre) {
342 char * name_in = argv[index] + 2; // skipping the -- on the beginning
343 uns pos = strchrnul(name_in, '=') - name_in;
344 struct opt_precomputed_option * opt = opt_find_item_longopt(strndupa(name_in, pos), pre);
347 if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
349 else if (opt->flags & OPT_REQUIRED_VALUE) {
350 if (pos < strlen(name_in))
351 value = name_in + pos + 1;
353 value = argv[index+1];
355 opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
359 else if (opt->flags & OPT_MAYBE_VALUE) {
360 if (pos < strlen(name_in))
361 value = name_in + pos + 1;
364 if (pos < strlen(name_in))
365 opt_failure("Argument --%s must not have any value.", opt->name);
367 opt_parse_value(opt, value, 1);
371 static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
373 struct opt_precomputed_option * opt;
374 while (argv[index][++chr] && (opt = opt_find_item_shortopt(argv[index][chr], pre))) {
375 if (opt->flags & OPT_NO_VALUE) {
376 opt_parse_value(opt, NULL, 0);
378 else if (opt->flags & OPT_REQUIRED_VALUE) {
379 if (chr == 1 && argv[index][2]) {
380 opt_parse_value(opt, argv[index] + 2, 0);
383 else if (argv[index][chr+1])
384 opt_failure("Option -%c must have a value but found inside a bunch of short opts.", opt->item->letter);
385 else if (!argv[index+1])
386 opt_failure("Option -%c must have a value but nothing supplied.", opt->item->letter);
388 opt_parse_value(opt, argv[index+1], 0);
392 else if (opt->flags & OPT_MAYBE_VALUE) {
393 if (chr == 1 && argv[index][2]) {
394 opt_parse_value(opt, argv[index] + 2, 0);
398 opt_parse_value(opt, NULL, 0);
405 if (argv[index][chr])
406 opt_failure("Unknown option -%c.", argv[index][chr]);
411 static void opt_positional(char * value, struct opt_precomputed * pre) {
412 opt_positional_count++;
413 struct opt_precomputed_option * opt = opt_find_item_shortopt((opt_positional_count > opt_positional_max ? 256 : opt_positional_count + 256), pre);
415 ASSERT(opt_positional_count > opt_positional_max);
416 opt_failure("Too many positional args.");
419 opt_parse_value(opt, value, 2);
422 #define OPT_TRAVERSE_SECTIONS \
423 while (item->cls == OPT_CL_SECTION) { \
427 struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
428 new_stk->prev = stk; \
429 stk->next = new_stk; \
433 item = item->u.section->opt; \
435 if (item->cls == OPT_CL_END) { \
436 if (!stk->prev) break; \
442 void opt_parse(const struct opt_section * options, char ** argv) {
443 opt_section_root = options;
446 struct opt_item * this;
447 struct opt_stack * prev;
448 struct opt_stack * next;
449 } * stk = alloca(sizeof(*stk));
454 struct opt_precomputed * pre = alloca(sizeof(*pre));
455 memset(pre, 0, sizeof (*pre));
460 for (struct opt_item * item = options->opt; ; item++) {
461 OPT_TRAVERSE_SECTIONS;
462 if (item->letter || item->name)
464 if (item->cls == OPT_CL_BOOL)
466 if (item->letter > 256)
467 opt_positional_max++;
468 if (item->cls == OPT_CL_HOOK)
472 pre->opts = alloca(sizeof(*pre->opts) * count);
473 pre->shortopt = alloca(sizeof(*pre->shortopt) * (opt_positional_max + 257));
474 memset(pre->shortopt, 0, sizeof(*pre->shortopt) * (opt_positional_max + 257));
475 pre->hooks_before_arg = alloca(sizeof (*pre->hooks_before_arg) * hooks);
476 pre->hooks_before_value = alloca(sizeof (*pre->hooks_before_value) * hooks);
477 pre->hooks_after_value = alloca(sizeof (*pre->hooks_after_value) * hooks);
479 pre->hooks_before_arg_count = 0;
480 pre->hooks_before_value_count = 0;
481 pre->hooks_after_value_count = 0;
485 for (struct opt_item * item = options->opt; ; item++) {
486 OPT_TRAVERSE_SECTIONS;
487 if (item->letter || item->name) {
488 struct opt_precomputed_option * opt = xmalloc(sizeof(*opt));
490 opt->flags = item->flags;
492 opt->name = item->name;
493 pre->opts[pre->opt_count++] = opt;
495 pre->shortopt[(int) item->letter] = opt;
496 OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
498 if (item->cls == OPT_CL_HOOK) {
499 if (item->flags & OPT_HOOK_BEFORE_ARG)
500 pre->hooks_before_arg[pre->hooks_before_arg_count++] = item;
501 else if (item->flags & OPT_HOOK_BEFORE_VALUE)
502 pre->hooks_before_value[pre->hooks_before_value_count++] = item;
503 else if (item->flags & OPT_HOOK_AFTER_VALUE)
504 pre->hooks_after_value[pre->hooks_after_value_count++] = item;
510 int force_positional = 0;
511 for (int i=0;argv[i];i++) {
512 if (argv[i][0] != '-' || force_positional) {
513 opt_positional(argv[i], pre);
516 if (argv[i][1] == '-') {
517 if (argv[i][2] == '\0')
520 i += opt_longopt(argv, i, pre);
523 i += opt_shortopt(argv, i, pre);
525 opt_positional(argv[i], pre);
529 for (int i=0;i<opt_positional_max+257;i++) {
530 if (!pre->shortopt[i])
532 if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags & OPT_REQUIRED))
534 opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
536 opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
539 for (int i=0;i<pre->opt_count;i++) {
542 if (!pre->opts[i]->count && (pre->opts[i]->flags & OPT_REQUIRED))
543 opt_failure("Required option --%s not found.\n", pre->opts[i]->item->name);
547 void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) {
548 if (opt_parsed_count > opt_conf_parsed_count)
549 opt_failure("Config options (-C, -S) must stand before other options.");
551 struct cf_context *cc = cf_get_context();
552 switch(opt->letter) {
556 opt_failure("Cannot set %s", value);
560 opt_failure("Cannot load config file %s", value);
562 #ifdef CONFIG_UCW_DEBUG
565 if (cc->postpone_commit && cf_close_group())
566 opt_failure("Loading of configuration failed");
567 struct fastbuf *b = bfdopen(1, 4096);
575 opt_conf_parsed_count++;
579 #include <ucw/fastbuf.h>
581 static void show_version(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) {
582 printf("This is a simple tea boiling console v0.1.\n");
586 struct teapot_temperature {
597 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
599 static enum TEAPOT_TYPE {
604 TEAPOT_UNDEFINED = -1
605 } set = TEAPOT_UNDEFINED;
607 static char * teapot_type_str[] = { "standard", "exclusive", "glass", "hands" };
609 static int english = 0;
610 static int sugar = 0;
611 static int verbose = 1;
612 static int with_gas = 0;
613 static clist black_magic;
615 static int water_amount = 0;
616 static char * first_tea = NULL;
618 #define MAX_TEA_COUNT 30
619 static char * tea_list[MAX_TEA_COUNT];
620 static int tea_num = 0;
621 static void add_tea(struct opt_item * opt UNUSED, const char * name, void * data) {
622 char ** tea_list = data;
623 if (tea_num >= MAX_TEA_COUNT) {
624 fprintf(stderr, "Cannot boil more than %d teas.\n", MAX_TEA_COUNT);
625 exit(OPT_EXIT_BAD_ARGS);
627 tea_list[tea_num++] = xstrdup(name);
630 static const char * teapot_temperature_parser(char * in, void * ptr) {
631 struct teapot_temperature * temp = ptr;
633 const char * err = str_to_int(&temp->value, in, &next, 10);
636 if (!strcmp("C", next))
637 temp->scale = TEMP_CELSIUS;
638 else if (!strcmp("F", next))
639 temp->scale = TEMP_FAHRENHEIT;
640 else if (!strcmp("K", next))
641 temp->scale = TEMP_KELVIN;
642 else if (!strcmp("R", next))
643 temp->scale = TEMP_RANKINE;
644 else if (!strcmp("Re", next))
645 temp->scale = TEMP_REAUMUR;
647 fprintf(stderr, "Unknown scale: %s\n", next);
648 exit(OPT_EXIT_BAD_ARGS);
653 static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
654 struct teapot_temperature * temp = ptr;
655 bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
658 static struct cf_user_type teapot_temperature_t = {
659 .size = sizeof(struct teapot_temperature),
660 .name = "teapot_temperature_t",
661 .parser = (cf_parser1*) teapot_temperature_parser,
662 .dumper = (cf_dumper1*) teapot_temperature_dumper
665 static struct opt_section water_options = {
667 OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "<volume>\tAmount of water (in mls; required)"),
668 OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "\tUse water with gas"),
673 static struct opt_section help = {
675 OPT_HELP("A simple tea boiling console."),
676 OPT_HELP("Usage: teapot [options] name-of-the-tea"),
677 OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
678 OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
679 OPT_HELP("At least one kind of tea must be specified."),
681 OPT_HELP("Options:"),
683 OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "\tShow the version"),
685 OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
686 OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
687 OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "\tStandard teapot"),
688 OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "\tExclusive teapot"),
689 OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "\tTransparent glass teapot"),
690 OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "\tUse user's hands as a teapot (a bit dangerous)"),
691 OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE | OPT_REQUIRED,
692 "<value>\tWanted final temperature of the tea to be served (required)\n"
693 "\t\tSupported scales: Celsius [60C], Fahrenheit [140F],\n"
694 "\t\t Kelvin [350K], Rankine [600R] and Reaumur [50Re]\n"
695 "\t\tOnly integer values allowed."),
696 OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
697 OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"),
698 OPT_INT('b', "black-magic", black_magic, OPT_MULTIPLE, "<strength>\tUse black magic to make the tea extraordinary delicious.\n\t\tMay be specified more than once to describe the amounts of black magic to be invoked in each step of tea boiling."),
699 OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"),
700 OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED | OPT_NO_HELP, ""),
701 OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, OPT_NO_HELP, ""),
703 OPT_HELP("Water options:"),
704 OPT_SECTION(water_options),
714 int main(int argc UNUSED, char ** argv)
716 clist_init(&black_magic);
717 opt_parse(&help, argv+1);
719 printf("English style: %s|", english ? "yes" : "no");
721 printf("Sugar: %d teaspoons|", sugar);
723 printf("Chosen teapot: %s|", teapot_type_str[set]);
724 printf("Temperature: %d%s|", temperature.value, temp_scale_str[temperature.scale]);
725 printf("Verbosity: %d|", verbose);
726 CLIST_FOR_EACH(struct intnode *, n, black_magic)
727 printf("Black magic: %d|", n->x);
728 printf("Prayer: %s|", pray ? "yes" : "no");
729 printf("Water amount: %d|", water_amount);
730 printf("Gas: %s|", with_gas ? "yes" : "no");
731 printf("First tea: %s|", first_tea);
732 for (int i=0; i<tea_num; i++)
733 printf("Boiling a tea: %s|", tea_list[i]);
735 printf("Everything OK. Bye.\n");