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