make firm compilable with a c++ compiler
[libfirm] / ir / libcore / lc_opts.c
1 /*
2   libcore: library for basic data structures and algorithms.
3   Copyright (C) 2005  IPD Goos, Universit"at Karlsruhe, Germany
4
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 #include "config.h"
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26
27 #ifdef _WIN32
28 #include <malloc.h>
29 #endif
30 #ifdef HAVE_ALLOCA_H
31 #include <alloca.h>
32 #endif
33
34 /* Includes to determine user's home directory */
35 #ifdef _WIN32
36 #include <shlobj.h>
37 #else
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <pwd.h>
41 #endif
42
43 /* maximum length of a path. */
44 #ifndef MAX_PATH
45 #define MAX_PATH 2048
46 #endif
47
48
49 #include "lc_common_t.h"
50 #include "lc_opts_t.h"
51 #include "lc_opts_enum.h"
52 #include "lc_parser_t.h"
53 #include "hashptr.h"
54 #include "lc_printf.h"
55 #include "xmalloc.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 = (char*)obstack_finish(&obst);
116         obstack_grow0(&obst, desc, strlen(desc));
117         copied_desc = (char*)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 lc_opt_entry_t *lc_opt_get_grp(lc_opt_entry_t *parent, const char *name)
217 {
218         lc_opt_entry_t *ent = lc_opt_find_grp(parent, name, NULL);
219
220         if (!ent) {
221                 ent = OALLOC(&obst, lc_opt_entry_t);
222                 init_entry(ent, parent, name, "");
223                 init_grp(ent, NULL);
224         }
225
226         return ent;
227 }
228
229 lc_opt_entry_t *lc_opt_add_opt(lc_opt_entry_t *parent,
230                                                            const char *name, const char *desc,
231                                                            lc_opt_type_t type, void *value, size_t length,
232                                                            lc_opt_callback_t *cb, lc_opt_dump_t *dump,
233                                                            lc_opt_dump_vals_t *dump_vals,
234                                                            lc_opt_err_info_t *err)
235 {
236         lc_opt_entry_t *res = NULL;
237
238         if (parent->is_grp) {
239                 lc_opt_entry_t *ent = lc_opt_find_opt(parent, name, NULL);
240
241                 if (!ent) {
242                         res = OALLOC(&obst, lc_opt_entry_t);
243                         init_entry(res, parent, name, desc);
244                         init_opt(res, type, value, length, cb, dump, dump_vals, err);
245                 } else
246                         set_error(err, lc_opt_err_opt_already_there, name);
247         } else
248                 set_error(err, lc_opt_err_grp_expected, name);
249
250         return res;
251 }
252
253
254 static lc_opt_entry_t *lc_opt_find_ent(const struct list_head *head, const char *name,
255                 int error_to_use, lc_opt_err_info_t *err)
256 {
257         lc_opt_entry_t *ent, *found = NULL;
258         int error = error_to_use;
259         unsigned hash = HASH_STR(name, strlen(name));
260
261         if (!list_empty(head)) {
262                 list_for_each_entry(lc_opt_entry_t, ent, head, list) {
263                         if (entry_matches(ent, hash, name)) {
264                                 error = lc_opt_err_none;
265                                 found = ent;
266                                 break;
267                         }
268                 }
269         }
270
271         set_error(err, error, name);
272         return found;
273 }
274
275 lc_opt_entry_t *lc_opt_find_grp(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
276 {
277         return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->grps,
278                         name, lc_opt_err_grp_not_found, err) : NULL;
279 }
280
281 lc_opt_entry_t *lc_opt_find_opt(const lc_opt_entry_t *grp, const char *name, lc_opt_err_info_t *err)
282 {
283         return grp ? lc_opt_find_ent(&lc_get_grp_special(grp)->opts,
284                         name, lc_opt_err_opt_not_found, err) : NULL;
285 }
286
287 static const lc_opt_entry_t *resolve_up_to_last(const lc_opt_entry_t *root,
288                 const char * const *names, int pos, int n, lc_opt_err_info_t *err)
289 {
290         lc_opt_entry_t *ent;
291
292         if (pos == n)
293                 return root;
294
295         ent = lc_opt_find_grp(root, names[pos], err);
296         return ent ? resolve_up_to_last(ent, names, pos + 1, n, err) : NULL;
297 }
298
299 static const char *path_delim = "/.";
300
301 static lc_opt_entry_t *resolve_up_to_last_str_rec(lc_opt_entry_t *from,
302                                                                                                                 const char *path,
303                                                                                                                 const char **last_name)
304 {
305
306         lc_opt_entry_t *res = from;
307         size_t end          = strcspn(path, path_delim);
308
309         if (path[end] != '\0') {
310                 /* skip all delimiters */
311                 size_t next = strspn(path + end, path_delim);
312
313                 /* copy the part of the path into a buffer */
314                 char *buf = (char*)malloc((end+1) * sizeof(buf[0]));
315                 strncpy(buf, path, end);
316                 buf[end] = '\0';
317
318                 /* resolve the group and free */
319                 from = lc_opt_get_grp(from, buf);
320                 free(buf);
321
322                 res = resolve_up_to_last_str_rec(from, path + end + next, last_name);
323         }
324
325         else if (last_name != NULL) {
326                 *last_name = path;
327         }
328
329         return res;
330 }
331
332 static lc_opt_entry_t *resolve_up_to_last_str(lc_opt_entry_t *root, const char *path, const char **last_name)
333 {
334         size_t next = strspn(path, path_delim);
335
336         /* if l != 0 we saw deliminators, so we resolve from the root */
337         if (next > 0)
338                 root = lc_opt_root_grp();
339
340         return resolve_up_to_last_str_rec(root, path + next, last_name);
341 }
342
343 lc_opt_entry_t *lc_opt_resolve_grp(const lc_opt_entry_t *root,
344                 const char * const *names, int n, lc_opt_err_info_t *err)
345 {
346         const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
347         return lc_opt_find_grp(grp, names[n - 1], err);
348 }
349
350 lc_opt_entry_t *lc_opt_resolve_opt(const lc_opt_entry_t *root,
351                 const char * const *names, int n, lc_opt_err_info_t *err)
352 {
353         const lc_opt_entry_t *grp = resolve_up_to_last(root, names, 0, n - 1, err);
354         return lc_opt_find_opt(grp, names[n - 1], err);
355 }
356
357 static char *strtolower(char *buf, size_t n, const char *str)
358 {
359         unsigned i;
360         for (i = 0; i < n; ++i)
361                 buf[i] = tolower(str[i]);
362         return buf;
363 }
364
365 int lc_opt_std_cb(UNUSED(const char *name), lc_opt_type_t type, void *data, size_t length, ...)
366 {
367         va_list args;
368         int res = 0;
369         int integer;
370
371         va_start(args, length);
372
373         if (data) {
374                 res = 1;
375                 switch (type) {
376                 case lc_opt_type_bit:
377                         integer = va_arg(args, int);
378                         if (integer)
379                                 *((int *) data) |= length;
380                         else
381                                 *((int *) data) &= ~length;
382                         break;
383
384                 case lc_opt_type_negbit:
385                         integer = va_arg(args, int);
386                         if (integer)
387                                 *((int *) data) &= ~length;
388                         else
389                                 *((int *) data) |= length;
390                         break;
391
392                 case lc_opt_type_boolean:
393                         *((int *) data) = va_arg(args, int);
394                         break;
395
396                 case lc_opt_type_negboolean:
397                         *((int *) data) = !va_arg(args, int);
398                         break;
399
400                 case lc_opt_type_string:
401                         strncpy((char*)data, va_arg(args, const char *), length);
402                         break;
403
404                 case lc_opt_type_int:
405                         *((int *) data) = va_arg(args, int);
406                         break;
407
408                 case lc_opt_type_double:
409                         *((double *) data) = va_arg(args, double);
410                         break;
411                 default:
412                         res = 0;
413                 }
414         }
415
416         va_end(args);
417         return res;
418 }
419
420 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))
421 {
422         int res;
423
424         if (data) {
425                 switch (type) {
426                 case lc_opt_type_bit:
427                 case lc_opt_type_negbit:
428                         res = snprintf(buf, n, "%x", *((unsigned *) data));
429                         break;
430                 case lc_opt_type_boolean:
431                 case lc_opt_type_negboolean:
432                         res = snprintf(buf, n, "%s", *((int *) data) ? "true" : "false");
433                         break;
434                 case lc_opt_type_string:
435                         strncpy(buf, (const char*)data, n);
436                         res = n;
437                         break;
438                 case lc_opt_type_int:
439                         res = snprintf(buf, n, "%d", *((int *) data));
440                         break;
441                 case lc_opt_type_double:
442                         res = snprintf(buf, n, "%g", *((double *) data));
443                         break;
444                 default:
445                         strncpy(buf, "", n);
446                         res = 0;
447                 }
448         }
449
450         else {
451                 strncpy(buf, "", n);
452                 res = 0;
453         }
454
455         return res;
456 }
457
458 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))
459 {
460         strncpy(buf, "true, false", n);
461         return n;
462 }
463
464 int lc_opt_occurs(lc_opt_entry_t *opt, const char *value, lc_opt_err_info_t *err)
465 {
466         static const struct {
467                 const char *str;
468                 int val;
469         } bool_strings[] = {
470                 { "yes", 1 },
471                 { "true", 1 },
472                 { "on", 1 },
473                 { "1", 1 },
474                 { "no", 0 },
475                 { "false", 0 },
476                 { "off", 0 },
477                 { "0", 0 },
478         };
479
480         unsigned i;
481         int error = lc_opt_err_illegal_format;
482         lc_opt_special_t *s = lc_get_opt_special(opt);
483         char buf[16];
484         union {
485                 int integer;
486                 double dbl;
487         } val_storage, *val = &val_storage;
488
489         if (!opt) {
490                 set_error(err, lc_opt_err_opt_not_found, "");
491                 return 0;
492         }
493
494         if (!s->cb) {
495                 set_error(err, lc_opt_err_no_callback, "");
496                 return 0;
497         }
498
499         s->is_set = 1;
500
501         switch (s->type) {
502                 case lc_opt_type_int:
503                         if (sscanf(value, "%i", (int *) val)) {
504                                 error = lc_opt_err_unknown_value;
505                                 if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
506                                         error = lc_opt_err_none;
507                         }
508                         break;
509
510                 case lc_opt_type_double:
511                         if (sscanf(value, "%lf", (double *) val)) {
512                                 error = lc_opt_err_unknown_value;
513                                 if (s->cb(opt->name, s->type, s->value, s->length, val->dbl))
514                                         error = lc_opt_err_none;
515                         }
516                         break;
517
518                 case lc_opt_type_boolean:
519                 case lc_opt_type_negboolean:
520                 case lc_opt_type_bit:
521                 case lc_opt_type_negbit:
522                                 strtolower(buf, sizeof(buf), value);
523                                 for (i = 0; i < LC_ARRSIZE(bool_strings); ++i) {
524                                         if (strcmp(buf, bool_strings[i].str) == 0) {
525                                                 val->integer = bool_strings[i].val;
526                                                 error = lc_opt_err_none;
527                                                 break;
528                                         }
529                                 }
530
531                                 if (error == lc_opt_err_none) {
532                                         error = lc_opt_err_unknown_value;
533                                         if (s->cb(opt->name, s->type, s->value, s->length, val->integer))
534                                                 error = lc_opt_err_none;
535                                 }
536
537                         break;
538
539                 case lc_opt_type_string:
540                 case lc_opt_type_enum:
541                         error = lc_opt_err_unknown_value;
542                         if (s->cb(opt->name, s->type, s->value, s->length, value))
543                                 error = lc_opt_err_none;
544                         break;
545                 case lc_opt_type_invalid:
546                         abort();
547         }
548
549         set_error(err, error, value);
550         return error == lc_opt_err_none;
551 }
552
553 char *lc_opt_value_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
554 {
555         const lc_opt_special_t *s = lc_get_opt_special(ent);
556         if (s->dump)
557                 s->dump(buf, len, ent->name, s->type, s->value, s->length);
558         else
559                 strncpy(buf, "<n/a>", len);
560
561         return buf;
562 }
563
564 static char *lc_opt_values_to_string(char *buf, size_t len, const lc_opt_entry_t *ent)
565 {
566         const lc_opt_special_t *s = lc_get_opt_special(ent);
567         if (s->dump_vals)
568                 s->dump_vals(buf, len, ent->name, s->type, s->value, s->length);
569
570         return buf;
571 }
572
573 int lc_opt_add_table(lc_opt_entry_t *root, const lc_opt_table_entry_t *table)
574 {
575         int i, res = 0;
576         lc_opt_err_info_t err;
577
578         for (i = 0; table[i].name != NULL; ++i) {
579                 const char *name;
580                 const lc_opt_table_entry_t *tab = &table[i];
581                 lc_opt_entry_t *grp = resolve_up_to_last_str(root, tab->name, &name);
582
583                 lc_opt_add_opt(grp, name, tab->desc, tab->type, tab->value, tab->len, tab->cb, tab->dump, tab->dump_vals, &err);
584                 if (err.error != lc_opt_err_none)
585                         res = 1;
586         }
587
588         return res;
589 }
590
591 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)
592 {
593         if (ent == stop_ent)
594                 return;
595         if (!lc_opt_grp_is_root(ent)) {
596                 size_t l;
597                 lc_opt_print_grp_path_rec(buf, len, ent->parent, separator, stop_ent);
598                 l = strlen(buf);
599                 if (l > 0 && l < len-1) {
600                         buf[l]     = separator;
601                         buf[l + 1] = '\0';
602                 }
603         }
604
605         strncat(buf, ent->name, len-1);
606 }
607
608 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)
609 {
610         if (len > 0)
611                 buf[0] = '\0';
612         lc_opt_print_grp_path_rec(buf, len, ent, separator, stop_ent);
613         return buf;
614 }
615
616 /**
617  * dump the option tree.
618  * @param ent        starting entity
619  * @param separator  separator char
620  * @param stop_ent   stop at this entity when dumping the name
621  * @param f          output file
622  */
623 static void lc_opt_print_help_rec(lc_opt_entry_t *ent, char separator, lc_opt_entry_t *stop_ent, FILE *f)
624 {
625         lc_grp_special_t *s = lc_get_grp_special(ent);
626         char grp_name[512];
627         char value[256];
628         char values[512];
629         lc_opt_entry_t *e;
630
631         if (!list_empty(&s->opts)) {
632                 lc_opt_print_grp_path(grp_name, sizeof(grp_name), ent, separator, stop_ent);
633                 fputc('\n', f);
634                 if (grp_name[0])
635                         fprintf(f, "%s:\n", grp_name);
636
637                 list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
638                         value[0]  = '\0';
639                         values[0] = '\0';
640                         lc_opt_value_to_string(value, sizeof(value), e);
641                         lc_opt_values_to_string(values, sizeof(values), e);
642                         fprintf(f, HELP_TEMPL_VALS "\n", e->name, lc_opt_get_type_name(e), e->desc, value, values);
643                 }
644         }
645
646         list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
647                 lc_opt_print_help_rec(e, separator, stop_ent, f);
648         }
649
650 }
651
652 void lc_opt_print_help(lc_opt_entry_t *ent, FILE *f)
653 {
654         fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
655         lc_opt_print_help_rec(ent, '.', NULL, f);
656 }
657
658 void lc_opt_print_help_for_entry(lc_opt_entry_t *ent, char separator, FILE *f)
659 {
660         fprintf(f, HELP_TEMPL_VALS "\n", "option", "type", "description", "default", "possible options");
661         lc_opt_print_help_rec(ent, separator, ent, f);
662 }
663
664
665 static void indent(FILE *f, int n)
666 {
667         int i;
668         for (i = 0; i < n; ++i)
669                 fputc(' ', f);
670 }
671
672 static void lc_opt_print_tree_lc_opt_indent(lc_opt_entry_t *ent, FILE *f, int level)
673 {
674         char buf[256];
675         lc_opt_special_t *s = lc_get_opt_special(ent);
676
677         indent(f, level);
678         fprintf(f, "%c%s(\"%s\"):%s = %s\n", s->is_set ? '+' : '-', ent->name,
679                         ent->desc, lc_opt_get_type_name(ent), lc_opt_value_to_string(buf, sizeof(buf), ent));
680 }
681
682 static void lc_opt_print_tree_grp_indent(lc_opt_entry_t *ent, FILE *f, int level)
683 {
684         lc_grp_special_t *s;
685
686         if (ent->is_grp) {
687                 lc_opt_entry_t *e;
688
689                 s = lc_get_grp_special(ent);
690                 indent(f, level);
691                 fprintf(f, "/%s\n", ent->name);
692
693                 list_for_each_entry(lc_opt_entry_t, e, &s->grps, list) {
694                         lc_opt_print_tree_grp_indent(e, f, level + 2);
695                 }
696
697                 list_for_each_entry(lc_opt_entry_t, e, &s->opts, list) {
698                         lc_opt_print_tree_lc_opt_indent(e, f, level + 2);
699                 }
700         }
701 }
702
703 void lc_opt_print_tree(lc_opt_entry_t *ent, FILE *f)
704 {
705         lc_opt_print_tree_grp_indent(ent, f, 0);
706 }
707
708 static int lc_opts_default_error_handler(const char *prefix, const lc_opt_err_info_t *err)
709 {
710         fprintf(stderr, "%s: %s; %s\n", prefix, err->msg, err->arg);
711         return 0;
712 }
713
714 void lc_opt_from_file(const char *filename, FILE *f, lc_opt_error_handler_t *handler)
715 {
716         if (handler == NULL)
717                 handler = lc_opts_default_error_handler;
718         PMANGLE(in) = f;
719         lc_opt_init_parser(filename, handler);
720         PMANGLE(parse)();
721 }
722
723 int lc_opt_from_single_arg(const lc_opt_entry_t *root,
724                                                    const char *opt_prefix,
725                                                    const char *arg, lc_opt_error_handler_t *handler)
726 {
727         const lc_opt_entry_t *grp = root;
728         int n                     = strlen(arg);
729         int n_prefix              = opt_prefix ? strlen(opt_prefix) : 0;
730         int error                 = 0;
731         int ret                   = 0;
732
733         lc_opt_err_info_t err;
734         const char *end, *eqsign;
735
736         if (n >= n_prefix && strncmp(opt_prefix, arg, n_prefix) == 0) {
737                 arg = arg + n_prefix;
738
739                 /*
740                  * check, if the next character is a @.
741                  * This means, that we want to read options
742                  * from a file.
743                  */
744                 if (arg[0] == '@') {
745                         size_t n    = strcspn(&arg[1], " \t\n");
746                         char *fname = (char*)alloca(n + 1);
747                         FILE *f;
748
749                         strncpy(fname, &arg[1], n);
750                         if ((f = fopen(fname, "rt")) != NULL) {
751                                 lc_opt_from_file(fname, f, handler);
752                                 fclose(f);
753                                 set_error(&err, lc_opt_err_none, NULL);
754                         }
755
756                         else
757                                 set_error(&err, lc_opt_err_file_not_found, arg);
758
759                         return !lc_opt_raise_error(&err, handler, "Unable to open file: %s", fname);
760                 }
761
762                 /* find the next delimiter (the -) and extract the string up to
763                  * there. */
764                 end    = strchr(arg, OPT_DELIM);
765                 eqsign = strchr(arg, '=');
766                 if (eqsign && eqsign < end)
767                         end = NULL;
768                 while (end != NULL) {
769                         /*
770                          * Copy the part of the option into the buffer and add the
771                          * finalizing zero.
772                          */
773                         char *buf = (char*)obstack_copy0(&obst, arg, end - arg);
774
775                         /* Resolve the group inside the group */
776                         grp = lc_opt_find_grp(grp, buf, &err);
777                         error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
778                         if (error)
779                                 break;
780
781                         /* Find the next option part delimiter. */
782                         arg = end + 1;
783                         end    = strchr(arg, OPT_DELIM);
784                         eqsign = strchr(arg, '=');
785                         if (eqsign && eqsign < end)
786                                 end = NULL;
787                         obstack_free(&obst, buf);
788                 }
789
790                 if (!error) {
791                         lc_opt_entry_t *opt;
792                         char           *buf;
793
794                         /*
795                          * Now, we are at the last option part:
796                          * --grp1-grp2-...-grpn-opt=value
797                          * Check, for the = and evaluate the option string. If the = is
798                          * missing, we should have a boolean option, but that is checked
799                          * later.
800                          */
801                         end = strchr(arg, '=');
802                         buf = (char*)obstack_copy0(&obst, arg, end ? end - arg : (int) strlen(arg));
803                         opt = lc_opt_find_opt(grp, buf, &err);
804                         error = lc_opt_raise_error(&err, handler, ERR_STRING, arg);
805
806                         if (!error) {
807                                 /*
808                                  * Now evaluate the parameter of the option (the part after
809                                  * the =) if it was given.
810                                  */
811                                 arg = end ? end + 1 : "true";
812
813                                 /* Set the value of the option. */
814                                 lc_opt_occurs(opt, arg, &err);
815                                 ret = !lc_opt_raise_error(&err, handler, ERR_STRING, arg);
816                         }
817                 }
818         }
819
820         return ret;
821 }
822
823 int lc_opt_from_argv(const lc_opt_entry_t *root,
824                                          const char *opt_prefix,
825                                          int argc, const char *argv[],
826                                          lc_opt_error_handler_t *handler)
827 {
828         int i;
829         int options_set = 0;
830
831         if (handler == NULL)
832                 handler = lc_opts_default_error_handler;
833
834         for (i = 0; i < argc; ++i) {
835                 options_set |= lc_opt_from_single_arg(root, opt_prefix, argv[i], handler);
836         }
837
838         return options_set;
839 }
840
841 static int opt_arg_type(UNUSED(const lc_arg_occ_t *occ))
842 {
843         return lc_arg_type_ptr;
844 }
845
846 static int opt_arg_emit(lc_appendable_t *app, const lc_arg_occ_t *occ, const lc_arg_value_t *arg)
847 {
848         char buf[256];
849
850         lc_opt_entry_t *opt = (lc_opt_entry_t*)arg->v_ptr;
851         const char     *s   = buf;
852         size_t          res = 0;
853
854         switch (occ->conversion) {
855         case 'V':
856                 lc_opt_value_to_string(buf, sizeof(buf), opt);
857                 break;
858         case 'T':
859                 s = lc_opt_get_type_name(opt);
860                 break;
861         case 'D':
862                 s = opt->desc;
863                 break;
864         case 'O':
865                 s = opt->name;
866                 break;
867         default:
868                 s = NULL;
869         }
870
871         if (s)
872                 res = lc_appendable_snadd(app, s, strlen(s));
873
874         return res;
875 }
876
877 static const lc_arg_handler_t lc_opt_arg_handler = {
878         opt_arg_type,
879         opt_arg_emit
880 };
881
882
883 /* lc_printf facility for options */
884
885 const lc_arg_env_t *lc_opt_get_arg_env(void)
886 {
887         static lc_arg_env_t *env = NULL;
888
889         if (!env) {
890                 env = lc_arg_new_env();
891
892                 lc_arg_register(env, "opt:value", 'V', &lc_opt_arg_handler);
893                 lc_arg_register(env, "opt:type",  'T', &lc_opt_arg_handler);
894                 lc_arg_register(env, "opt:desc",  'D', &lc_opt_arg_handler);
895                 lc_arg_register(env, "opt:name",  'O', &lc_opt_arg_handler);
896         }
897
898         return env;
899 }
900
901 void lc_opt_default_configs(const char *ini_name)
902 {
903         FILE *f;
904         char path[MAX_PATH];
905         char local_ini_file[MAX_PATH];
906         char home_dir_ini_file[MAX_PATH];
907
908         /* <cmnt>.ini */
909         strncpy(local_ini_file, ini_name, sizeof(local_ini_file));
910         strncat(local_ini_file, ".ini", sizeof(local_ini_file)-1);
911         local_ini_file[sizeof(local_ini_file) - 1] = '\0';
912         path[0] = '\0';
913
914 #ifdef _WIN32
915 #if _MSC_VER > 1200
916         /* ARG: need newer SDK to compile this */
917         SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, path);
918         strncat(path, "\\", sizeof(path)-1);
919 #endif
920         strncpy(home_dir_ini_file, local_ini_file, sizeof(home_dir_ini_file));
921         home_dir_ini_file[sizeof(home_dir_ini_file) - 1] = '\0';
922 #else
923         {
924                 struct passwd *entry = getpwuid(getuid());
925                 if (entry != NULL) {
926                         strcpy(path, entry->pw_dir);
927                         strncat(path, "/", sizeof(path)-1);
928                         /* .<cmnt>rc */
929                         snprintf(home_dir_ini_file, sizeof(home_dir_ini_file), ".%src", ini_name);
930                         home_dir_ini_file[sizeof(home_dir_ini_file) - 1] = '\0';
931                 } else {
932                         /* FIXME: some error occurred */
933                         home_dir_ini_file[0] = '\0';
934                 }
935         }
936 #endif
937
938         strncat(path, home_dir_ini_file, sizeof(path)-1);
939         path[sizeof(path) - 1] = '\0';
940
941         /* Process ini file in user's home. */
942         f = fopen(path, "rt");
943         if (f) {
944                 lc_opt_from_file(path, f, lc_opts_default_error_handler);
945                 fclose(f);
946         }
947
948         /* Process ini file in current directory. */
949         f = fopen(local_ini_file, "rt");
950         if (f) {
951                 lc_opt_from_file(local_ini_file, f, lc_opts_default_error_handler);
952                 fclose(f);
953         }
954 }