remove configfile parsing stuff, this also eliminates the dependency on bison/flex
[libfirm] / ir / libcore / lc_opts.c
1 /*
2   libcore: library for basic data structures and algorithms.
3   Copyright (C) 2005  IPD Goos, Universit"at Karlsruhe, Germany
4
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26
27 #ifdef _WIN32
28 #include <malloc.h>
29 #endif
30 #ifdef HAVE_ALLOCA_H
31 #include <alloca.h>
32 #endif
33
34 /* Includes to determine user's home directory */
35 #ifdef _WIN32
36 #include <shlobj.h>
37 #else
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <pwd.h>
41 #endif
42
43 /* maximum length of a path. */
44 #ifndef MAX_PATH
45 #define MAX_PATH 2048
46 #endif
47
48
49 #include "lc_common_t.h"
50 #include "lc_opts_t.h"
51 #include "lc_opts_enum.h"
52 #include "hashptr.h"
53 #include "lc_printf.h"
54 #include "xmalloc.h"
55
56 #define ERR_STRING "In argument \"%s\": "
57
58 #define OPT_DELIM '-'
59
60 #define HELP_TEMPL         "%-15s %-10s %-45s"
61 #define HELP_TEMPL_VALS    HELP_TEMPL " [%s] (%s)"
62
63 static struct obstack obst;
64
65 static void set_name(lc_opt_entry_t *ent, const char *name)
66 {
67         ent->name = name;
68         ent->hash = HASH_STR(name, strlen(name));
69 }
70
71 #define entry_matches(ent,hash_val,str) \
72         ((ent)->hash == hash_val && strcmp((ent)->name, (str)) == 0)
73
74 #define entries_equal(e1,e2) entry_matches(e1, (e2)->hash, (e2)->name)
75
76 static lc_opt_err_info_t *set_error(lc_opt_err_info_t *err, int error, const char *arg)
77 {
78         if (err) {
79                 err->error = error;
80                 err->msg = "";
81                 err->arg = arg;
82         }
83
84         return err;
85 }
86
87 int lc_opt_raise_error(const lc_opt_err_info_t *err, lc_opt_error_handler_t *handler,
88                 const char *fmt, ...)
89 {
90         va_list args;
91         int res = 0;
92
93         va_start(args, fmt);
94         if (err && lc_opt_is_error(err)) {
95                 res = 1;
96                 if (handler) {
97                         char buf[256];
98                         vsnprintf(buf, sizeof(buf), fmt, args);
99                         handler(buf, err);
100                 }
101         }
102         va_end(args);
103
104         return res;
105 }
106
107 static lc_opt_entry_t *init_entry(lc_opt_entry_t *ent, lc_opt_entry_t *parent,
108                 const char *name, const char *desc)
109 {
110         const char *copied_name;
111         const char *copied_desc;
112
113         obstack_grow0(&obst, name, strlen(name));
114         copied_name = (char*)obstack_finish(&obst);
115         obstack_grow0(&obst, desc, strlen(desc));
116         copied_desc = (char*)obstack_finish(&obst);
117
118         memset(ent, 0, sizeof(*ent));
119         set_name(ent, copied_name);
120         ent->desc = copied_desc;
121         ent->parent = parent;
122         return ent;
123 }
124
125 static lc_opt_entry_t *init_grp(lc_opt_entry_t *ent, lc_opt_err_info_t *err)
126 {
127         ent->is_grp = 1;
128         INIT_LIST_HEAD(&ent->v.grp.grps);
129         INIT_LIST_HEAD(&ent->v.grp.opts);
130
131         set_error(err, lc_opt_err_none, "");
132         if (ent->parent) {
133                 if (ent->parent->is_grp)
134                         list_add_tail(&ent->list, &lc_get_grp_special(ent->parent)->grps);
135                 else
136                         set_error(err, lc_opt_err_grp_expected, ent->parent->name);
137         }
138
139         return ent;
140 }
141
142 static lc_opt_entry_t *init_opt(lc_opt_entry_t *ent,
143                                                                 lc_opt_type_t type,
144                                                                 void *val, size_t length,
145                                                                 lc_opt_callback_t *cb,
146                                                                 lc_opt_dump_t *dump,
147                                                                 lc_opt_dump_vals_t *dump_vals,
148                                                                 lc_opt_err_info_t *err)
149 {
150         lc_opt_special_t *s = lc_get_opt_special(ent);
151
152         ent->is_grp = 0;
153         set_error(err, lc_opt_err_none, "");
154         list_add_tail(&ent->list, &lc_get_grp_special(ent->parent)->opts);
155
156         s->type      = type;
157         s->value     = val;
158         s->cb        = cb;
159         s->dump      = dump;
160         s->dump_vals = dump_vals;
161         s->length    = length;
162
163         return ent;
164 }
165
166
167 lc_opt_entry_t *lc_opt_root_grp(void)
168 {
169         static lc_opt_entry_t root_group;
170         static int inited = 0;
171
172         if (!inited) {
173                 obstack_init(&obst);
174                 inited = 1;
175
176                 init_entry(&root_group, NULL, "root", "The root node");
177                 init_grp(&root_group, NULL);
178         }
179
180         return &root_group;
181 }
182
183 int lc_opt_grp_is_root(const lc_opt_entry_t *ent)
184 {
185         return ent->parent == NULL;
186 }
187
188 static const char *get_type_name(lc_opt_type_t type)
189 {
190         const char *res;
191
192 #define XXX(t) case lc_opt_type_ ## t: res = #t; break
193         switch (type) {
194                 XXX(enum);
195                 XXX(bit);
196                 XXX(int);
197                 XXX(double);
198                 XXX(boolean);
199                 XXX(string);
200                 case lc_opt_type_negbit:     res = "bit";     break;
201                 case lc_opt_type_negboolean: res = "boolean"; break;
202                 default:
203                 res = "<none>";
204         }
205 #undef XXX
206
207         return res;
208 }
209
210 const char *lc_opt_get_type_name(const lc_opt_entry_t *ent)
211 {
212         return get_type_name(lc_get_opt_special(ent)->type);
213 }
214
215 lc_opt_entry_t *lc_opt_get_grp(lc_opt_entry_t *parent, const char *name)
216 {
217         lc_opt_entry_t *ent = lc_opt_find_grp(parent, name, NULL);
218
219         if (!ent) {
220                 ent = OALLOC(&obst, lc_opt_entry_t);
221                 init_entry(ent, parent, name, "");
222                 init_grp(ent, NULL);
223         }
224
225         return ent;
226 }
227
228 lc_opt_entry_t *lc_opt_add_opt(lc_opt_entry_t *parent,
229                                                            const char *name, const char *desc,
230                                                            lc_opt_type_t type, void *value, size_t length,
231                                                            lc_opt_callback_t *cb, lc_opt_dump_t *dump,
232                                                            lc_opt_dump_vals_t *dump_vals,
233                                                            lc_opt_err_info_t *err)
234 {
235         lc_opt_entry_t *res = NULL;
236
237         if (parent->is_grp) {
238                 lc_opt_entry_t *ent = lc_opt_find_opt(parent, name, NULL);
239
240                 if (!ent) {
241                         res = OALLOC(&obst, lc_opt_entry_t);
242                         init_entry(res, parent, name, desc);
243                         init_opt(res, type, value, length, cb, dump, dump_vals, err);
244                 } else
245                         set_error(err, lc_opt_err_opt_already_there, name);
246         } else
247                 set_error(err, lc_opt_err_grp_expected, name);
248
249         return res;
250 }
251
252
253 static lc_opt_entry_t *lc_opt_find_ent(const struct list_head *head, const char *name,
254                 int error_to_use, lc_opt_err_info_t *err)
255 {
256         lc_opt_entry_t *ent, *found = NULL;
257         int error = error_to_use;
258         unsigned hash = HASH_STR(name, strlen(name));
259
260         if (!list_empty(head)) {
261                 list_for_each_entry(lc_opt_entry_t, ent, head, list) {
262                         if (entry_matches(ent, hash, name)) {
263                                 error = lc_opt_err_none;
264                                 found = ent;
265                                 break;
266                         }
267                 }
268         }
269
270         set_error(err, error, name);
271         return found;
272 }
273
274 lc_opt_entry_t *lc_opt_find_grp(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
275 {
276         return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->grps,
277                         name, lc_opt_err_grp_not_found, err) : NULL;
278 }
279
280 lc_opt_entry_t *lc_opt_find_opt(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
281 {
282         return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->opts,
283                         name, lc_opt_err_opt_not_found, err) : NULL;
284 }
285
286 static const lc_opt_entry_t *resolve_up_to_last(const lc_opt_entry_t *root,
287                 const char * const *names, int pos, int n, lc_opt_err_info_t *err)
288 {
289         lc_opt_entry_t *ent;
290
291         if (pos == n)
292                 return root;
293
294         ent = lc_opt_find_grp(root, names[pos], err);
295         return ent ? resolve_up_to_last(ent, names, pos + 1, n, err) : NULL;
296 }
297
298 static const char *path_delim = "/.";
299
300 static lc_opt_entry_t *resolve_up_to_last_str_rec(lc_opt_entry_t *from,
301                                                                                                                 const char *path,
302                                                                                                                 const char **last_name)
303 {
304
305         lc_opt_entry_t *res = from;
306         size_t end          = strcspn(path, path_delim);
307
308         if (path[end] != '\0') {
309                 /* skip all delimiters */
310                 size_t next = strspn(path + end, path_delim);
311
312                 /* copy the part of the path into a buffer */
313                 char *buf = (char*)malloc((end+1) * sizeof(buf[0]));
314                 strncpy(buf, path, end);
315                 buf[end] = '\0';
316
317                 /* resolve the group and free */
318                 from = lc_opt_get_grp(from, buf);
319                 free(buf);
320
321                 res = resolve_up_to_last_str_rec(from, path + end + next, last_name);
322         }
323
324         else if (last_name != NULL) {
325                 *last_name = path;
326         }
327
328         return res;
329 }
330
331 static lc_opt_entry_t *resolve_up_to_last_str(lc_opt_entry_t *root, const char *path, const char **last_name)
332 {
333         size_t next = strspn(path, path_delim);
334
335         /* if l != 0 we saw deliminators, so we resolve from the root */
336         if (next > 0)
337                 root = lc_opt_root_grp();
338
339         return resolve_up_to_last_str_rec(root, path + next, last_name);
340 }
341
342 lc_opt_entry_t *lc_opt_resolve_grp(const lc_opt_entry_t *root,
343                 const char * const *names, int n, lc_opt_err_info_t *err)
344 {
345         const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
346         return lc_opt_find_grp(grp, names[n - 1], err);
347 }
348
349 lc_opt_entry_t *lc_opt_resolve_opt(const lc_opt_entry_t *root,
350                 const char * const *names, int n, lc_opt_err_info_t *err)
351 {
352         const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
353         return lc_opt_find_opt(grp, names[n - 1], err);
354 }
355
356 static char *strtolower(char *buf, size_t n, const char *str)
357 {
358         unsigned i;
359         for (i = 0; i < n; ++i)
360                 buf[i] = tolower(str[i]);
361         return buf;
362 }
363
364 int lc_opt_std_cb(UNUSED(const char *name), lc_opt_type_t type, void *data, size_t length, ...)
365 {
366         va_list args;
367         int res = 0;
368         int integer;
369
370         va_start(args, length);
371
372         if (data) {
373                 res = 1;
374                 switch (type) {
375                 case lc_opt_type_bit:
376                         integer = va_arg(args, int);
377                         if (integer)
378                                 *((int *) data) |= length;
379                         else
380                                 *((int *) data) &= ~length;
381                         break;
382
383                 case lc_opt_type_negbit:
384                         integer = va_arg(args, int);
385                         if (integer)
386                                 *((int *) data) &= ~length;
387                         else
388                                 *((int *) data) |= length;
389                         break;
390
391                 case lc_opt_type_boolean:
392                         *((int *) data) = va_arg(args, int);
393                         break;
394
395                 case lc_opt_type_negboolean:
396                         *((int *) data) = !va_arg(args, int);
397                         break;
398
399                 case lc_opt_type_string:
400                         strncpy((char*)data, va_arg(args, const char *), length);
401                         break;
402
403                 case lc_opt_type_int:
404                         *((int *) data) = va_arg(args, int);
405                         break;
406
407                 case lc_opt_type_double:
408                         *((double *) data) = va_arg(args, double);
409                         break;
410                 default:
411                         res = 0;
412                 }
413         }
414
415         va_end(args);
416         return res;
417 }
418
419 int lc_opt_std_dump(char *buf, size_t n, UNUSED(const char *name), lc_opt_type_t type, void *data, UNUSED(size_t length))
420 {
421         int res;
422
423         if (data) {
424                 switch (type) {
425                 case lc_opt_type_bit:
426                 case lc_opt_type_negbit:
427                         res = snprintf(buf, n, "%x", *((unsigned *) data));
428                         break;
429                 case lc_opt_type_boolean:
430                 case lc_opt_type_negboolean:
431                         res = snprintf(buf, n, "%s", *((int *) data) ? "true" : "false");
432                         break;
433                 case lc_opt_type_string:
434                         strncpy(buf, (const char*)data, n);
435                         res = n;
436                         break;
437                 case lc_opt_type_int:
438                         res = snprintf(buf, n, "%d", *((int *) data));
439                         break;
440                 case lc_opt_type_double:
441                         res = snprintf(buf, n, "%g", *((double *) data));
442                         break;
443                 default:
444                         strncpy(buf, "", n);
445                         res = 0;
446                 }
447         }
448
449         else {
450                 strncpy(buf, "", n);
451                 res = 0;
452         }
453
454         return res;
455 }
456
457 int lc_opt_bool_dump_vals(char *buf, size_t n, UNUSED(const char *name), UNUSED(lc_opt_type_t type), UNUSED(void *data), UNUSED(size_t length))
458 {
459         strncpy(buf, "true, false", n);
460         return n;
461 }
462
463 int lc_opt_occurs(lc_opt_entry_t *opt, const char *value, lc_opt_err_info_t *err)
464 {
465         static const struct {
466                 const char *str;
467                 int val;
468         } bool_strings[] = {
469                 { "yes", 1 },
470                 { "true", 1 },
471                 { "on", 1 },
472                 { "1", 1 },
473                 { "no", 0 },
474                 { "false", 0 },
475                 { "off", 0 },
476                 { "0", 0 },
477         };
478
479         unsigned i;
480         int error = lc_opt_err_illegal_format;
481         lc_opt_special_t *s = lc_get_opt_special(opt);
482         char buf[16];
483         union {
484                 int integer;
485                 double dbl;
486         } val_storage, *val = &val_storage;
487
488         if (!opt) {
489                 set_error(err, lc_opt_err_opt_not_found, "");
490                 return 0;
491         }
492
493         if (!s->cb) {
494                 set_error(err, lc_opt_err_no_callback, "");
495                 return 0;
496         }
497
498         s->is_set = 1;
499
500         switch (s->type) {
501                 case lc_opt_type_int:
502                         if (sscanf(value, "%i", (int *) val)) {
503                                 error = lc_opt_err_unknown_value;
504                                 if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
505                                         error = lc_opt_err_none;
506                         }
507                         break;
508
509                 case lc_opt_type_double:
510                         if (sscanf(value, "%lf", (double *) val)) {
511                                 error = lc_opt_err_unknown_value;
512                                 if (s->cb(opt->name, s->type, s->value, s->length, val->dbl))
513                                         error = lc_opt_err_none;
514                         }
515                         break;
516
517                 case lc_opt_type_boolean:
518                 case lc_opt_type_negboolean:
519                 case lc_opt_type_bit:
520                 case lc_opt_type_negbit:
521                                 strtolower(buf, sizeof(buf), value);
522                                 for (i = 0; i < LC_ARRSIZE(bool_strings); ++i) {
523                                         if (strcmp(buf, bool_strings[i].str) == 0) {
524                                                 val->integer = bool_strings[i].val;
525                                                 error = lc_opt_err_none;
526                                                 break;
527                                         }
528                                 }
529
530                                 if (error == lc_opt_err_none) {
531                                         error = lc_opt_err_unknown_value;
532                                         if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
533                                                 error = lc_opt_err_none;
534                                 }
535
536                         break;
537
538                 case lc_opt_type_string:
539                 case lc_opt_type_enum:
540                         error = lc_opt_err_unknown_value;
541                         if (s->cb(opt->name, s->type, s->value, s->length, value))
542                                 error = lc_opt_err_none;
543                         break;
544                 case lc_opt_type_invalid:
545                         abort();
546         }
547
548         set_error(err, error, value);
549         return error == lc_opt_err_none;
550 }
551
552 char *lc_opt_value_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
553 {
554         const lc_opt_special_t *s = lc_get_opt_special(ent);
555         if (s->dump)
556                 s->dump(buf, len, ent->name, s->type, s->value, s->length);
557         else
558                 strncpy(buf, "<n/a>", len);
559
560         return buf;
561 }
562
563 static char *lc_opt_values_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
564 {
565         const lc_opt_special_t *s = lc_get_opt_special(ent);
566         if (s->dump_vals)
567                 s->dump_vals(buf, len, ent->name, s->type, s->value, s->length);
568
569         return buf;
570 }
571
572 int lc_opt_add_table(lc_opt_entry_t *root, const lc_opt_table_entry_t *table)
573 {
574         int i, res = 0;
575         lc_opt_err_info_t err;
576
577         for (i = 0; table[i].name != NULL; ++i) {
578                 const char *name;
579                 const lc_opt_table_entry_t *tab = &table[i];
580                 lc_opt_entry_t *grp = resolve_up_to_last_str(root, tab->name, &name);
581
582                 lc_opt_add_opt(grp, name, tab->desc, tab->type, tab->value, tab->len, tab->cb, tab->dump, tab->dump_vals, &err);
583                 if (err.error != lc_opt_err_none)
584                         res = 1;
585         }
586
587         return res;
588 }
589
590 static void lc_opt_print_grp_path_rec(char *buf, size_t len, const lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent)
591 {
592         if (ent == stop_ent)
593                 return;
594         if (!lc_opt_grp_is_root(ent)) {
595                 size_t l;
596                 lc_opt_print_grp_path_rec(buf, len, ent->parent, separator, stop_ent);
597                 l = strlen(buf);
598                 if (l > 0 && l < len-1) {
599                         buf[l]     = separator;
600                         buf[l + 1] = '\0';
601                 }
602         }
603
604         strncat(buf, ent->name, len-1);
605 }
606
607 static char *lc_opt_print_grp_path(char *buf, size_t len, const lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent)
608 {
609         if (len > 0)
610                 buf[0] = '\0';
611         lc_opt_print_grp_path_rec(buf, len, ent, separator, stop_ent);
612         return buf;
613 }
614
615 /**
616  * dump the option tree.
617  * @param ent        starting entity
618  * @param separator  separator char
619  * @param stop_ent   stop at this entity when dumping the name
620  * @param f          output file
621  */
622 static void lc_opt_print_help_rec(lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent, FILE *f)
623 {
624         lc_grp_special_t *s = lc_get_grp_special(ent);
625         char grp_name[512];
626         char value[256];
627         char values[512];
628         lc_opt_entry_t *e;
629
630         if (!list_empty(&s->opts)) {
631                 lc_opt_print_grp_path(grp_name, sizeof(grp_name), ent, separator, stop_ent);
632                 fputc('\n', f);
633                 if (grp_name[0])
634                         fprintf(f, "%s:\n", grp_name);
635
636                 list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
637                         value[0]  = '\0';
638                         values[0] = '\0';
639                         lc_opt_value_to_string(value, sizeof(value), e);
640                         lc_opt_values_to_string(values, sizeof(values), e);
641                         fprintf(f, HELP_TEMPL_VALS "\n", e->name, lc_opt_get_type_name(e), e->desc, value, values);
642                 }
643         }
644
645         list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
646                 lc_opt_print_help_rec(e, separator, stop_ent, f);
647         }
648
649 }
650
651 void lc_opt_print_help(lc_opt_entry_t *ent, FILE *f)
652 {
653         fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
654         lc_opt_print_help_rec(ent, '.', NULL, f);
655 }
656
657 void lc_opt_print_help_for_entry(lc_opt_entry_t *ent, char separator, FILE *f)
658 {
659         fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
660         lc_opt_print_help_rec(ent, separator, ent, f);
661 }
662
663
664 static void indent(FILE *f, int n)
665 {
666         int i;
667         for (i = 0; i < n; ++i)
668                 fputc(' ', f);
669 }
670
671 static void lc_opt_print_tree_lc_opt_indent(lc_opt_entry_t *ent, FILE *f, int level)
672 {
673         char buf[256];
674         lc_opt_special_t *s = lc_get_opt_special(ent);
675
676         indent(f, level);
677         fprintf(f, "%c%s(\"%s\"):%s = %s\n", s->is_set ? '+' : '-', ent->name,
678                         ent->desc, lc_opt_get_type_name(ent), lc_opt_value_to_string(buf, sizeof(buf), ent));
679 }
680
681 static void lc_opt_print_tree_grp_indent(lc_opt_entry_t *ent, FILE *f, int level)
682 {
683         lc_grp_special_t *s;
684
685         if (ent->is_grp) {
686                 lc_opt_entry_t *e;
687
688                 s = lc_get_grp_special(ent);
689                 indent(f, level);
690                 fprintf(f, "/%s\n", ent->name);
691
692                 list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
693                         lc_opt_print_tree_grp_indent(e, f, level + 2);
694                 }
695
696                 list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
697                         lc_opt_print_tree_lc_opt_indent(e, f, level + 2);
698                 }
699         }
700 }
701
702 void lc_opt_print_tree(lc_opt_entry_t *ent, FILE *f)
703 {
704         lc_opt_print_tree_grp_indent(ent, f, 0);
705 }
706
707 static int lc_opts_default_error_handler(const char *prefix, const lc_opt_err_info_t *err)
708 {
709         fprintf(stderr, "%s: %s; %s\n", prefix, err->msg, err->arg);
710         return 0;
711 }
712
713 int lc_opt_from_single_arg(const lc_opt_entry_t *root,
714                                                    const char *opt_prefix,
715                                                    const char *arg, lc_opt_error_handler_t *handler)
716 {
717         const lc_opt_entry_t *grp = root;
718         size_t n                  = strlen(arg);
719         size_t n_prefix           = opt_prefix ? strlen(opt_prefix) : 0;
720         int error                 = 0;
721         int ret                   = 0;
722
723         lc_opt_err_info_t err;
724         const char *end, *eqsign;
725
726         if (n >= n_prefix && strncmp(opt_prefix, arg, n_prefix) == 0) {
727                 arg = arg + n_prefix;
728
729                 /* find the next delimiter (the -) and extract the string up to
730                  * there. */
731                 end    = strchr(arg, OPT_DELIM);
732                 eqsign = strchr(arg, '=');
733                 if (eqsign && eqsign < end)
734                         end = NULL;
735                 while (end != NULL) {
736                         /*
737                          * Copy the part of the option into the buffer and add the
738                          * finalizing zero.
739                          */
740                         char *buf = (char*)obstack_copy0(&obst, arg, end - arg);
741
742                         /* Resolve the group inside the group */
743                         grp = lc_opt_find_grp(grp, buf, &err);
744                         error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
745                         if (error)
746                                 break;
747
748                         /* Find the next option part delimiter. */
749                         arg = end + 1;
750                         end    = strchr(arg, OPT_DELIM);
751                         eqsign = strchr(arg, '=');
752                         if (eqsign && eqsign < end)
753                                 end = NULL;
754                         obstack_free(&obst, buf);
755                 }
756
757                 if (!error) {
758                         lc_opt_entry_t *opt;
759                         char           *buf;
760
761                         /*
762                          * Now, we are at the last option part:
763                          * --grp1-grp2-...-grpn-opt=value
764                          * Check, for the = and evaluate the option string. If the = is
765                          * missing, we should have a boolean option, but that is checked
766                          * later.
767                          */
768                         end = strchr(arg, '=');
769                         buf = (char*)obstack_copy0(&obst, arg, end ? end - arg : (int) strlen(arg));
770                         opt = lc_opt_find_opt(grp, buf, &err);
771                         error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
772
773                         if (!error) {
774                                 /*
775                                  * Now evaluate the parameter of the option (the part after
776                                  * the =) if it was given.
777                                  */
778                                 arg = end ? end + 1 : "true";
779
780                                 /* Set the value of the option. */
781                                 lc_opt_occurs(opt, arg, &err);
782                                 ret = !lc_opt_raise_error(&err, handler, ERR_STRING, arg);
783                         }
784                 }
785         }
786
787         return ret;
788 }
789
790 int lc_opt_from_argv(const lc_opt_entry_t *root,
791                                          const char *opt_prefix,
792                                          int argc, const char *argv[],
793                                          lc_opt_error_handler_t *handler)
794 {
795         int i;
796         int options_set = 0;
797
798         if (handler == NULL)
799                 handler = lc_opts_default_error_handler;
800
801         for (i = 0; i < argc; ++i) {
802                 options_set |= lc_opt_from_single_arg(root, opt_prefix, argv[i], handler);
803         }
804
805         return options_set;
806 }
807
808 static int opt_arg_type(UNUSED(const lc_arg_occ_t *occ))
809 {
810         return lc_arg_type_ptr;
811 }
812
813 static int opt_arg_emit(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *arg)
814 {
815         char buf[256];
816
817         lc_opt_entry_t *opt = (lc_opt_entry_t*)arg->v_ptr;
818         const char     *s   = buf;
819         size_t          res = 0;
820
821         switch (occ->conversion) {
822         case 'V':
823                 lc_opt_value_to_string(buf, sizeof(buf), opt);
824                 break;
825         case 'T':
826                 s = lc_opt_get_type_name(opt);
827                 break;
828         case 'D':
829                 s = opt->desc;
830                 break;
831         case 'O':
832                 s = opt->name;
833                 break;
834         default:
835                 s = NULL;
836         }
837
838         if (s)
839                 res = lc_appendable_snadd(app, s, strlen(s));
840
841         return res;
842 }
843
844 static const lc_arg_handler_t lc_opt_arg_handler = {
845         opt_arg_type,
846         opt_arg_emit
847 };
848
849
850 /* lc_printf facility for options */
851
852 const lc_arg_env_t *lc_opt_get_arg_env(void)
853 {
854         static lc_arg_env_t *env = NULL;
855
856         if (!env) {
857                 env = lc_arg_new_env();
858
859                 lc_arg_register(env, "opt:value", 'V', &lc_opt_arg_handler);
860                 lc_arg_register(env, "opt:type",  'T', &lc_opt_arg_handler);
861                 lc_arg_register(env, "opt:desc",  'D', &lc_opt_arg_handler);
862                 lc_arg_register(env, "opt:name",  'O', &lc_opt_arg_handler);
863         }
864
865         return env;
866 }