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