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