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