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);
32 va_end(args); // FIXME: Does this make a sense after exit()?
35 // FIXME: This could be an inline function, couldn't it?
36 #define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \
38 if (item->letter >= 256) { \
39 if (flags & OPT_VALUE_FLAGS) /* FIXME: Redundant condition */ \
40 flags &= ~OPT_VALUE_FLAGS; \
41 flags |= OPT_REQUIRED_VALUE; \
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); \
48 else if (!(flags & OPT_VALUE_FLAGS)) /* FIXME: Streamline the conditions */ \
49 flags |= opt_default_value_flags[item->cls]; \
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])
54 const struct opt_section * opt_section_root;
56 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
58 void opt_help_internal(const struct opt_section * help) {
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) {
72 FOREACHLINE(item->help)
78 struct opt_section * sect;
79 } sections[sections_cnt];
82 const char *lines[lines_cnt][3];
83 memset(lines, 0, sizeof(lines));
86 int linelengths[3] = { -1, -1, -1 };
88 for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
89 if (item->flags & OPT_NO_HELP) continue;
91 if (item->cls == OPT_CL_HELP) {
96 #define SPLITLINES(text) do { \
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; \
104 if (*e == '\t' && linelengths[cell] < (e - b)) \
105 linelengths[cell] = e-b; \
110 SPLITLINES(item->help);
114 if (item->cls == OPT_CL_SECTION) {
115 sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
119 uns valoff = strchrnul(item->help, '\t') - item->help;
120 uns eol = strchrnul(item->help, '\n') - item->help;
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)))
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]);
129 if (linelengths[0] < 0)
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]);
140 lines[line][2] = item->help + valoff + 1;
145 if (*(item->help + eol))
146 SPLITLINES(item->help + eol + 1);
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);
158 if (lines[i][0] == NULL)
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));
165 printf("%-*.*s %-*.*s %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
167 while (s < sections_cnt && sections[s].pos == line) {
168 opt_help_internal(sections[s].sect);
173 static int opt_positional_max = 0;
174 static int opt_positional_count = 0;
176 struct opt_precomputed {
177 struct opt_precomputed_option {
178 struct opt_precomputed *pre;
179 struct opt_item * item;
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;
189 short hooks_before_arg_count;
190 short hooks_before_value_count;
191 short hooks_after_value_count;
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];
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);
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;
207 for (int i=0; i<pre->opt_count; i++) {
208 if (!pre->opts[i]->name)
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);
218 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
220 candidate = pre->opts[i];
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);
230 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
232 candidate = pre->opts[i];
239 opt_failure("Invalid option %s.", str);
242 #define OPT_PTR(type) ({ \
244 if (item->flags & OPT_MULTIPLE) { \
248 } * n = xmalloc(sizeof(*n)); \
249 clist_add_tail(item->ptr, &(n->n)); \
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);
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));
269 opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
274 switch (item->type) {
279 e = cf_parse_int(value, OPT_PTR(int));
281 opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
287 e = cf_parse_u64(value, OPT_PTR(u64));
289 opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
293 *OPT_PTR(double) = NAN;
295 e = cf_parse_double(value, OPT_PTR(double));
297 opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
301 e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
303 e = cf_parse_ip(value, OPT_PTR(u32));
305 opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
309 *OPT_PTR(const char *) = NULL;
311 *OPT_PTR(const char *) = xstrdup(value);
319 if (*((int *)item->ptr) != -1)
320 opt_failure("Multiple switches: %s", OPT_NAME);
322 *((int *)item->ptr) = item->u.value;
325 if (opt->flags & OPT_NEGATIVE)
326 (*((int *)item->ptr))--;
328 (*((int *)item->ptr))++;
331 item->u.call(item, value, item->ptr);
336 e = item->u.utype->parser(value, OPT_PTR(void*));
338 opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
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);
351 static int opt_longopt(char ** argv, int index, struct opt_precomputed * pre) {
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);
358 if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
360 else if (opt->flags & OPT_REQUIRED_VALUE) {
361 if (pos < strlen(name_in))
362 value = name_in + pos + 1;
364 value = argv[index+1];
366 opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
370 else if (opt->flags & OPT_MAYBE_VALUE) {
371 if (pos < strlen(name_in))
372 value = name_in + pos + 1;
375 if (pos < strlen(name_in))
376 opt_failure("Argument --%s must not have any value.", opt->name);
378 opt_parse_value(opt, value, 1);
382 static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
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);
389 else if (opt->flags & OPT_REQUIRED_VALUE) {
390 if (chr == 1 && argv[index][2]) {
391 opt_parse_value(opt, argv[index] + 2, 0);
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);
399 opt_parse_value(opt, argv[index+1], 0);
403 else if (opt->flags & OPT_MAYBE_VALUE) {
404 if (chr == 1 && argv[index][2]) {
405 opt_parse_value(opt, argv[index] + 2, 0);
409 opt_parse_value(opt, NULL, 0);
416 if (argv[index][chr])
417 opt_failure("Unknown option -%c.", argv[index][chr]);
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);
426 ASSERT(opt_positional_count > opt_positional_max);
427 opt_failure("Too many positional args.");
430 opt_parse_value(opt, value, 2);
433 #define OPT_TRAVERSE_SECTIONS \
434 while (item->cls == OPT_CL_SECTION) { \
438 struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
439 new_stk->prev = stk; \
440 stk->next = new_stk; \
444 item = item->u.section->opt; \
446 if (item->cls == OPT_CL_END) { \
447 if (!stk->prev) break; \
453 void opt_parse(const struct opt_section * options, char ** argv) {
454 opt_section_root = options;
457 struct opt_item * this;
458 struct opt_stack * prev;
459 struct opt_stack * next;
460 } * stk = alloca(sizeof(*stk));
465 struct opt_precomputed * pre = alloca(sizeof(*pre));
466 memset(pre, 0, sizeof (*pre));
471 for (struct opt_item * item = options->opt; ; item++) {
472 OPT_TRAVERSE_SECTIONS;
473 if (item->letter || item->name)
475 if (item->cls == OPT_CL_BOOL)
477 if (item->letter > 256)
478 opt_positional_max++;
479 if (item->cls == OPT_CL_HOOK)
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);
490 pre->hooks_before_arg_count = 0;
491 pre->hooks_before_value_count = 0;
492 pre->hooks_after_value_count = 0;
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));
502 opt->flags = item->flags;
504 opt->name = item->name;
505 pre->opts[pre->opt_count++] = opt;
507 pre->shortopt[(int) item->letter] = opt;
508 OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
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;
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);
530 if (argv[i][1] == '-') {
531 if (argv[i][2] == '\0')
534 i += opt_longopt(argv, i, pre);
537 i += opt_shortopt(argv, i, pre);
539 opt_positional(argv[i], pre);
543 for (int i=0;i<opt_positional_max+257;i++) {
544 if (!pre->shortopt[i])
546 if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags & OPT_REQUIRED))
548 opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
550 opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
553 for (int i=0;i<pre->opt_count;i++) {
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);
561 static void opt_conf_end_of_options(struct cf_context *cc) {
563 if (cc->postpone_commit && cf_close_group())
564 opt_failure("Loading of configuration failed");
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) {
573 opt_failure("Cannot set %s", value);
577 opt_failure("Cannot load config file %s", value);
579 #ifdef CONFIG_UCW_DEBUG
581 opt_conf_end_of_options(cc);
582 struct fastbuf *b = bfdopen(1, 4096);
590 opt_conf_parsed_count++;
593 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
596 OPT_CONF_HOOK_CONFIG,
598 } state = OPT_CONF_HOOK_BEGIN;
602 if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
606 case OPT_CONF_HOOK_BEGIN:
608 state = OPT_CONF_HOOK_CONFIG;
610 opt_conf_end_of_options(cf_get_context());
611 state = OPT_CONF_HOOK_OTHERS;
614 case OPT_CONF_HOOK_CONFIG:
616 opt_conf_end_of_options(cf_get_context());
617 state = OPT_CONF_HOOK_OTHERS;
620 case OPT_CONF_HOOK_OTHERS:
622 opt_failure("Config options (-C, -S) must stand before other options.");
630 #include <ucw/fastbuf.h>
632 static void show_version(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) {
633 printf("This is a simple tea boiling console v0.1.\n");
637 struct teapot_temperature {
648 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
650 static enum TEAPOT_TYPE {
655 TEAPOT_UNDEFINED = -1
656 } set = TEAPOT_UNDEFINED;
658 static char * teapot_type_str[] = { "standard", "exclusive", "glass", "hands" };
660 static int show_hooks = 0;
661 static int english = 0;
662 static int sugar = 0;
663 static int verbose = 1;
664 static int with_gas = 0;
665 static clist black_magic;
667 static int water_amount = 0;
668 static char * first_tea = NULL;
670 #define MAX_TEA_COUNT 30
671 static char * tea_list[MAX_TEA_COUNT];
672 static int tea_num = 0;
673 static void add_tea(struct opt_item * opt UNUSED, const char * name, void * data) {
674 char ** tea_list = data;
675 if (tea_num >= MAX_TEA_COUNT) {
676 fprintf(stderr, "Cannot boil more than %d teas.\n", MAX_TEA_COUNT);
677 exit(OPT_EXIT_BAD_ARGS);
679 tea_list[tea_num++] = xstrdup(name);
682 static const char * teapot_temperature_parser(char * in, void * ptr) {
683 struct teapot_temperature * temp = ptr;
685 const char * err = str_to_int(&temp->value, in, &next, 10);
688 if (!strcmp("C", next))
689 temp->scale = TEMP_CELSIUS;
690 else if (!strcmp("F", next))
691 temp->scale = TEMP_FAHRENHEIT;
692 else if (!strcmp("K", next))
693 temp->scale = TEMP_KELVIN;
694 else if (!strcmp("R", next))
695 temp->scale = TEMP_RANKINE;
696 else if (!strcmp("Re", next))
697 temp->scale = TEMP_REAUMUR;
699 fprintf(stderr, "Unknown scale: %s\n", next);
700 exit(OPT_EXIT_BAD_ARGS);
705 static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
706 struct teapot_temperature * temp = ptr;
707 bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
710 static struct cf_user_type teapot_temperature_t = {
711 .size = sizeof(struct teapot_temperature),
712 .name = "teapot_temperature_t",
713 .parser = (cf_parser1*) teapot_temperature_parser,
714 .dumper = (cf_dumper1*) teapot_temperature_dumper
717 static void opt_test_hook(struct opt_item * opt, const char * value, void * data) {
721 printf("[HOOK-%s:%c/%s=%s] ", (char *) data, opt->letter, opt->name, value);
723 printf("[HOOK-%s] ", (char *) data);
726 static struct opt_section water_options = {
728 OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "<volume>\tAmount of water (in mls; required)"),
729 OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "\tUse water with gas"),
734 static struct opt_section help = {
736 OPT_HELP("A simple tea boiling console."),
737 OPT_HELP("Usage: teapot [options] name-of-the-tea"),
738 OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
739 OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
740 OPT_HELP("At least one kind of tea must be specified."),
742 OPT_HELP("Options:"),
744 OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "\tShow the version"),
746 OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
747 OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
748 OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "\tStandard teapot"),
749 OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "\tExclusive teapot"),
750 OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "\tTransparent glass teapot"),
751 OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "\tUse user's hands as a teapot (a bit dangerous)"),
752 OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE | OPT_REQUIRED,
753 "<value>\tWanted final temperature of the tea to be served (required)\n"
754 "\t\tSupported scales: Celsius [60C], Fahrenheit [140F],\n"
755 "\t\t Kelvin [350K], Rankine [600R] and Reaumur [50Re]\n"
756 "\t\tOnly integer values allowed."),
757 OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
758 OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"),
759 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."),
760 OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"),
761 OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED | OPT_NO_HELP, ""),
762 OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, OPT_NO_HELP, ""),
764 OPT_HELP("Water options:"),
765 OPT_SECTION(water_options),
766 OPT_HOOK(opt_test_hook, "prearg", OPT_HOOK_BEFORE_ARG),
767 OPT_HOOK(opt_test_hook, "preval", OPT_HOOK_BEFORE_VALUE),
768 OPT_HOOK(opt_test_hook, "postval", OPT_HOOK_AFTER_VALUE),
769 OPT_BOOL('H', "show-hooks", show_hooks, 0, "Demonstrate the hooks."),
780 int main(int argc UNUSED, char ** argv)
782 clist_init(&black_magic);
783 opt_parse(&help, argv+1);
785 printf("English style: %s|", english ? "yes" : "no");
787 printf("Sugar: %d teaspoons|", sugar);
789 printf("Chosen teapot: %s|", teapot_type_str[set]);
790 printf("Temperature: %d%s|", temperature.value, temp_scale_str[temperature.scale]);
791 printf("Verbosity: %d|", verbose);
792 CLIST_FOR_EACH(struct intnode *, n, black_magic)
793 printf("Black magic: %d|", n->x);
794 printf("Prayer: %s|", pray ? "yes" : "no");
795 printf("Water amount: %d|", water_amount);
796 printf("Gas: %s|", with_gas ? "yes" : "no");
797 printf("First tea: %s|", first_tea);
798 for (int i=0; i<tea_num; i++)
799 printf("Boiling a tea: %s|", tea_list[i]);
801 printf("Everything OK. Bye.\n");