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