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