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