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_precomputed *pre;
177 struct opt_item * item;
182 struct opt_precomputed_option ** shortopt;
183 struct opt_item ** hooks_before_arg;
184 struct opt_item ** hooks_before_value;
185 struct opt_item ** hooks_after_value;
187 short hooks_before_arg_count;
188 short hooks_before_value_count;
189 short hooks_after_value_count;
192 static struct opt_precomputed_option * opt_find_item_shortopt(int chr, struct opt_precomputed * pre) {
193 struct opt_precomputed_option * candidate = pre->shortopt[chr];
195 opt_failure("Invalid option -%c", chr);
196 if (candidate->count++ && (candidate->flags & OPT_SINGLE))
197 opt_failure("Option -%c appeared the second time.", candidate->item->letter);
201 static struct opt_precomputed_option * opt_find_item_longopt(char * str, struct opt_precomputed * pre) {
202 uns len = strlen(str);
203 struct opt_precomputed_option * candidate = NULL;
205 for (int i=0; i<pre->opt_count; i++) {
206 if (!pre->opts[i]->name)
208 if (!strncmp(pre->opts[i]->name, str, len)) {
209 if (strlen(pre->opts[i]->name) == len) {
210 if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
211 opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
216 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
218 candidate = pre->opts[i];
220 if (!strncmp("no-", str, 3) && !strncmp(pre->opts[i]->name, str+3, len-3)) {
221 if (strlen(pre->opts[i]->name) == len-3) {
222 if (pre->opts[i]->count++ && (pre->opts[i]->flags & OPT_SINGLE))
223 opt_failure("Option %s appeared the second time.", pre->opts[i]->name);
228 opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
230 candidate = pre->opts[i];
237 opt_failure("Invalid option %s.", str);
240 #define OPT_PTR(type) ({ \
242 if (item->flags & OPT_MULTIPLE) { \
246 } * n = xmalloc(sizeof(*n)); \
247 clist_add_tail(item->ptr, &(n->n)); \
253 #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)))
254 static void opt_parse_value(struct opt_precomputed_option * opt, char * value, int longopt) {
255 struct opt_item * item = opt->item;
256 struct opt_precomputed * pre = opt->pre;
257 for (int i=0;i<pre->hooks_before_value_count;i++)
258 pre->hooks_before_value[i]->u.call(item, value, pre->hooks_before_value[i]->ptr);
262 if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
263 *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
264 else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
265 *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
267 opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
272 switch (item->type) {
277 e = cf_parse_int(value, OPT_PTR(int));
279 opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
285 e = cf_parse_u64(value, OPT_PTR(u64));
287 opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
291 *OPT_PTR(double) = NAN;
293 e = cf_parse_double(value, OPT_PTR(double));
295 opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
299 e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
301 e = cf_parse_ip(value, OPT_PTR(u32));
303 opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
307 *OPT_PTR(const char *) = NULL;
309 *OPT_PTR(const char *) = xstrdup(value);
317 if (*((int *)item->ptr) != -1)
318 opt_failure("Multiple switches: %s", OPT_NAME);
320 *((int *)item->ptr) = item->u.value;
323 if (opt->flags & OPT_NEGATIVE)
324 (*((int *)item->ptr))--;
326 (*((int *)item->ptr))++;
329 item->u.call(item, value, item->ptr);
334 e = item->u.utype->parser(value, OPT_PTR(void*));
336 opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
344 for (int i=0;i<pre->hooks_after_value_count;i++)
345 pre->hooks_after_value[i]->u.call(item, value, pre->hooks_after_value[i]->ptr);
349 static int opt_longopt(char ** argv, int index, struct opt_precomputed * pre) {
351 char * name_in = argv[index] + 2; // skipping the -- on the beginning
352 uns pos = strchrnul(name_in, '=') - name_in;
353 struct opt_precomputed_option * opt = opt_find_item_longopt(strndupa(name_in, pos), pre);
356 if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
358 else if (opt->flags & OPT_REQUIRED_VALUE) {
359 if (pos < strlen(name_in))
360 value = name_in + pos + 1;
362 value = argv[index+1];
364 opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
368 else if (opt->flags & OPT_MAYBE_VALUE) {
369 if (pos < strlen(name_in))
370 value = name_in + pos + 1;
373 if (pos < strlen(name_in))
374 opt_failure("Argument --%s must not have any value.", opt->name);
376 opt_parse_value(opt, value, 1);
380 static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
382 struct opt_precomputed_option * opt;
383 while (argv[index][++chr] && (opt = opt_find_item_shortopt(argv[index][chr], pre))) {
384 if (opt->flags & OPT_NO_VALUE) {
385 opt_parse_value(opt, NULL, 0);
387 else if (opt->flags & OPT_REQUIRED_VALUE) {
388 if (chr == 1 && argv[index][2]) {
389 opt_parse_value(opt, argv[index] + 2, 0);
392 else if (argv[index][chr+1])
393 opt_failure("Option -%c must have a value but found inside a bunch of short opts.", opt->item->letter);
394 else if (!argv[index+1])
395 opt_failure("Option -%c must have a value but nothing supplied.", opt->item->letter);
397 opt_parse_value(opt, argv[index+1], 0);
401 else if (opt->flags & OPT_MAYBE_VALUE) {
402 if (chr == 1 && argv[index][2]) {
403 opt_parse_value(opt, argv[index] + 2, 0);
407 opt_parse_value(opt, NULL, 0);
414 if (argv[index][chr])
415 opt_failure("Unknown option -%c.", argv[index][chr]);
420 static void opt_positional(char * value, struct opt_precomputed * pre) {
421 opt_positional_count++;
422 struct opt_precomputed_option * opt = opt_find_item_shortopt((opt_positional_count > opt_positional_max ? 256 : opt_positional_count + 256), pre);
424 ASSERT(opt_positional_count > opt_positional_max);
425 opt_failure("Too many positional args.");
428 opt_parse_value(opt, value, 2);
431 #define OPT_TRAVERSE_SECTIONS \
432 while (item->cls == OPT_CL_SECTION) { \
436 struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
437 new_stk->prev = stk; \
438 stk->next = new_stk; \
442 item = item->u.section->opt; \
444 if (item->cls == OPT_CL_END) { \
445 if (!stk->prev) break; \
451 void opt_parse(const struct opt_section * options, char ** argv) {
452 opt_section_root = options;
455 struct opt_item * this;
456 struct opt_stack * prev;
457 struct opt_stack * next;
458 } * stk = alloca(sizeof(*stk));
463 struct opt_precomputed * pre = alloca(sizeof(*pre));
464 memset(pre, 0, sizeof (*pre));
469 for (struct opt_item * item = options->opt; ; item++) {
470 OPT_TRAVERSE_SECTIONS;
471 if (item->letter || item->name)
473 if (item->cls == OPT_CL_BOOL)
475 if (item->letter > 256)
476 opt_positional_max++;
477 if (item->cls == OPT_CL_HOOK)
481 pre->opts = alloca(sizeof(*pre->opts) * count);
482 pre->shortopt = alloca(sizeof(*pre->shortopt) * (opt_positional_max + 257));
483 memset(pre->shortopt, 0, sizeof(*pre->shortopt) * (opt_positional_max + 257));
484 pre->hooks_before_arg = alloca(sizeof (*pre->hooks_before_arg) * hooks);
485 pre->hooks_before_value = alloca(sizeof (*pre->hooks_before_value) * hooks);
486 pre->hooks_after_value = alloca(sizeof (*pre->hooks_after_value) * hooks);
488 pre->hooks_before_arg_count = 0;
489 pre->hooks_before_value_count = 0;
490 pre->hooks_after_value_count = 0;
494 for (struct opt_item * item = options->opt; ; item++) {
495 OPT_TRAVERSE_SECTIONS;
496 if (item->letter || item->name) {
497 struct opt_precomputed_option * opt = xmalloc(sizeof(*opt));
500 opt->flags = item->flags;
502 opt->name = item->name;
503 pre->opts[pre->opt_count++] = opt;
505 pre->shortopt[(int) item->letter] = opt;
506 OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
508 if (item->cls == OPT_CL_HOOK) {
509 if (item->flags & OPT_HOOK_BEFORE_ARG)
510 pre->hooks_before_arg[pre->hooks_before_arg_count++] = item;
511 else if (item->flags & OPT_HOOK_BEFORE_VALUE)
512 pre->hooks_before_value[pre->hooks_before_value_count++] = item;
513 else if (item->flags & OPT_HOOK_AFTER_VALUE)
514 pre->hooks_after_value[pre->hooks_after_value_count++] = item;
520 int force_positional = 0;
521 for (int i=0;argv[i];i++) {
522 for (int j=0;j<pre->hooks_before_arg_count;j++)
523 pre->hooks_before_arg[j]->u.call(NULL, NULL, pre->hooks_before_arg[j]->ptr);
524 if (argv[i][0] != '-' || force_positional) {
525 opt_positional(argv[i], pre);
528 if (argv[i][1] == '-') {
529 if (argv[i][2] == '\0')
532 i += opt_longopt(argv, i, pre);
535 i += opt_shortopt(argv, i, pre);
537 opt_positional(argv[i], pre);
541 for (int i=0;i<opt_positional_max+257;i++) {
542 if (!pre->shortopt[i])
544 if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags & OPT_REQUIRED))
546 opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
548 opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
551 for (int i=0;i<pre->opt_count;i++) {
554 if (!pre->opts[i]->count && (pre->opts[i]->flags & OPT_REQUIRED))
555 opt_failure("Required option --%s not found.\n", pre->opts[i]->item->name);
559 static void opt_conf_end_of_options(struct cf_context *cc) {
561 if (cc->postpone_commit && cf_close_group())
562 opt_failure("Loading of configuration failed");
565 void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) {
566 struct cf_context *cc = cf_get_context();
567 switch (opt->letter) {
571 opt_failure("Cannot set %s", value);
575 opt_failure("Cannot load config file %s", value);
577 #ifdef CONFIG_UCW_DEBUG
579 opt_conf_end_of_options(cc);
580 struct fastbuf *b = bfdopen(1, 4096);
588 opt_conf_parsed_count++;
591 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
594 OPT_CONF_HOOK_CONFIG,
596 } state = OPT_CONF_HOOK_BEGIN;
600 if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
604 case OPT_CONF_HOOK_BEGIN:
606 state = OPT_CONF_HOOK_CONFIG;
608 opt_conf_end_of_options(cf_get_context());
609 state = OPT_CONF_HOOK_OTHERS;
612 case OPT_CONF_HOOK_CONFIG:
614 opt_conf_end_of_options(cf_get_context());
615 state = OPT_CONF_HOOK_OTHERS;
618 case OPT_CONF_HOOK_OTHERS:
620 opt_failure("Config options (-C, -S) must stand before other options.");
628 #include <ucw/fastbuf.h>
630 static void show_version(struct opt_item * opt UNUSED, const char * value UNUSED, void * data UNUSED) {
631 printf("This is a simple tea boiling console v0.1.\n");
635 struct teapot_temperature {
646 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
648 static enum TEAPOT_TYPE {
653 TEAPOT_UNDEFINED = -1
654 } set = TEAPOT_UNDEFINED;
656 static char * teapot_type_str[] = { "standard", "exclusive", "glass", "hands" };
658 static int show_hooks = 0;
659 static int english = 0;
660 static int sugar = 0;
661 static int verbose = 1;
662 static int with_gas = 0;
663 static clist black_magic;
665 static int water_amount = 0;
666 static char * first_tea = NULL;
668 #define MAX_TEA_COUNT 30
669 static char * tea_list[MAX_TEA_COUNT];
670 static int tea_num = 0;
671 static void add_tea(struct opt_item * opt UNUSED, const char * name, void * data) {
672 char ** tea_list = data;
673 if (tea_num >= MAX_TEA_COUNT) {
674 fprintf(stderr, "Cannot boil more than %d teas.\n", MAX_TEA_COUNT);
675 exit(OPT_EXIT_BAD_ARGS);
677 tea_list[tea_num++] = xstrdup(name);
680 static const char * teapot_temperature_parser(char * in, void * ptr) {
681 struct teapot_temperature * temp = ptr;
683 const char * err = str_to_int(&temp->value, in, &next, 10);
686 if (!strcmp("C", next))
687 temp->scale = TEMP_CELSIUS;
688 else if (!strcmp("F", next))
689 temp->scale = TEMP_FAHRENHEIT;
690 else if (!strcmp("K", next))
691 temp->scale = TEMP_KELVIN;
692 else if (!strcmp("R", next))
693 temp->scale = TEMP_RANKINE;
694 else if (!strcmp("Re", next))
695 temp->scale = TEMP_REAUMUR;
697 fprintf(stderr, "Unknown scale: %s\n", next);
698 exit(OPT_EXIT_BAD_ARGS);
703 static void teapot_temperature_dumper(struct fastbuf * fb, void * ptr) {
704 struct teapot_temperature * temp = ptr;
705 bprintf(fb, "%d%s", temp->value, temp_scale_str[temp->scale]);
708 static struct cf_user_type teapot_temperature_t = {
709 .size = sizeof(struct teapot_temperature),
710 .name = "teapot_temperature_t",
711 .parser = (cf_parser1*) teapot_temperature_parser,
712 .dumper = (cf_dumper1*) teapot_temperature_dumper
715 static void opt_test_hook(struct opt_item * opt, const char * value, void * data) {
719 printf("[HOOK-%s:%c/%s=%s] ", (char *) data, opt->letter, opt->name, value);
721 printf("[HOOK-%s] ", (char *) data);
724 static struct opt_section water_options = {
726 OPT_INT('w', "water", water_amount, OPT_REQUIRED | OPT_REQUIRED_VALUE, "<volume>\tAmount of water (in mls; required)"),
727 OPT_BOOL('G', "with-gas", with_gas, OPT_NO_VALUE, "\tUse water with gas"),
732 static struct opt_section help = {
734 OPT_HELP("A simple tea boiling console."),
735 OPT_HELP("Usage: teapot [options] name-of-the-tea"),
736 OPT_HELP("Black, green or white tea supported as well as fruit or herbal tea."),
737 OPT_HELP("You may specify more kinds of tea, all of them will be boiled for you, in the given order."),
738 OPT_HELP("At least one kind of tea must be specified."),
740 OPT_HELP("Options:"),
742 OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "\tShow the version"),
744 OPT_BOOL('e', "english-style", english, 0, "\tEnglish style (with milk)"),
745 OPT_INT('s', "sugar", sugar, OPT_REQUIRED_VALUE, "<spoons>\tAmount of sugar (in teaspoons)"),
746 OPT_SWITCH(0, "standard-set", set, TEAPOT_STANDARD, 0, "\tStandard teapot"),
747 OPT_SWITCH('x', "exclusive-set", set, TEAPOT_EXCLUSIVE, 0, "\tExclusive teapot"),
748 OPT_SWITCH('g', "glass-set", set, TEAPOT_GLASS, 0, "\tTransparent glass teapot"),
749 OPT_SWITCH('h', "hands", set, TEAPOT_HANDS, 0, "\tUse user's hands as a teapot (a bit dangerous)"),
750 OPT_USER('t', "temperature", temperature, teapot_temperature_t, OPT_REQUIRED_VALUE | OPT_REQUIRED,
751 "<value>\tWanted final temperature of the tea to be served (required)\n"
752 "\t\tSupported scales: Celsius [60C], Fahrenheit [140F],\n"
753 "\t\t Kelvin [350K], Rankine [600R] and Reaumur [50Re]\n"
754 "\t\tOnly integer values allowed."),
755 OPT_INC('v', "verbose", verbose, 0, "\tVerbose (the more -v, the more verbose)"),
756 OPT_INC('q', "quiet", verbose, OPT_NEGATIVE, "\tQuiet (the more -q, the more quiet)"),
757 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."),
758 OPT_BOOL('p', "pray", pray, OPT_SINGLE, "\tPray before boiling"),
759 OPT_STRING(OPT_POSITIONAL(1), NULL, first_tea, OPT_REQUIRED | OPT_NO_HELP, ""),
760 OPT_CALL(OPT_POSITIONAL_TAIL, NULL, add_tea, &tea_list, OPT_NO_HELP, ""),
762 OPT_HELP("Water options:"),
763 OPT_SECTION(water_options),
764 OPT_HOOK(opt_test_hook, "prearg", OPT_HOOK_BEFORE_ARG),
765 OPT_HOOK(opt_test_hook, "preval", OPT_HOOK_BEFORE_VALUE),
766 OPT_HOOK(opt_test_hook, "postval", OPT_HOOK_AFTER_VALUE),
767 OPT_BOOL('H', "show-hooks", show_hooks, 0, "Demonstrate the hooks."),
778 int main(int argc UNUSED, char ** argv)
780 clist_init(&black_magic);
781 opt_parse(&help, argv+1);
783 printf("English style: %s|", english ? "yes" : "no");
785 printf("Sugar: %d teaspoons|", sugar);
787 printf("Chosen teapot: %s|", teapot_type_str[set]);
788 printf("Temperature: %d%s|", temperature.value, temp_scale_str[temperature.scale]);
789 printf("Verbosity: %d|", verbose);
790 CLIST_FOR_EACH(struct intnode *, n, black_magic)
791 printf("Black magic: %d|", n->x);
792 printf("Prayer: %s|", pray ? "yes" : "no");
793 printf("Water amount: %d|", water_amount);
794 printf("Gas: %s|", with_gas ? "yes" : "no");
795 printf("First tea: %s|", first_tea);
796 for (int i=0; i<tea_num; i++)
797 printf("Boiling a tea: %s|", tea_list[i]);
799 printf("Everything OK. Bye.\n");