]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: Config getopt and hooks
[libucw.git] / ucw / opt.c
1 /*
2  *      UCW Library -- Parsing of command line options
3  *
4  *      (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU Lesser General Public License.
8  */
9
10 #include <ucw/lib.h>
11 #include <ucw/opt.h>
12 #include <ucw/conf.h>
13 #include <ucw/conf-internal.h>
14 #include <ucw/fastbuf.h>
15 #include <ucw/stkstring.h>
16 #include <ucw/strtonum.h>
17
18 #include <alloca.h>
19 #include <math.h>
20
21 int opt_parsed_count = 0;
22 int opt_conf_parsed_count = 0;
23
24 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
25 static void opt_failure(const char * mesg, ...) {
26   va_list args;
27   va_start(args, mesg);
28   vfprintf(stderr, mesg, args);
29   fprintf(stderr, "\n");
30   opt_usage();
31   exit(OPT_EXIT_BAD_ARGS);
32   va_end(args);
33 }
34
35 #define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \
36   do { \
37     if (item->letter >= 256) { \
38       if (flags & OPT_VALUE_FLAGS) \
39         flags &= ~OPT_VALUE_FLAGS; \
40       flags |= OPT_REQUIRED_VALUE; \
41     } \
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); \
45       ASSERT(0); \
46     } \
47     else if (!(flags & OPT_VALUE_FLAGS)) \
48       flags |= opt_default_value_flags[item->cls]; \
49   } while (0)
50 #define OPT_ITEM_FLAGS(item) ((item->flags & OPT_VALUE_FLAGS) ? item->flags : item->flags | opt_default_value_flags[item->cls])
51
52 const struct opt_section * opt_section_root;
53
54 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
55
56 void opt_help_internal(const struct opt_section * help) {
57   int sections_cnt = 0;
58   int lines_cnt = 0;
59
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) {
63       sections_cnt++;
64       continue;
65     }
66     if (!*(item->help)) {
67       lines_cnt++;
68       continue;
69     }
70     FOREACHLINE(item->help)
71       lines_cnt++;
72   }
73
74   struct opt_sectlist {
75     int pos;
76     struct opt_section * sect;
77   } sections[sections_cnt];
78   int s = 0;
79
80   const char *lines[lines_cnt][3];
81   memset(lines, 0, sizeof(lines));
82   int line = 0;
83
84   int linelengths[3] = { -1, -1, -1 };
85
86   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
87     if (item->flags & OPT_NO_HELP) continue;
88
89     if (item->cls == OPT_CL_HELP) {
90       if (!*(item->help)) {
91         line++;
92         continue;
93       }
94 #define SPLITLINES(text) do { \
95       FOREACHLINE(text) { \
96         int cell = 0; \
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; \
99           if (cell >= 2) \
100             break; \
101           else \
102             if (*e == '\t' && linelengths[cell] < (e - b)) \
103               linelengths[cell] = e-b; \
104           cell++; \
105         } \
106         line++; \
107       } } while (0)
108       SPLITLINES(item->help);
109       continue;
110     }
111
112     if (item->cls == OPT_CL_SECTION) {
113       sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
114       continue;
115     }
116
117     uns valoff = strchrnul(item->help, '\t') - item->help;
118     uns eol = strchrnul(item->help, '\n') - item->help;
119     if (valoff > eol)
120       valoff = eol;
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)))
122     if (item->name) {
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]);
126       lines[line][0] = "";
127       if (linelengths[0] < 0)
128         linelengths[0] = 0;
129     }
130     if (item->letter) {
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]);
134     }
135 #undef VAL
136
137     if (eol > valoff) {
138       lines[line][2] = item->help + valoff + 1;
139     }
140
141     line++;
142
143     if (*(item->help + eol))
144       SPLITLINES(item->help + eol + 1);
145   }
146 #undef SPLITLINES
147
148   s = 0;
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);
154       s++;
155     }
156     if (lines[i][0] == NULL)
157       printf("\n");
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));
162     else
163       printf("%-*.*s  %-*.*s  %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
164   }
165   while (s < sections_cnt && sections[s].pos == line) {
166     opt_help_internal(sections[s].sect);
167     s++;
168   }
169 }
170
171 static int opt_positional_max = 0;
172 static int opt_positional_count = 0;
173
174 struct opt_precomputed {
175   struct opt_precomputed_option {
176     struct opt_item * item;
177     const char * name;
178     short flags;
179     short count;
180   } ** opts;
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;
185   short opt_count;
186   short hooks_before_arg_count;
187   short hooks_before_value_count;
188   short hooks_after_value_count;
189 };
190
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];
193   if (!candidate)
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);
197   return candidate;
198 }
199
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;
203
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);
209
210         return pre->opts[i];
211       }
212       if (candidate)
213         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
214       else
215         candidate = pre->opts[i];
216     }
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);
221
222         return pre->opts[i];
223       }
224       if (candidate)
225         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, pre->opts[i]->name);
226       else
227         candidate = pre->opts[i];
228     }
229   }
230
231   if (candidate)
232     return candidate;
233
234   opt_failure("Invalid option %s.", str);
235 }
236
237 #define OPT_PTR(type) ({ \
238   type * ptr; \
239   if (item->flags & OPT_MULTIPLE) { \
240     struct { \
241       cnode n; \
242       type v; \
243     } * n = xmalloc(sizeof(*n)); \
244     clist_add_tail(item->ptr, &(n->n)); \
245     ptr = &(n->v); \
246   } else \
247     ptr = item->ptr; \
248   ptr; })
249
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;
253
254   switch (item->cls) {
255     case OPT_CL_BOOL:
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));
260       else
261         opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
262       break;
263     case OPT_CL_STATIC:
264       {
265         char * e = NULL;
266         switch (item->type) {
267           case CT_INT:
268             if (!value)
269               *OPT_PTR(int) = 0;
270             else
271               e = cf_parse_int(value, OPT_PTR(int));
272             if (e)
273               opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
274             break;
275           case CT_U64:
276             if (!value)
277               *OPT_PTR(u64) = 0;
278             else
279               e = cf_parse_u64(value, OPT_PTR(u64));
280             if (e)
281               opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
282             break;
283           case CT_DOUBLE:
284             if (!value)
285               *OPT_PTR(double) = NAN;
286             else
287               e = cf_parse_double(value, OPT_PTR(double));
288             if (e)
289               opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
290             break;
291           case CT_IP:
292             if (!value)
293               e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
294             else
295               e = cf_parse_ip(value, OPT_PTR(u32));
296             if (e)
297               opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
298             break;
299           case CT_STRING:
300             if (!value)
301               *OPT_PTR(const char *) = NULL;
302             else
303               *OPT_PTR(const char *) = xstrdup(value);
304             break;
305           default:
306             ASSERT(0);
307         }
308         break;
309       }
310     case OPT_CL_SWITCH:
311       if (*((int *)item->ptr) != -1)
312         opt_failure("Multiple switches: %s", OPT_NAME);
313       else
314         *((int *)item->ptr) = item->u.value;
315       break;
316     case OPT_CL_INC:
317       if (opt->flags & OPT_NEGATIVE)
318         (*((int *)item->ptr))--;
319       else
320         (*((int *)item->ptr))++;
321       break;
322     case OPT_CL_CALL:
323       item->u.call(item, value, item->ptr);
324       break;
325     case OPT_CL_USER:
326       {
327         char * e = NULL;
328         e = item->u.utype->parser(value, OPT_PTR(void*));
329         if (e)
330           opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
331         break;
332       }
333     default:
334       ASSERT(0);
335   }
336   opt_parsed_count++;
337 }
338 #undef OPT_NAME
339
340 static int opt_longopt(char ** argv, int index, struct opt_precomputed * pre) {
341   int eaten = 0;
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);
345   char * value = NULL;
346
347   if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
348     value = "n";
349   else if (opt->flags & OPT_REQUIRED_VALUE) {
350     if (pos < strlen(name_in))
351       value = name_in + pos + 1;
352     else {
353       value = argv[index+1];
354       if (!value)
355         opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
356       eaten++;
357     }
358   }
359   else if (opt->flags & OPT_MAYBE_VALUE) {
360     if (pos < strlen(name_in))
361       value = name_in + pos + 1;
362   }
363   else {
364     if (pos < strlen(name_in))
365       opt_failure("Argument --%s must not have any value.", opt->name);
366   }
367   opt_parse_value(opt, value, 1);
368   return eaten;
369 }
370
371 static int opt_shortopt(char ** argv, int index, struct opt_precomputed * pre) {
372   int chr = 0;
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);
377     }
378     else if (opt->flags & OPT_REQUIRED_VALUE) {
379       if (chr == 1 && argv[index][2]) {
380         opt_parse_value(opt, argv[index] + 2, 0);
381         return 0;
382       }
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);
387       else {
388         opt_parse_value(opt, argv[index+1], 0);
389         return 1;
390       }
391     }
392     else if (opt->flags & OPT_MAYBE_VALUE) {
393       if (chr == 1 && argv[index][2]) {
394         opt_parse_value(opt, argv[index] + 2, 0);
395         return 0;
396       }
397       else
398         opt_parse_value(opt, NULL, 0);
399     }
400     else {
401       ASSERT(0);
402     }
403   }
404
405   if (argv[index][chr])
406     opt_failure("Unknown option -%c.", argv[index][chr]);
407   
408   return 0;
409 }
410
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);
414   if (!opt) {
415     ASSERT(opt_positional_count > opt_positional_max);
416     opt_failure("Too many positional args.");
417   }
418
419   opt_parse_value(opt, value, 2);
420 }
421
422 #define OPT_TRAVERSE_SECTIONS \
423     while (item->cls == OPT_CL_SECTION) { \
424       if (stk->next) \
425         stk = stk->next; \
426       else { \
427         struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
428         new_stk->prev = stk; \
429         stk->next = new_stk; \
430         stk = new_stk; \
431       } \
432       stk->this = item; \
433       item = item->u.section->opt; \
434     } \
435     if (item->cls == OPT_CL_END) { \
436       if (!stk->prev) break; \
437       item = stk->this; \
438       stk = stk->prev; \
439       continue; \
440     }
441
442 void opt_parse(const struct opt_section * options, char ** argv) {
443   opt_section_root = options;
444
445   struct opt_stack {
446     struct opt_item * this;
447     struct opt_stack * prev;
448     struct opt_stack * next;
449   } * stk = alloca(sizeof(*stk));
450   stk->this = NULL;
451   stk->prev = NULL;
452   stk->next = NULL;
453
454   struct opt_precomputed * pre = alloca(sizeof(*pre));
455   memset(pre, 0, sizeof (*pre));
456
457   int count = 0;
458   int hooks = 0;
459
460   for (struct opt_item * item = options->opt; ; item++) {
461     OPT_TRAVERSE_SECTIONS;
462     if (item->letter || item->name)
463       count++;
464     if (item->cls == OPT_CL_BOOL)
465       count++;
466     if (item->letter > 256)
467       opt_positional_max++;
468     if (item->cls == OPT_CL_HOOK)
469       hooks++;
470   }
471   
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);
478   
479   pre->hooks_before_arg_count = 0;
480   pre->hooks_before_value_count = 0;
481   pre->hooks_after_value_count = 0;
482   
483   pre->opt_count = 0;
484
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));
489       opt->item = item;
490       opt->flags = item->flags;
491       opt->count = 0;
492       opt->name = item->name;
493       pre->opts[pre->opt_count++] = opt;
494       if (item->letter)
495         pre->shortopt[(int) item->letter] = opt;
496       OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
497     }
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;
505       else
506         ASSERT(0);
507     }
508   }
509
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);
514     }
515     else {
516       if (argv[i][1] == '-') {
517         if (argv[i][2] == '\0')
518           force_positional++;
519         else
520           i += opt_longopt(argv, i, pre);
521       }
522       else if (argv[i][1])
523         i += opt_shortopt(argv, i, pre);
524       else
525         opt_positional(argv[i], pre);
526     }
527   }
528
529   for (int i=0;i<opt_positional_max+257;i++) {
530     if (!pre->shortopt[i])
531       continue;
532     if (!pre->shortopt[i]->count && (pre->shortopt[i]->flags & OPT_REQUIRED))
533       if (i < 256)
534         opt_failure("Required option -%c not found.\n", pre->shortopt[i]->item->letter);
535       else
536         opt_failure("Required positional argument #%d not found.\n", (i > 256) ? pre->shortopt[i]->item->letter-256 : opt_positional_max+1);
537   }
538
539   for (int i=0;i<pre->opt_count;i++) {
540     if (!pre->opts[i])
541       continue;
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);
544   }
545 }
546
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.");
550
551   struct cf_context *cc = cf_get_context();
552   switch(opt->letter) {
553     case 'S':
554       cf_load_default(cc);
555       if (cf_set(value))
556         opt_failure("Cannot set %s", value);
557       break;
558     case 'C':
559       if (cf_load(value))
560         opt_failure("Cannot load config file %s", value);
561       break;
562 #ifdef CONFIG_UCW_DEBUG
563     case '0':
564       cf_load_default(cc);
565       if (cc->postpone_commit && cf_close_group())
566         opt_failure("Loading of configuration failed");
567       struct fastbuf *b = bfdopen(1, 4096);
568       cf_dump_sections(b);
569       bclose(b);
570       exit(0);
571       break;
572 #endif
573   }
574
575   opt_conf_parsed_count++;
576 }
577
578 #ifdef TEST
579 #include <ucw/fastbuf.h>
580
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");
583   exit(EXIT_SUCCESS);
584 }
585
586 struct teapot_temperature {
587   enum {
588     TEMP_CELSIUS = 0,
589     TEMP_FAHRENHEIT,
590     TEMP_KELVIN,
591     TEMP_REAUMUR,
592     TEMP_RANKINE
593   } scale;
594   int value;
595 } temperature;
596
597 static char * temp_scale_str[] = { "C", "F", "K", "Re", "R" };
598
599 static enum TEAPOT_TYPE {
600   TEAPOT_STANDARD = 0,
601   TEAPOT_EXCLUSIVE,
602   TEAPOT_GLASS,
603   TEAPOT_HANDS,
604   TEAPOT_UNDEFINED = -1
605 } set = TEAPOT_UNDEFINED;
606
607 static char * teapot_type_str[] = { "standard", "exclusive", "glass", "hands" };
608
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;
614 static int pray = 0;
615 static int water_amount = 0;
616 static char * first_tea = NULL;
617
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);
626   }
627   tea_list[tea_num++] = xstrdup(name);
628 }
629
630 static const char * teapot_temperature_parser(char * in, void * ptr) {
631   struct teapot_temperature * temp = ptr;
632   const char * next;
633   const char * err = str_to_int(&temp->value, in, &next, 10);
634   if (err)
635     return err;
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;
646   else {
647     fprintf(stderr, "Unknown scale: %s\n", next);
648     exit(OPT_EXIT_BAD_ARGS);
649   }
650   return NULL;
651 }
652
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]);
656 }
657
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
663 };
664
665 static struct opt_section water_options = {
666   OPT_ITEMS {
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"),
669     OPT_END
670   }
671 };
672
673 static struct opt_section help = {
674   OPT_ITEMS {
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."),
680     OPT_HELP(""),
681     OPT_HELP("Options:"),
682     OPT_HELP_OPTION,
683     OPT_CALL('V', "version", show_version, NULL, OPT_NO_VALUE, "\tShow the version"),
684     OPT_HELP(""),
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, ""),
702     OPT_HELP(""),
703     OPT_HELP("Water options:"),
704     OPT_SECTION(water_options),
705     OPT_END
706   }
707 };
708
709 struct intnode {
710   cnode n;
711   int x;
712 };
713
714 int main(int argc UNUSED, char ** argv)
715 {
716   clist_init(&black_magic);
717   opt_parse(&help, argv+1);
718
719   printf("English style: %s|", english ? "yes" : "no");
720   if (sugar)
721     printf("Sugar: %d teaspoons|", sugar);
722   if (set != -1)
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]);
734
735   printf("Everything OK. Bye.\n");
736 }
737
738 #endif