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