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