]> mj.ucw.cz Git - libucw.git/blob - ucw/opt.c
Opt: Merged fixes from Gigamail.
[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 /***
22  * Value flags defaults
23  * ~~~~~~~~~~~~~~~~~~~~
24  *
25  * OPT_NO_VALUE for OPT_BOOL, OPT_SWITCH and OPT_INC
26  * OPT_MAYBE_VALUE for OPT_STRING, OPT_UNS, OPT_INT
27  * Some of the value flags (OPT_NO_VALUE, OPT_MAYBE_VALUE, OPT_REQUIRED_VALUE)
28  * must be specified for OPT_CALL and OPT_USER.
29  ***/
30 static uns opt_default_value_flags[] = {
31     [OPT_CL_BOOL] = OPT_NO_VALUE,
32     [OPT_CL_STATIC] = OPT_MAYBE_VALUE,
33     [OPT_CL_SWITCH] = OPT_NO_VALUE,
34     [OPT_CL_INC] = OPT_NO_VALUE,
35     [OPT_CL_CALL] = 0,
36     [OPT_CL_USER] = 0,
37     [OPT_CL_SECTION] = 0,
38     [OPT_CL_HELP] = 0
39 };
40
41 struct opt_precomputed {
42   struct opt_item * item;
43   const char * name;
44   short flags;
45   short count;
46 };
47
48 struct opt_context {
49   struct opt_precomputed ** opts;
50   struct opt_precomputed ** shortopt;
51   struct opt_item ** hooks_before_arg;
52   struct opt_item ** hooks_before_value;
53   struct opt_item ** hooks_after_value;
54   short opt_count;
55   short hooks_before_arg_count;
56   short hooks_before_value_count;
57   short hooks_after_value_count;
58   int positional_max;
59   int positional_count;
60 };
61
62 static void opt_failure(const char * mesg, ...) FORMAT_CHECK(printf,1,2) NONRET;
63 static void opt_failure(const char * mesg, ...) {
64   va_list args;
65   va_start(args, mesg);
66   vfprintf(stderr, mesg, args);
67   fprintf(stderr, "\n");
68   opt_usage();
69   exit(OPT_EXIT_BAD_ARGS);
70   va_end(args);         // FIXME: Does this make a sense after exit()?
71 }
72
73 // FIXME: This could be an inline function, couldn't it?
74 #define OPT_ADD_DEFAULT_ITEM_FLAGS(item, flags) \
75   do { \
76     if (item->letter >= 256) { \
77       if (flags & OPT_VALUE_FLAGS) /* FIXME: Redundant condition */ \
78         flags &= ~OPT_VALUE_FLAGS; \
79       flags |= OPT_REQUIRED_VALUE; \
80     } \
81     if (!(flags & OPT_VALUE_FLAGS) && \
82         (item->cls == OPT_CL_CALL || item->cls == OPT_CL_USER)) { \
83       fprintf(stderr, "You MUST specify some of the value flags for the %c/%s item.\n", item->letter, item->name); \
84       ASSERT(0); \
85     } \
86     else if (!(flags & OPT_VALUE_FLAGS)) /* FIXME: Streamline the conditions */ \
87       flags |= opt_default_value_flags[item->cls]; \
88   } while (0)
89
90 // FIXME: Is this still useful? Isn't it better to use OPT_ADD_DEFAULT_ITEM_FLAGS during init?
91 #define OPT_ITEM_FLAGS(item) ((item->flags & OPT_VALUE_FLAGS) ? item->flags : item->flags | opt_default_value_flags[item->cls])
92
93 #define FOREACHLINE(text) for (const char * begin = (text), * end = (text); (*end) && (end = strchrnul(begin, '\n')); begin = end+1)
94
95 static inline uns uns_min(uns x, uns y)
96 {
97   return MIN(x, y);
98 }
99
100 void opt_help(const struct opt_section * help) {
101   int sections_cnt = 0;
102   int lines_cnt = 0;
103
104   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
105     if (item->flags & OPT_NO_HELP) continue;
106     if (item->cls == OPT_CL_SECTION) {
107       sections_cnt++;
108       continue;
109     }
110     if (!*(item->help)) {
111       lines_cnt++;
112       continue;
113     }
114     FOREACHLINE(item->help)
115       lines_cnt++;
116   }
117
118   struct opt_sectlist {
119     int pos;
120     struct opt_section * sect;
121   } sections[sections_cnt];
122   int s = 0;
123
124   const char *lines[lines_cnt][3];
125   memset(lines, 0, sizeof(lines));
126   int line = 0;
127
128   int linelengths[3] = { -1, -1, -1 };
129
130   for (struct opt_item * item = help->opt; item->cls != OPT_CL_END; item++) {
131     if (item->flags & OPT_NO_HELP) continue;
132
133     if (item->cls == OPT_CL_HELP) {
134       if (!*(item->help)) {
135         line++;
136         continue;
137       }
138 #define SPLITLINES(text) do { \
139       FOREACHLINE(text) { \
140         int cell = 0; \
141         for (const char * b = begin, * e = begin; (e < end) && (e = strchrnul(b, '\t')) && (e > end ? (e = end) : end); b = e+1) { \
142           lines[line][cell] = b; \
143           if (cell >= 2) \
144             break; \
145           else \
146             if (*e == '\t' && linelengths[cell] < (e - b)) \
147               linelengths[cell] = e-b; \
148           cell++; \
149         } \
150         line++; \
151       } } while (0)
152       SPLITLINES(item->help);
153       continue;
154     }
155
156     if (item->cls == OPT_CL_SECTION) {
157       sections[s++] = (struct opt_sectlist) { .pos = line, .sect = item->u.section };
158       continue;
159     }
160
161     uns valoff = strchrnul(item->help, '\t') - item->help;
162     uns eol = strchrnul(item->help, '\n') - item->help;
163     if (valoff > eol)
164       valoff = eol;
165 #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)))
166     if (item->name) {
167       lines[line][1] = stk_printf("--%s%s", item->name, VAL(item));
168       if (linelengths[1] < (int) strlen(lines[line][1]))
169         linelengths[1] = strlen(lines[line][1]);
170       lines[line][0] = "";
171       if (linelengths[0] < 0)
172         linelengths[0] = 0;
173     }
174     if (item->letter) {
175       lines[line][0] = stk_printf("-%c,", item->letter);
176       if (linelengths[0] < (int) strlen(lines[line][0]))
177         linelengths[0] = strlen(lines[line][0]);
178     }
179 #undef VAL
180
181     if (eol > valoff) {
182       lines[line][2] = item->help + valoff + 1;
183     }
184
185     line++;
186
187     if (*(item->help + eol))
188       SPLITLINES(item->help + eol + 1);
189   }
190 #undef SPLITLINES
191
192   s = 0;
193 #define FIELD(k) linelengths[k], uns_min(strchrnul(lines[i][k], '\t') - lines[i][k], strchrnul(lines[i][k], '\n') - lines[i][k]), lines[i][k]
194 #define LASTFIELD(k) uns_min(strchrnul(lines[i][k], '\t') - lines[i][k], strchrnul(lines[i][k], '\n') - lines[i][k]), lines[i][k]
195   for (int i=0;i<line;i++) {
196     while (s < sections_cnt && sections[s].pos == i) {
197       opt_help(sections[s].sect);
198       s++;
199     }
200     if (lines[i][0] == NULL)
201       printf("\n");
202     else if (linelengths[0] == -1 || lines[i][1] == NULL)
203       printf("%.*s\n", LASTFIELD(0));
204     else if (linelengths[1] == -1 || lines[i][2] == NULL)
205       printf("%-*.*s  %.*s\n", FIELD(0), LASTFIELD(1));
206     else
207       printf("%-*.*s  %-*.*s  %.*s\n", FIELD(0), FIELD(1), LASTFIELD(2));
208   }
209   while (s < sections_cnt && sections[s].pos == line) {
210     opt_help(sections[s].sect);
211     s++;
212   }
213 }
214
215 static struct opt_precomputed * opt_find_item_shortopt(struct opt_context * oc, int chr) {
216   struct opt_precomputed * candidate = oc->shortopt[chr];
217   if (!candidate)
218     opt_failure("Invalid option -%c", chr);
219   if (candidate->count++ && (candidate->flags & OPT_SINGLE))
220     opt_failure("Option -%c appeared the second time.", candidate->item->letter);
221   return candidate;
222 }
223
224 static struct opt_precomputed * opt_find_item_longopt(struct opt_context * oc, char * str) {
225   uns len = strlen(str);
226   struct opt_precomputed * candidate = NULL;
227
228   for (int i=0; i<oc->opt_count; i++) {
229     if (!oc->opts[i]->name)
230       continue;
231     if (!strncmp(oc->opts[i]->name, str, len)) {
232       if (strlen(oc->opts[i]->name) == len) {
233         if (oc->opts[i]->count++ && (oc->opts[i]->flags & OPT_SINGLE))
234           opt_failure("Option %s appeared the second time.", oc->opts[i]->name);
235
236         return oc->opts[i];
237       }
238       if (candidate)
239         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, oc->opts[i]->name);
240       else
241         candidate = oc->opts[i];
242     }
243     if (!strncmp("no-", str, 3) && !strncmp(oc->opts[i]->name, str+3, len-3)) {
244       if (strlen(oc->opts[i]->name) == len-3) {
245         if (oc->opts[i]->count++ && (oc->opts[i]->flags & OPT_SINGLE))
246           opt_failure("Option %s appeared the second time.", oc->opts[i]->name);
247
248         return oc->opts[i];
249       }
250       if (candidate)
251         opt_failure("Ambiguous prefix %s: Found matching %s and %s.", str, candidate->name, oc->opts[i]->name);
252       else
253         candidate = oc->opts[i];
254     }
255   }
256
257   if (candidate)
258     return candidate;
259
260   opt_failure("Invalid option %s.", str);
261 }
262
263 #define OPT_PTR(type) ({ \
264   type * ptr; \
265   if (item->flags & OPT_MULTIPLE) { \
266     struct { \
267       cnode n; \
268       type v; \
269     } * n = xmalloc(sizeof(*n)); \
270     clist_add_tail(item->ptr, &(n->n)); \
271     ptr = &(n->v); \
272   } else \
273     ptr = item->ptr; \
274   ptr; })
275
276 #define OPT_NAME (longopt == 2 ? stk_printf("positional arg #%d", oc->positional_count) : (longopt == 1 ? stk_printf("--%s", opt->name) : stk_printf("-%c", item->letter)))
277 static void opt_parse_value(struct opt_context * oc, struct opt_precomputed * opt, char * value, int longopt) {
278   struct opt_item * item = opt->item;
279   for (int i=0;i<oc->hooks_before_value_count;i++)
280     oc->hooks_before_value[i]->u.call(item, value, oc->hooks_before_value[i]->ptr);
281
282   switch (item->cls) {
283     case OPT_CL_BOOL:
284       if (!value || !strcasecmp(value, "y") || !strcasecmp(value, "yes") || !strcasecmp(value, "true") || !strcasecmp(value, "1"))
285         *((int *) item->ptr) = 1 ^ (!!(opt->flags & OPT_NEGATIVE));
286       else if (!strcasecmp(value, "n") || !strcasecmp(value, "no") || !strcasecmp(value, "false") || !strcasecmp(value, "0"))
287         *((int *) item->ptr) = 0 ^ (!!(opt->flags & OPT_NEGATIVE));
288       else
289         opt_failure("Boolean argument for %s has a strange value. Supported (case insensitive): y/n, yes/no, true/false.", OPT_NAME);
290       break;
291     case OPT_CL_STATIC:
292       {
293         char * e = NULL;
294         switch (item->type) {
295           case CT_INT:
296             if (!value)
297               *OPT_PTR(int) = 0;
298             else
299               e = cf_parse_int(value, OPT_PTR(int));
300             if (e)
301               opt_failure("Integer value parsing failed for %s: %s", OPT_NAME, e);
302             break;
303           case CT_U64:
304             if (!value)
305               *OPT_PTR(u64) = 0;
306             else
307               e = cf_parse_u64(value, OPT_PTR(u64));
308             if (e)
309               opt_failure("Unsigned 64-bit value parsing failed for %s: %s", OPT_NAME, e);
310             break;
311           case CT_DOUBLE:
312             if (!value)
313               *OPT_PTR(double) = NAN;
314             else
315               e = cf_parse_double(value, OPT_PTR(double));
316             if (e)
317               opt_failure("Double value parsing failed for %s: %s", OPT_NAME, e);
318             break;
319           case CT_IP:
320             if (!value)
321               e = cf_parse_ip("0.0.0.0", OPT_PTR(u32));
322             else
323               e = cf_parse_ip(value, OPT_PTR(u32));
324             if (e)
325               opt_failure("IP parsing failed for %s: %s", OPT_NAME, e);
326             break;
327           case CT_STRING:
328             if (!value)
329               *OPT_PTR(const char *) = NULL;
330             else
331               *OPT_PTR(const char *) = xstrdup(value);
332             break;
333           default:
334             ASSERT(0);
335         }
336         break;
337       }
338     case OPT_CL_SWITCH:
339       if (*((int *)item->ptr) != -1)
340         opt_failure("Multiple switches: %s", OPT_NAME);
341       else
342         *((int *)item->ptr) = item->u.value;
343       break;
344     case OPT_CL_INC:
345       if (opt->flags & OPT_NEGATIVE)
346         (*((int *)item->ptr))--;
347       else
348         (*((int *)item->ptr))++;
349       break;
350     case OPT_CL_CALL:
351       item->u.call(item, value, item->ptr);
352       break;
353     case OPT_CL_USER:
354       {
355         char * e = NULL;
356         e = item->u.utype->parser(value, OPT_PTR(void*));
357         if (e)
358           opt_failure("User defined type value parsing failed for %s: %s", OPT_NAME, e);
359         break;
360       }
361     default:
362       ASSERT(0);
363   }
364
365   for (int i=0;i<oc->hooks_after_value_count;i++)
366     oc->hooks_after_value[i]->u.call(item, value, oc->hooks_after_value[i]->ptr);
367 }
368 #undef OPT_NAME
369
370 static int opt_longopt(struct opt_context * oc, char ** argv, int index) {
371   int eaten = 0;
372   char * name_in = argv[index] + 2; // skipping the -- on the beginning
373   uns pos = strchrnul(name_in, '=') - name_in;
374   struct opt_precomputed * opt = opt_find_item_longopt(oc, strndupa(name_in, pos));
375   char * value = NULL;
376
377   if (opt->item->cls == OPT_CL_BOOL && !strncmp(name_in, "no-", 3) && !strncmp(name_in+3, opt->item->name, pos-3))
378     value = "n";
379   else if (opt->flags & OPT_REQUIRED_VALUE) {
380     if (pos < strlen(name_in))
381       value = name_in + pos + 1;
382     else {
383       value = argv[index+1];
384       if (!value)
385         opt_failure("Argument --%s must have a value but nothing supplied.", opt->name);
386       eaten++;
387     }
388   }
389   else if (opt->flags & OPT_MAYBE_VALUE) {
390     if (pos < strlen(name_in))
391       value = name_in + pos + 1;
392   }
393   else {
394     if (pos < strlen(name_in))
395       opt_failure("Argument --%s must not have any value.", opt->name);
396   }
397   opt_parse_value(oc, opt, value, 1);
398   return eaten;
399 }
400
401 static int opt_shortopt(struct opt_context * oc, char ** argv, int index) {
402   int chr = 0;
403   struct opt_precomputed * opt;
404   while (argv[index][++chr] && (opt = opt_find_item_shortopt(oc, argv[index][chr]))) {
405     if (opt->flags & OPT_NO_VALUE) {
406       opt_parse_value(oc, opt, NULL, 0);
407     }
408     else if (opt->flags & OPT_REQUIRED_VALUE) {
409       if (chr == 1 && argv[index][2]) {
410         opt_parse_value(oc, opt, argv[index] + 2, 0);
411         return 0;
412       }
413       else if (argv[index][chr+1])
414         opt_failure("Option -%c must have a value but found inside a bunch of short opts.", opt->item->letter);
415       else if (!argv[index+1])
416         opt_failure("Option -%c must have a value but nothing supplied.", opt->item->letter);
417       else {
418         opt_parse_value(oc, opt, argv[index+1], 0);
419         return 1;
420       }
421     }
422     else if (opt->flags & OPT_MAYBE_VALUE) {
423       if (chr == 1 && argv[index][2]) {
424         opt_parse_value(oc, opt, argv[index] + 2, 0);
425         return 0;
426       }
427       else
428         opt_parse_value(oc, opt, NULL, 0);
429     }
430     else {
431       ASSERT(0);
432     }
433   }
434
435   if (argv[index][chr])
436     opt_failure("Unknown option -%c.", argv[index][chr]);
437
438   return 0;
439 }
440
441 static void opt_positional(struct opt_context * oc, char * value) {
442   oc->positional_count++;
443   struct opt_precomputed * opt = opt_find_item_shortopt(oc, (oc->positional_count > oc->positional_max ? 256 : oc->positional_count + 256));
444   if (!opt) {
445     ASSERT(oc->positional_count > oc->positional_max);
446     opt_failure("Too many positional args.");
447   }
448
449   opt_parse_value(oc, opt, value, 2);
450 }
451
452 #define OPT_TRAVERSE_SECTIONS \
453     while (item->cls == OPT_CL_SECTION) { \
454       if (stk->next) \
455         stk = stk->next; \
456       else { \
457         struct opt_stack * new_stk = alloca(sizeof(*new_stk)); \
458         new_stk->prev = stk; \
459         stk->next = new_stk; \
460         stk = new_stk; \
461       } \
462       stk->this = item; \
463       item = item->u.section->opt; \
464     } \
465     if (item->cls == OPT_CL_END) { \
466       if (!stk->prev) break; \
467       item = stk->this; \
468       stk = stk->prev; \
469       continue; \
470     }
471
472 void opt_parse(const struct opt_section * options, char ** argv) {
473   struct opt_stack {
474     struct opt_item * this;
475     struct opt_stack * prev;
476     struct opt_stack * next;
477   } * stk = alloca(sizeof(*stk));
478   stk->this = NULL;
479   stk->prev = NULL;
480   stk->next = NULL;
481
482   struct opt_context * oc = alloca(sizeof(*oc));
483   memset(oc, 0, sizeof (*oc));
484
485   int count = 0;
486   int hooks = 0;
487
488   for (struct opt_item * item = options->opt; ; item++) {
489     OPT_TRAVERSE_SECTIONS;
490     if (item->letter || item->name)
491       count++;
492     if (item->cls == OPT_CL_BOOL)
493       count++;
494     if (item->letter > 256)
495       oc->positional_max++;
496     if (item->cls == OPT_CL_HOOK)
497       hooks++;
498   }
499
500   oc->opts = alloca(sizeof(*oc->opts) * count);
501   oc->shortopt = alloca(sizeof(*oc->shortopt) * (oc->positional_max + 257));
502   memset(oc->shortopt, 0, sizeof(*oc->shortopt) * (oc->positional_max + 257));
503   oc->hooks_before_arg = alloca(sizeof (*oc->hooks_before_arg) * hooks);
504   oc->hooks_before_value = alloca(sizeof (*oc->hooks_before_value) * hooks);
505   oc->hooks_after_value = alloca(sizeof (*oc->hooks_after_value) * hooks);
506
507   oc->hooks_before_arg_count = 0;
508   oc->hooks_before_value_count = 0;
509   oc->hooks_after_value_count = 0;
510
511   oc->opt_count = 0;
512
513   for (struct opt_item * item = options->opt; ; item++) {
514     OPT_TRAVERSE_SECTIONS;
515     if (item->letter || item->name) {
516       struct opt_precomputed * opt = xmalloc(sizeof(*opt));
517       opt->item = item;
518       opt->flags = item->flags;
519       opt->count = 0;
520       opt->name = item->name;
521       oc->opts[oc->opt_count++] = opt;
522       if (item->letter)
523         oc->shortopt[(int) item->letter] = opt;
524       OPT_ADD_DEFAULT_ITEM_FLAGS(item, opt->flags);
525     }
526     if (item->cls == OPT_CL_HOOK) {
527       if (item->flags & OPT_HOOK_BEFORE_ARG)
528         oc->hooks_before_arg[oc->hooks_before_arg_count++] = item;
529       else if (item->flags & OPT_HOOK_BEFORE_VALUE)
530         oc->hooks_before_value[oc->hooks_before_value_count++] = item;
531       else if (item->flags & OPT_HOOK_AFTER_VALUE)
532         oc->hooks_after_value[oc->hooks_after_value_count++] = item;
533       else
534         ASSERT(0);
535     }
536   }
537
538   int force_positional = 0;
539   for (int i=0;argv[i];i++) {
540     for (int j=0;j<oc->hooks_before_arg_count;j++)
541       oc->hooks_before_arg[j]->u.call(NULL, NULL, oc->hooks_before_arg[j]->ptr);
542     if (argv[i][0] != '-' || force_positional) {
543       opt_positional(oc, argv[i]);
544     }
545     else {
546       if (argv[i][1] == '-') {
547         if (argv[i][2] == '\0')
548           force_positional++;
549         else
550           i += opt_longopt(oc, argv, i);
551       }
552       else if (argv[i][1])
553         i += opt_shortopt(oc, argv, i);
554       else
555         opt_positional(oc, argv[i]);
556     }
557   }
558
559   for (int i=0;i<oc->positional_max+257;i++) {
560     if (!oc->shortopt[i])
561       continue;
562     if (!oc->shortopt[i]->count && (oc->shortopt[i]->flags & OPT_REQUIRED))
563       if (i < 256)
564         if (oc->shortopt[i]->item->name)
565           opt_failure("Required option -%c/--%s not found.", oc->shortopt[i]->item->letter, oc->shortopt[i]->item->name);
566         else
567           opt_failure("Required option -%c not found.", oc->shortopt[i]->item->letter);
568       else
569         opt_failure("Required positional argument #%d not found.", (i > 256) ? oc->shortopt[i]->item->letter-256 : oc->positional_max+1);
570   }
571
572   for (int i=0;i<oc->opt_count;i++) {
573     if (!oc->opts[i])
574       continue;
575     if (!oc->opts[i]->count && (oc->opts[i]->flags & OPT_REQUIRED))
576       opt_failure("Required option --%s not found.", oc->opts[i]->item->name);
577   }
578 }
579
580 static void opt_conf_end_of_options(struct cf_context *cc) {
581   cf_load_default(cc);
582   if (cc->postpone_commit && cf_close_group())
583     opt_failure("Loading of configuration failed");
584 }
585
586 void opt_conf_internal(struct opt_item * opt, const char * value, void * data UNUSED) {
587   struct cf_context *cc = cf_get_context();
588   switch (opt->letter) {
589     case 'S':
590       cf_load_default(cc);
591       if (cf_set(value))
592         opt_failure("Cannot set %s", value);
593       break;
594     case 'C':
595       if (cf_load(value))
596         opt_failure("Cannot load config file %s", value);
597       break;
598 #ifdef CONFIG_UCW_DEBUG
599     case '0':
600       opt_conf_end_of_options(cc);
601       struct fastbuf *b = bfdopen(1, 4096);
602       cf_dump_sections(b);
603       bclose(b);
604       exit(0);
605       break;
606 #endif
607   }
608 }
609
610 void opt_conf_hook_internal(struct opt_item * opt, const char * value UNUSED, void * data UNUSED) {
611   static enum {
612     OPT_CONF_HOOK_BEGIN,
613     OPT_CONF_HOOK_CONFIG,
614     OPT_CONF_HOOK_OTHERS
615   } state = OPT_CONF_HOOK_BEGIN;
616
617   int confopt = 0;
618
619   if (opt->letter == 'S' || opt->letter == 'C' || (opt->name && !strcmp(opt->name, "dumpconfig")))
620     confopt = 1;
621
622   switch (state) {
623     case OPT_CONF_HOOK_BEGIN:
624       if (confopt)
625         state = OPT_CONF_HOOK_CONFIG;
626       else {
627         opt_conf_end_of_options(cf_get_context());
628         state = OPT_CONF_HOOK_OTHERS;
629       }
630       break;
631     case OPT_CONF_HOOK_CONFIG:
632       if (!confopt) {
633         opt_conf_end_of_options(cf_get_context());
634         state = OPT_CONF_HOOK_OTHERS;
635       }
636       break;
637     case OPT_CONF_HOOK_OTHERS:
638       if (confopt)
639         opt_failure("Config options (-C, -S) must stand before other options.");
640       break;
641     default:
642       ASSERT(0);
643   }
644 }