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/stkstring.h>
14 #include <ucw/strtonum.h>
19 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
20 static void opt_failure(const char * mesg, ...) {
23 vfprintf(stderr, mesg, args);
24 fprintf(stderr, "\n");
26 exit(OPT_EXIT_BAD_ARGS);
30 #define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \
32 if (item->letter >= 256) { \
33 if (flags & OPT_VALUE_FLAGS) \
34 flags &= ~OPT_VALUE_FLAGS; \
35 flags |= OPT_REQUIRED_VALUE; \
37 if (!(flags & OPT_VALUE_FLAGS) && \
38 (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER)) { \
39 fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name); \
42 else if (!(flags & OPT_VALUE_FLAGS)) \
43 flags |= opt_default_value_flags[item->cls]; \
45 #define OPT_ITEM_FLAGS(item) ((item->flags & OPT_VALUE_FLAGS) ? item->flags : item->flags | opt_default_value_flags[item->cls])
47 const struct opt_section * opt_section_root;
49 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
51 void opt_help_internal(const struct opt_section * help) {
55 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
56 if (item->flags & OPT_NO_HELP) continue;
57 if (item->cls == OPT_CL_SECTION) {
65 FOREACHLINE(item->help)
71 struct opt_section * sect;
72 } sections[sections_cnt];
75 const char *lines[lines_cnt][3];
76 memset(lines, 0, sizeof(lines));
79 int linelengths[3] = { -1, -1, -1 };
81 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
82 if (item->flags & OPT_NO_HELP) continue;
84 if (item->cls == OPT_CL_HELP) {
89 #define SPLITLINES(text) do { \
92 for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \
93 lines[line][cell] = b; \
97 if (*e == '\t' && linelengths[cell] < (e - b)) \
98 linelengths[cell] = e-b; \
103 SPLITLINES(item->help);
107 if (item->cls == OPT_CL_SECTION) {
108 sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
112 uns valoff = strchrnul(item->help, '\t') - item->help;
113 uns eol = strchrnul(item->help, '\n') - item->help;
116 #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)))
118 lines[line][1] = stk_printf("--%s%s", item->name, VAL(item));
119 if (linelengths[1] < (int) strlen(lines[line][1]))
120 linelengths[1] = strlen(lines[line][1]);
122 if (linelengths[0] < 0)
126 lines[line][0] = stk_printf("-%c,", item->letter);
127 if (linelengths[0] < (int) strlen(lines[line][0]))
128 linelengths[0] = strlen(lines[line][0]);
133 lines[line][2] = item->help + valoff + 1;
138 if (*(item->help + eol))
139 SPLITLINES(item->help + eol + 1);
144 #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]
145 #define LASTFIELD(k) MIN(strchrnul(lines[i][k], '\t')-lines[i][k],strchrnul(lines[i][k], '\n')-lines[i][k]), lines[i][k]
146 for (int i=0;i<line;i++) {
147 while (s < sections_cnt && sections[s].pos == i) {
148 opt_help_internal(sections[s].sect);
151 if (lines[i][0] == NULL)
153 else if (linelengths[0] == -1 || lines[i][1] == NULL)
154 printf("%.*s\n", LASTFIELD(0));
155 else if (linelengths[1] == -1 || lines[i][2] == NULL)
156 printf("%-*.*s %.*s\n", FIELD(0), LASTFIELD(1));
158 printf("%-*.*s %-*.*s %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
160 while (s < sections_cnt && sections[s].pos == line) {
161 opt_help_internal(sections[s].sect);
166 static int opt_positional_max = 0;
167 static int opt_positional_count = 0;
169 struct opt_precomputed {
170 struct opt_precomputed_option {
171 struct opt_item * item;
176 struct opt_precomputed_option ** shortopt;
180 static struct opt_precomputed_option * opt_find_item_shortopt(int chr, struct opt_precomputed * pre) {
181 struct opt_precomputed_option * candidate = pre->shortopt[chr];
183 opt_failure("Invalid option -%c", chr);
184 if (candidate->count++ && (candidate->flags & OPT_SINGLE))
185 opt_failure("Option -%c appeared the second time.", candidate->item->letter);
189 static struct opt_precomputed_option * opt_find_item_longopt(char * str, struct opt_precomputed * pre) {
190 uns len = strlen(str);
191 struct opt_precomputed_option * candidate = NULL;
193 for (int i=0; i<pre->opt_count; i++) {
194 if (!strncmp(pre->opts[i]->name, str, len)) {
195 if (strlen(pre->opts[i]->name) == len) {
196 if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
197 opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
202 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
204 candidate = pre->opts[i];
206 if (!strncmp("no-", str, 3) && !strncmp(pre->opts[i]->name, str+3, len-3)) {
207 if (strlen(pre->opts[i]->name) == len-3) {
208 if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
209 opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
214 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
216 candidate = pre->opts[i];
223 opt_failure("Invalid option %s.", str);
226 #define OPT_PTR(type) ({ \
228 if (item->flags & OPT_MULTIPLE) { \
232 } * n = xmalloc(sizeof(*n)); \
233 clist_add_tail(item->ptr, &(n->n)); \
239 #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)))
240 static void opt_parse_value(struct opt_precomputed_option * opt, char * value, int longopt) {
241 struct opt_item * item = opt->item;
245 if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
246 *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
247 else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
248 *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
250 opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
255 switch (item->type) {
260 e = cf_parse_int(value, OPT_PTR(int));
262 opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
268 e = cf_parse_u64(value, OPT_PTR(u64));
270 opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
274 *OPT_PTR(double) = NAN;
276 e = cf_parse_double(value, OPT_PTR(double));
278 opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
282 e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
284 e = cf_parse_ip(value, OPT_PTR(u32));
286 opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
290 *OPT_PTR(const char *) = NULL;
292 *OPT_PTR(const char *) = xstrdup(value);
300 if (*((int *)item->ptr) != -1)
301 opt_failure("Multiple switches: %s", OPT_NAME);
303 *((int *)item->ptr) = item->u.value;
306 if (opt->flags & OPT_NEGATIVE)
307 (*((int *)item->ptr))--;
309 (*((int *)item->ptr))++;
312 item->u.call(item, value, item->ptr);
317 e = item->u.utype->parser(value, OPT_PTR(void*));
319 opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
328 static int opt_longopt(char ** argv, int index, struct opt_precomputed * pre) {
330 char * name_in = argv[index] + 2; // skipping the -- on the beginning
331 uns pos = strchrnul(name_in, '=') - name_in;
332 struct opt_precomputed_option * opt = opt_find_item_longopt(strndupa(name_in, pos), pre);
335 if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
337 else if (opt->flags & OPT_REQUIRED_VALUE) {
338 if (pos < strlen(name_in))
339 value = name_in + pos + 1;
341 value = argv[index+1];
343 opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
347 else if (opt->flags & OPT_MAYBE_VALUE) {
348 if (pos < strlen(name_in))
349 value = name_in + pos + 1;
352 if (pos < strlen(name_in))
353 opt_failure("Argument --%s must not have any value.", opt->name);
355 opt_parse_value(opt, value, 1);
359 static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
361 struct opt_precomputed_option * opt;
362 while (argv[index][++chr] && (opt = opt_find_item_shortopt(argv[index][chr], pre))) {
363 if (opt->flags & OPT_NO_VALUE) {
364 opt_parse_value(opt, NULL, 0);
366 else if (opt->flags & OPT_REQUIRED_VALUE) {
367 if (chr == 1 && argv[index][2]) {
368 opt_parse_value(opt, argv[index] + 2, 0);
371 else if (argv[index][chr+1])
372 opt_failure("Option -%c must have a value but found inside a bunch of short opts.", opt->item->letter);
373 else if (!argv[index+1])
374 opt_failure("Option -%c must have a value but nothing supplied.", opt->item->letter);
376 opt_parse_value(opt, argv[index+1], 0);
380 else if (opt->flags & OPT_MAYBE_VALUE) {
381 if (chr == 1 && argv[index][2]) {
382 opt_parse_value(opt, argv[index] + 2, 0);
386 opt_parse_value(opt, NULL, 0);
393 if (argv[index][chr])
394 opt_failure("Unknown option -%c.", argv[index][chr]);
399 static void opt_positional(char * value, struct opt_precomputed * pre) {
400 opt_positional_count++;
401 struct opt_precomputed_option * opt = opt_find_item_shortopt((opt_positional_count > opt_positional_max ? 256 : opt_positional_count + 256), pre);
403 ASSERT(opt_positional_count > opt_positional_max);
404 opt_failure("Too many positional args.");
407 opt_parse_value(opt, value, 2);
410 #define OPT_TRAVERSE_SECTIONS \
411 while (item->cls == OPT_CL_SECTION) { \
415 struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
416 new_stk->prev = stk; \
417 stk->next = new_stk; \
421 item = item->u.section->opt; \
423 if (item->cls == OPT_CL_END) { \
424 if (!stk->prev) break; \
430 void opt_parse(const struct opt_section * options, char ** argv) {
431 opt_section_root = options;
434 struct opt_item * this;
435 struct opt_stack * prev;
436 struct opt_stack * next;
437 } * stk = alloca(sizeof(*stk));
442 struct opt_precomputed * pre = alloca(sizeof(*pre));
443 memset(pre, 0, sizeof (*pre));
447 for (struct opt_item * item = options->opt; ; item++) {
448 OPT_TRAVERSE_SECTIONS;
449 if (item->letter || item->name)
451 if (item->cls == OPT_CL_BOOL)
453 if (item->letter > 256)
454 opt_positional_max++;
457 pre->opts = alloca(sizeof(*pre->opts) * count);
458 pre->shortopt = alloca(sizeof(*pre->shortopt) * (opt_positional_max + 257));
459 memset(pre->shortopt, 0, sizeof(*pre->shortopt) * (opt_positional_max + 257));
463 for (struct opt_item * item = options->opt; ; item++) {
464 OPT_TRAVERSE_SECTIONS;
465 if (item->letter || item->name) {
466 struct opt_precomputed_option * opt = xmalloc(sizeof(*opt));
468 opt->flags = item->flags;
470 opt->name = item->name;
471 pre->opts[pre->opt_count++] = opt;
473 pre->shortopt[(int) item->letter] = opt;
474 OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
478 int force_positional = 0;
479 for (int i=0;argv[i];i++) {
480 if (argv[i][0] != '-' || force_positional) {
481 opt_positional(argv[i], pre);
484 if (argv[i][1] == '-') {
485 if (argv[i][2] == '\0')
488 i += opt_longopt(argv, i, pre);
491 i += opt_shortopt(argv, i, pre);
493 opt_positional(argv[i], pre);
497 for (int i=0;i<opt_positional_max+257;i++) {
498 if (!pre->shortopt[i])
500 if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags & OPT_REQUIRED))
502 opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
504 opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
507 for (int i=0;i<pre->opt_count;i++) {
510 if (!pre->opts[i]->count && (pre->opts[i]->flags & OPT_REQUIRED))
511 opt_failure("Required option --%s not found.\n", pre->opts[i]->item->name);
516 #include <ucw/fastbuf.h>
518 static void show_version(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) {
519 printf("This is a simple tea boiling console v0.1.\n");
523 struct teapot_temperature {
534 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
536 static enum TEAPOT_TYPE {
541 TEAPOT_UNDEFINED = -1
542 } set = TEAPOT_UNDEFINED;
544 static char * teapot_type_str[] = { "standard", "exclusive", "glass", "hands" };
546 static int english = 0;
547 static int sugar = 0;
548 static int verbose = 1;
549 static int with_gas = 0;
550 static clist black_magic;
552 static int water_amount = 0;
553 static char * first_tea = NULL;
555 #define MAX_TEA_COUNT 30
556 static char * tea_list[MAX_TEA_COUNT];
557 static int tea_num = 0;
558 static void add_tea(struct opt_item * opt UNUSED, const char * name, void * data) {
559 char ** tea_list = data;
560 if (tea_num >= MAX_TEA_COUNT) {
561 fprintf(stderr, "Cannot boil more than %d teas.\n", MAX_TEA_COUNT);
562 exit(OPT_EXIT_BAD_ARGS);
564 tea_list[tea_num++] = xstrdup(name);
567 static const char * teapot_temperature_parser(char * in, void * ptr) {
568 struct teapot_temperature * temp = ptr;
570 const char * err = str_to_int(&temp->value, in, &next, 10);
573 if (!strcmp("C", next))
574 temp->scale = TEMP_CELSIUS;
575 else if (!strcmp("F", next))
576 temp->scale = TEMP_FAHRENHEIT;
577 else if (!strcmp("K", next))
578 temp->scale = TEMP_KELVIN;
579 else if (!strcmp("R", next))
580 temp->scale = TEMP_RANKINE;
581 else if (!strcmp("Re", next))
582 temp->scale = TEMP_REAUMUR;
584 fprintf(stderr, "Unknown scale: %s\n", next);
585 exit(OPT_EXIT_BAD_ARGS);
590 static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
591 struct teapot_temperature * temp = ptr;
592 bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
595 static struct cf_user_type teapot_temperature_t = {
596 .size = sizeof(struct teapot_temperature),
597 .name = "teapot_temperature_t",
598 .parser = (cf_parser1*) teapot_temperature_parser,
599 .dumper = (cf_dumper1*) teapot_temperature_dumper
602 static struct opt_section water_options = {
604 OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "<volume>\tAmount of water (in mls; required)"),
605 OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "\tUse water with gas"),
610 static struct opt_section help = {
612 OPT_HELP("A simple tea boiling console."),
613 OPT_HELP("Usage: teapot [options] name-of-the-tea"),
614 OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
615 OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
616 OPT_HELP("At least one kind of tea must be specified."),
618 OPT_HELP("Options:"),
620 OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "\tShow the version"),
622 OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
623 OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
624 OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "\tStandard teapot"),
625 OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "\tExclusive teapot"),
626 OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "\tTransparent glass teapot"),
627 OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "\tUse user's hands as a teapot (a bit dangerous)"),
628 OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE | OPT_REQUIRED,
629 "<value>\tWanted final temperature of the tea to be served (required)\n"
630 "\t\tSupported scales: Celsius [60C], Fahrenheit [140F],\n"
631 "\t\t Kelvin [350K], Rankine [600R] and Reaumur [50Re]\n"
632 "\t\tOnly integer values allowed."),
633 OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
634 OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"),
635 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."),
636 OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"),
637 OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED | OPT_NO_HELP, ""),
638 OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, OPT_NO_HELP, ""),
640 OPT_HELP("Water options:"),
641 OPT_SECTION(water_options),
651 int main(int argc UNUSED, char ** argv)
653 clist_init(&black_magic);
654 opt_parse(&help, argv+1);
656 printf("English style: %s|", english ? "yes" : "no");
658 printf("Sugar: %d teaspoons|", sugar);
660 printf("Chosen teapot: %s|", teapot_type_str[set]);
661 printf("Temperature: %d%s|", temperature.value, temp_scale_str[temperature.scale]);
662 printf("Verbosity: %d|", verbose);
663 CLIST_FOR_EACH(struct intnode *, n, black_magic)
664 printf("Black magic: %d|", n->x);
665 printf("Prayer: %s|", pray ? "yes" : "no");
666 printf("Water amount: %d|", water_amount);
667 printf("Gas: %s|", with_gas ? "yes" : "no");
668 printf("First tea: %s|", first_tea);
669 for (int i=0; i<tea_num; i++)
670 printf("Boiling a tea: %s|", tea_list[i]);
672 printf("Everything OK. Bye.\n");