1: /*
2: * Copyright (c) 2002-2017 Martin Hedenfalk <martin@bzero.se>
3: *
4: * Permission to use, copy, modify, and/or distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #ifdef HAVE_CONFIG_H
18: # include <config.h>
19: #endif
20:
21: #include <sys/types.h>
22: #include <string.h>
23: #ifdef HAVE_STRINGS_H
24: # include <strings.h>
25: #endif
26: #include <stdlib.h>
27: #include <assert.h>
28: #include <errno.h>
29: #ifndef _WIN32
30: # include <pwd.h>
31: #endif
32: #ifdef HAVE_UNISTD_H
33: # include <unistd.h>
34: #endif
35: #include <ctype.h>
36:
37: #ifdef HAVE_SYS_STAT_H
38: # include <sys/stat.h>
39: # ifndef S_ISREG
40: # define S_ISREG(mode) ((mode) & S_IFREG)
41: # endif
42: #endif
43:
44: #include "compat.h"
45: #include "confuse.h"
46:
47: #define is_set(f, x) (((f) & (x)) == (f))
48:
49: #if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
50: # include <locale.h>
51: # include <libintl.h>
52: # define _(str) dgettext(PACKAGE, str)
53: #else
54: # define _(str) str
55: #endif
56: #define N_(str) str
57:
58: const char confuse_version[] = PACKAGE_VERSION;
59: const char confuse_copyright[] = PACKAGE_STRING " by Martin Hedenfalk <martin@bzero.se>";
60: const char confuse_author[] = "Martin Hedenfalk <martin@bzero.se>";
61:
62: char *cfg_yylval = 0;
63:
64: extern int cfg_yylex(cfg_t *cfg);
65: extern void cfg_yylex_destroy(void);
66: extern int cfg_lexer_include(cfg_t *cfg, const char *fname);
67: extern void cfg_scan_fp_begin(FILE *fp);
68: extern void cfg_scan_fp_end(void);
69:
70: static int cfg_parse_internal(cfg_t *cfg, int level, int force_state, cfg_opt_t *force_opt);
71: static void cfg_free_opt_array(cfg_opt_t *opts);
72: static int cfg_print_pff_indent(cfg_t *cfg, FILE *fp,
73: cfg_print_filter_func_t fb_pff, int indent);
74:
75: #define STATE_CONTINUE 0
76: #define STATE_EOF -1
77: #define STATE_ERROR 1
78:
79: #ifndef HAVE_FMEMOPEN
80: extern FILE *fmemopen(void *buf, size_t size, const char *type);
81: #endif
82:
83: #ifndef HAVE_REALLOCARRAY
84: extern void *reallocarray(void *optr, size_t nmemb, size_t size);
85: #endif
86:
87: #ifndef HAVE_STRDUP
88: /*
89: * Copyright (c) 1988, 1993
90: * The Regents of the University of California. All rights reserved.
91: *
92: * Redistribution and use in source and binary forms, with or without
93: * modification, are permitted provided that the following conditions
94: * are met:
95: * 1. Redistributions of source code must retain the above copyright
96: * notice, this list of conditions and the following disclaimer.
97: * 2. Redistributions in binary form must reproduce the above copyright
98: * notice, this list of conditions and the following disclaimer in the
99: * documentation and/or other materials provided with the distribution.
100: * 3. Neither the name of the University nor the names of its contributors
101: * may be used to endorse or promote products derived from this software
102: * without specific prior written permission.
103: *
104: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
105: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
106: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
107: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
108: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
109: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
110: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
111: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
112: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
113: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
114: * SUCH DAMAGE.
115: */
116: static char *strdup(const char *str)
117: {
118: size_t len;
119: char *dup;
120:
121: len = strlen(str) + 1;
122: dup = calloc(len, sizeof(char));
123: if (!dup)
124: return NULL;
125:
126: memcpy(dup, str, len);
127:
128: return dup;
129: }
130: #endif
131:
132: #ifndef HAVE_STRNDUP
133: static char *strndup(const char *s, size_t n)
134: {
135: char *r;
136:
137: r = malloc(n + 1);
138: if (!r)
139: return NULL;
140:
141: strncpy(r, s, n);
142: r[n] = 0;
143:
144: return r;
145: }
146: #endif
147:
148: #ifndef HAVE_STRCASECMP
149: int strcasecmp(const char *s1, const char *s2)
150: {
151: assert(s1);
152: assert(s2);
153:
154: while (*s1) {
155: int c1 = tolower(*(const unsigned char *)s1);
156: int c2 = tolower(*(const unsigned char *)s2);
157:
158: if (c1 < c2)
159: return -1;
160: if (c1 > c2)
161: return +1;
162:
163: ++s1;
164: ++s2;
165: }
166:
167: if (*s2 != 0)
168: return -1;
169:
170: return 0;
171: }
172: #endif
173:
174: static cfg_opt_t *cfg_getopt_leaf(cfg_t *cfg, const char *name)
175: {
176: unsigned int i;
177:
178: for (i = 0; cfg->opts && cfg->opts[i].name; i++) {
179: if (is_set(CFGF_NOCASE, cfg->flags)) {
180: if (strcasecmp(cfg->opts[i].name, name) == 0)
181: return &cfg->opts[i];
182: } else {
183: if (strcmp(cfg->opts[i].name, name) == 0)
184: return &cfg->opts[i];
185: }
186: }
187:
188: return NULL;
189: }
190:
191: static char *parse_title(const char *name, size_t *len)
192: {
193: const char *escapes = "'\\";
194: char *title;
195: char *end;
196: char *ch;
197:
198: if (*name != '\'') {
199: *len = strcspn(name, "|");
200: if (!*len)
201: return NULL;
202: return strndup(name, *len);
203: }
204:
205: title = strdup(name + 1);
206: if (!title)
207: return NULL;
208:
209: *len = 1;
210: ch = title;
211: end = title + strlen(title);
212: while (ch < end) {
213: size_t l = strcspn(ch, escapes);
214: *len += l + 1;
215: ch += l;
216: switch (*ch) {
217: case '\'':
218: *ch = 0;
219: return title;
220: case '\\':
221: if (!ch[1] || strcspn(ch + 1, escapes)) {
222: free(title);
223: return NULL;
224: }
225: memmove(ch, ch + 1, strlen(ch));
226: ch++;
227: (*len)++;
228: break;
229: default:
230: free(title);
231: return NULL;
232: }
233: }
234:
235: free(title);
236: return NULL;
237: }
238:
239: static long int cfg_opt_gettsecidx(cfg_opt_t *opt, const char *title)
240: {
241: unsigned int i, n;
242:
243: n = cfg_opt_size(opt);
244: for (i = 0; i < n; i++) {
245: cfg_t *sec = cfg_opt_getnsec(opt, i);
246:
247: if (!sec || !sec->title)
248: return -1;
249:
250: if (is_set(CFGF_NOCASE, opt->flags)) {
251: if (strcasecmp(title, sec->title) == 0)
252: return i;
253: } else {
254: if (strcmp(title, sec->title) == 0)
255: return i;
256: }
257: }
258:
259: return -1;
260: }
261:
262: static cfg_opt_t *cfg_getopt_secidx(cfg_t *cfg, const char *name,
263: long int *index)
264: {
265: cfg_opt_t *opt = NULL;
266: cfg_t *sec = cfg;
267:
268: if (!cfg || !cfg->name || !name || !*name) {
269: errno = EINVAL;
270: return NULL;
271: }
272:
273: while (name && *name) {
274: char *title = NULL;
275: long int i = -1;
276: char *secname;
277: size_t len;
278:
279: len = strcspn(name, "|=");
280: if (!index && name[len] == 0 /*len == strlen(name) */ )
281: /* no more subsections */
282: break;
283:
284: if (!len)
285: break;
286:
287: secname = strndup(name, len);
288: if (!secname)
289: return NULL;
290:
291: do {
292: char *endptr;
293:
294: opt = cfg_getopt_leaf(sec, secname);
295: if (!opt || opt->type != CFGT_SEC) {
296: opt = NULL;
297: break;
298: }
299: if (name[len] != '=') {
300: /* non-multi, and backwards compat */
301: i = 0;
302: break;
303: }
304: if (!is_set(CFGF_MULTI, opt->flags))
305: break;
306: name += len + 1;
307: title = parse_title(name, &len);
308: if (!title)
309: break;
310: if (is_set(CFGF_TITLE, opt->flags)) {
311: i = cfg_opt_gettsecidx(opt, title);
312: break;
313: }
314:
315: i = strtol(title, &endptr, 0);
316: if (*endptr != '\0')
317: i = -1;
318: } while(0);
319:
320: if (index)
321: *index = i;
322:
323: sec = i >= 0 ? cfg_opt_getnsec(opt, i) : NULL;
324: if (!sec && !is_set(CFGF_IGNORE_UNKNOWN, cfg->flags)) {
325: if (opt && !is_set(CFGF_MULTI, opt->flags))
326: cfg_error(cfg, _("no such option '%s'"), secname);
327: else if (title)
328: cfg_error(cfg, _("no sub-section '%s' in '%s'"), title, secname);
329: else
330: cfg_error(cfg, _("no sub-section title/index for '%s'"), secname);
331: }
332:
333: free(secname);
334: if (title)
335: free(title);
336: if (!sec)
337: return NULL;
338:
339: name += len;
340: name += strspn(name, "|");
341: }
342:
343: if (!index) {
344: opt = cfg_getopt_leaf(sec, name);
345:
346: if (!opt && !is_set(CFGF_IGNORE_UNKNOWN, cfg->flags))
347: cfg_error(cfg, _("no such option '%s'"), name);
348: }
349:
350: return opt;
351: }
352:
353: DLLIMPORT cfg_opt_t *cfg_getnopt(cfg_t *cfg, unsigned int index)
354: {
355: unsigned int i;
356:
357: if (!cfg)
358: return NULL;
359:
360: for (i = 0; cfg->opts && cfg->opts[i].name; i++) {
361: if (i == index)
362: return &cfg->opts[i];
363: }
364:
365: return NULL;
366: }
367:
368: DLLIMPORT cfg_opt_t *cfg_getopt(cfg_t *cfg, const char *name)
369: {
370: return cfg_getopt_secidx(cfg, name, NULL);
371: }
372:
373: DLLIMPORT const char *cfg_title(cfg_t *cfg)
374: {
375: if (cfg)
376: return cfg->title;
377: return NULL;
378: }
379:
380: DLLIMPORT const char *cfg_name(cfg_t *cfg)
381: {
382: if (cfg)
383: return cfg->name;
384: return NULL;
385: }
386:
387: DLLIMPORT const char *cfg_opt_name(cfg_opt_t *opt)
388: {
389: if (opt)
390: return opt->name;
391: return NULL;
392: }
393:
394: DLLIMPORT const char *cfg_opt_getstr(cfg_opt_t *opt)
395: {
396: return cfg_opt_getnstr(opt, 0);
397: }
398:
399: DLLIMPORT unsigned int cfg_opt_size(cfg_opt_t *opt)
400: {
401: if (opt)
402: return opt->nvalues;
403: return 0;
404: }
405:
406: DLLIMPORT unsigned int cfg_size(cfg_t *cfg, const char *name)
407: {
408: return cfg_opt_size(cfg_getopt(cfg, name));
409: }
410:
411: DLLIMPORT char *cfg_opt_getcomment(cfg_opt_t *opt)
412: {
413: if (opt)
414: return opt->comment;
415:
416: return NULL;
417: }
418:
419: DLLIMPORT char *cfg_getcomment(cfg_t *cfg, const char *name)
420: {
421: return cfg_opt_getcomment(cfg_getopt(cfg, name));
422: }
423:
424: DLLIMPORT signed long cfg_opt_getnint(cfg_opt_t *opt, unsigned int index)
425: {
426: if (!opt || opt->type != CFGT_INT) {
427: errno = EINVAL;
428: return 0;
429: }
430:
431: if (opt->values && index < opt->nvalues)
432: return opt->values[index]->number;
433: if (opt->simple_value.number)
434: return *opt->simple_value.number;
435:
436: return 0;
437: }
438:
439: DLLIMPORT signed long cfg_getnint(cfg_t *cfg, const char *name, unsigned int index)
440: {
441: return cfg_opt_getnint(cfg_getopt(cfg, name), index);
442: }
443:
444: DLLIMPORT signed long cfg_getint(cfg_t *cfg, const char *name)
445: {
446: return cfg_getnint(cfg, name, 0);
447: }
448:
449: DLLIMPORT double cfg_opt_getnfloat(cfg_opt_t *opt, unsigned int index)
450: {
451: if (!opt || opt->type != CFGT_FLOAT) {
452: errno = EINVAL;
453: return 0;
454: }
455:
456: if (opt->values && index < opt->nvalues)
457: return opt->values[index]->fpnumber;
458: if (opt->simple_value.fpnumber)
459: return *opt->simple_value.fpnumber;
460:
461: return 0;
462: }
463:
464: DLLIMPORT double cfg_getnfloat(cfg_t *cfg, const char *name, unsigned int index)
465: {
466: return cfg_opt_getnfloat(cfg_getopt(cfg, name), index);
467: }
468:
469: DLLIMPORT double cfg_getfloat(cfg_t *cfg, const char *name)
470: {
471: return cfg_getnfloat(cfg, name, 0);
472: }
473:
474: DLLIMPORT cfg_bool_t cfg_opt_getnbool(cfg_opt_t *opt, unsigned int index)
475: {
476: if (!opt || opt->type != CFGT_BOOL) {
477: errno = EINVAL;
478: return cfg_false;
479: }
480:
481: if (opt->values && index < opt->nvalues)
482: return opt->values[index]->boolean;
483: if (opt->simple_value.boolean)
484: return *opt->simple_value.boolean;
485:
486: return cfg_false;
487: }
488:
489: DLLIMPORT cfg_bool_t cfg_getnbool(cfg_t *cfg, const char *name, unsigned int index)
490: {
491: return cfg_opt_getnbool(cfg_getopt(cfg, name), index);
492: }
493:
494: DLLIMPORT cfg_bool_t cfg_getbool(cfg_t *cfg, const char *name)
495: {
496: return cfg_getnbool(cfg, name, 0);
497: }
498:
499: DLLIMPORT char *cfg_opt_getnstr(cfg_opt_t *opt, unsigned int index)
500: {
501: if (!opt || opt->type != CFGT_STR) {
502: errno = EINVAL;
503: return NULL;
504: }
505:
506: if (opt->values && index < opt->nvalues)
507: return opt->values[index]->string;
508: if (opt->simple_value.string)
509: return *opt->simple_value.string;
510:
511: return NULL;
512: }
513:
514: DLLIMPORT char *cfg_getnstr(cfg_t *cfg, const char *name, unsigned int index)
515: {
516: return cfg_opt_getnstr(cfg_getopt(cfg, name), index);
517: }
518:
519: DLLIMPORT char *cfg_getstr(cfg_t *cfg, const char *name)
520: {
521: return cfg_getnstr(cfg, name, 0);
522: }
523:
524: DLLIMPORT void *cfg_opt_getnptr(cfg_opt_t *opt, unsigned int index)
525: {
526: if (!opt || opt->type != CFGT_PTR) {
527: errno = EINVAL;
528: return NULL;
529: }
530:
531: if (opt->values && index < opt->nvalues)
532: return opt->values[index]->ptr;
533: if (opt->simple_value.ptr)
534: return *opt->simple_value.ptr;
535:
536: return NULL;
537: }
538:
539: DLLIMPORT void *cfg_getnptr(cfg_t *cfg, const char *name, unsigned int index)
540: {
541: return cfg_opt_getnptr(cfg_getopt(cfg, name), index);
542: }
543:
544: DLLIMPORT void *cfg_getptr(cfg_t *cfg, const char *name)
545: {
546: return cfg_getnptr(cfg, name, 0);
547: }
548:
549: DLLIMPORT cfg_t *cfg_opt_getnsec(cfg_opt_t *opt, unsigned int index)
550: {
551: if (!opt || opt->type != CFGT_SEC) {
552: errno = EINVAL;
553: return NULL;
554: }
555:
556: if (opt->values && index < opt->nvalues)
557: return opt->values[index]->section;
558:
559: errno = ENOENT;
560: return NULL;
561: }
562:
563: DLLIMPORT cfg_t *cfg_getnsec(cfg_t *cfg, const char *name, unsigned int index)
564: {
565: return cfg_opt_getnsec(cfg_getopt(cfg, name), index);
566: }
567:
568: DLLIMPORT cfg_t *cfg_opt_gettsec(cfg_opt_t *opt, const char *title)
569: {
570: long int i;
571:
572: if (!opt || !title) {
573: errno = EINVAL;
574: return NULL;
575: }
576:
577: if (!is_set(CFGF_TITLE, opt->flags)) {
578: errno = EINVAL;
579: return NULL;
580: }
581:
582: i = cfg_opt_gettsecidx(opt, title);
583: if (i >= 0)
584: return cfg_opt_getnsec(opt, i);
585:
586: errno = ENOENT;
587: return NULL;
588: }
589:
590: DLLIMPORT cfg_t *cfg_gettsec(cfg_t *cfg, const char *name, const char *title)
591: {
592: return cfg_opt_gettsec(cfg_getopt(cfg, name), title);
593: }
594:
595: DLLIMPORT cfg_t *cfg_getsec(cfg_t *cfg, const char *name)
596: {
597: cfg_opt_t *opt;
598: long int index;
599:
600: opt = cfg_getopt_secidx(cfg, name, &index);
601: return cfg_opt_getnsec(opt, index);
602: }
603:
604: static cfg_value_t *cfg_addval(cfg_opt_t *opt)
605: {
606: void *ptr;
607:
608: ptr = realloc(opt->values, (opt->nvalues + 1) * sizeof(cfg_value_t *));
609: if (!ptr)
610: return NULL;
611:
612: opt->values = ptr;
613: opt->values[opt->nvalues] = calloc(1, sizeof(cfg_value_t));
614: if (!opt->values[opt->nvalues])
615: return NULL;
616:
617: opt->flags |= CFGF_MODIFIED;
618:
619: return opt->values[opt->nvalues++];
620: }
621:
622: static cfg_opt_t *cfg_addopt(cfg_t *cfg, char *key)
623: {
624: int num = cfg_num(cfg);
625: cfg_opt_t *opts;
626:
627: opts = reallocarray(cfg->opts, num + 2, sizeof(cfg_opt_t));
628: if (!opts)
629: return NULL;
630:
631: /* Write new opt to previous CFG_END() marker */
632: cfg->opts = opts;
633: cfg->opts[num].name = strdup(key);
634: cfg->opts[num].type = CFGT_STR;
635:
636: if (!cfg->opts[num].name) {
637: free(opts);
638: return NULL;
639: }
640:
641: /* Set new CFG_END() */
642: memset(&cfg->opts[num + 1], 0, sizeof(cfg_opt_t));
643:
644: return &cfg->opts[num];
645: }
646:
647: DLLIMPORT int cfg_numopts(cfg_opt_t *opts)
648: {
649: int n;
650:
651: for (n = 0; opts && opts[n].name; n++)
652: /* do nothing */ ;
653: return n;
654: }
655:
656: DLLIMPORT unsigned int cfg_num(cfg_t *cfg)
657: {
658: if (!cfg)
659: return 0;
660:
661: return (unsigned int)cfg_numopts(cfg->opts);
662: }
663:
664: static cfg_opt_t *cfg_dupopt_array(cfg_opt_t *opts)
665: {
666: int i;
667: cfg_opt_t *dupopts;
668: int n = cfg_numopts(opts);
669:
670: dupopts = calloc(n + 1, sizeof(cfg_opt_t));
671: if (!dupopts)
672: return NULL;
673:
674: memcpy(dupopts, opts, n * sizeof(cfg_opt_t));
675:
676: for (i = 0; i < n; i++) {
677: /* Clear dynamic ptrs, protecting the original on failure */
678: dupopts[i].name = NULL;
679: dupopts[i].subopts = NULL;
680: dupopts[i].def.parsed = NULL;
681: dupopts[i].def.string = NULL;
682: dupopts[i].comment = NULL;
683: }
684:
685: for (i = 0; i < n; i++) {
686: dupopts[i].name = strdup(opts[i].name);
687: if (!dupopts[i].name)
688: goto err;
689:
690: if (opts[i].subopts) {
691: dupopts[i].subopts = cfg_dupopt_array(opts[i].subopts);
692: if (!dupopts[i].subopts)
693: goto err;
694: }
695:
696: if (opts[i].def.parsed) {
697: dupopts[i].def.parsed = strdup(opts[i].def.parsed);
698: if (!dupopts[i].def.parsed)
699: goto err;
700: }
701:
702: if (opts[i].def.string) {
703: dupopts[i].def.string = strdup(opts[i].def.string);
704: if (!dupopts[i].def.string)
705: goto err;
706: }
707:
708: if (opts[i].comment) {
709: dupopts[i].comment = strdup(opts[i].comment);
710: if (!dupopts[i].comment)
711: goto err;
712: }
713: }
714:
715: return dupopts;
716: err:
717: cfg_free_opt_array(dupopts);
718: return NULL;
719: }
720:
721: DLLIMPORT int cfg_parse_boolean(const char *s)
722: {
723: if (!s) {
724: errno = EINVAL;
725: return CFG_FAIL;
726: }
727:
728: if (strcasecmp(s, "true") == 0 || strcasecmp(s, "on") == 0 || strcasecmp(s, "yes") == 0)
729: return 1;
730: if (strcasecmp(s, "false") == 0 || strcasecmp(s, "off") == 0 || strcasecmp(s, "no") == 0)
731: return 0;
732:
733: return CFG_FAIL;
734: }
735:
736: static void cfg_init_defaults(cfg_t *cfg)
737: {
738: int i;
739:
740: for (i = 0; cfg->opts && cfg->opts[i].name; i++) {
741: int j;
742:
743: for (j = 0; j < i; ++j) {
744: if (is_set(CFGF_NOCASE, cfg->opts[i].flags | cfg->opts[j].flags)) {
745: if (strcasecmp(cfg->opts[i].name, cfg->opts[j].name))
746: continue;
747: } else {
748: if (strcmp(cfg->opts[i].name, cfg->opts[j].name))
749: continue;
750: }
751: /*
752: * There are two definitions of the same option name.
753: * What to do? It's a programming error and not an end
754: * user input error. Lets print a message and abort...
755: */
756: cfg_error(cfg, _("duplicate option '%s' not allowed"),
757: cfg->opts[i].name);
758: break;
759: }
760:
761: /* libConfuse doesn't handle default values for "simple" options */
762: if (cfg->opts[i].simple_value.ptr || is_set(CFGF_NODEFAULT, cfg->opts[i].flags))
763: continue;
764:
765: if (cfg->opts[i].type != CFGT_SEC) {
766: cfg->opts[i].flags |= CFGF_DEFINIT;
767:
768: if (is_set(CFGF_LIST, cfg->opts[i].flags) || cfg->opts[i].def.parsed) {
769: int xstate, ret = 0;
770: char *buf;
771: FILE *fp;
772:
773: /* If it's a list, but no default value was given,
774: * keep the option uninitialized.
775: */
776: buf = cfg->opts[i].def.parsed;
777: if (!buf || !buf[0])
778: continue;
779:
780: /* setup scanning from the string specified for the
781: * "default" value, force the correct state and option
782: */
783:
784: if (is_set(CFGF_LIST, cfg->opts[i].flags))
785: /* lists must be surrounded by {braces} */
786: xstate = 3;
787: else if (cfg->opts[i].type == CFGT_FUNC)
788: xstate = 0;
789: else
790: xstate = 2;
791:
792: fp = fmemopen(buf, strlen(buf), "r");
793: if (!fp) {
794: /*
795: * fmemopen() on older GLIBC versions do not accept zero
796: * length buffers for some reason. This is a workaround.
797: */
798: if (strlen(buf) > 0)
799: ret = STATE_ERROR;
800: } else {
801: cfg_scan_fp_begin(fp);
802:
803: do {
804: ret = cfg_parse_internal(cfg, 1, xstate, &cfg->opts[i]);
805: xstate = -1;
806: } while (ret == STATE_CONTINUE);
807:
808: cfg_scan_fp_end();
809: fclose(fp);
810: }
811:
812: if (ret == STATE_ERROR) {
813: /*
814: * If there was an error parsing the default string,
815: * the initialization of the default value could be
816: * inconsistent or empty. What to do? It's a
817: * programming error and not an end user input
818: * error. Lets print a message and abort...
819: */
820: fprintf(stderr, "Parse error in default value '%s'"
821: " for option '%s'\n", cfg->opts[i].def.parsed, cfg->opts[i].name);
822: fprintf(stderr, "Check your initialization macros and the" " libConfuse documentation\n");
823: abort();
824: }
825: } else {
826: switch (cfg->opts[i].type) {
827: case CFGT_INT:
828: cfg_opt_setnint(&cfg->opts[i], cfg->opts[i].def.number, 0);
829: break;
830:
831: case CFGT_FLOAT:
832: cfg_opt_setnfloat(&cfg->opts[i], cfg->opts[i].def.fpnumber, 0);
833: break;
834:
835: case CFGT_BOOL:
836: cfg_opt_setnbool(&cfg->opts[i], cfg->opts[i].def.boolean, 0);
837: break;
838:
839: case CFGT_STR:
840: cfg_opt_setnstr(&cfg->opts[i], cfg->opts[i].def.string, 0);
841: break;
842:
843: case CFGT_FUNC:
844: case CFGT_PTR:
845: break;
846:
847: default:
848: cfg_error(cfg, "internal error in cfg_init_defaults(%s)", cfg->opts[i].name);
849: break;
850: }
851: }
852:
853: /* The default value should only be returned if no value
854: * is given in the configuration file, so we set the RESET
855: * flag here. When/If cfg_setopt() is called, the value(s)
856: * will be freed and the flag unset.
857: */
858: cfg->opts[i].flags |= CFGF_RESET;
859: cfg->opts[i].flags &= ~CFGF_MODIFIED;
860: } else if (!is_set(CFGF_MULTI, cfg->opts[i].flags)) {
861: cfg_setopt(cfg, &cfg->opts[i], 0);
862: cfg->opts[i].flags |= CFGF_DEFINIT;
863: }
864: }
865: }
866:
867: DLLIMPORT cfg_value_t *cfg_setopt(cfg_t *cfg, cfg_opt_t *opt, const char *value)
868: {
869: cfg_value_t *val = NULL;
870: int b;
871: const char *s;
872: double f;
873: long int i;
874: void *p;
875: char *endptr;
876:
877: if (!cfg || !opt) {
878: errno = EINVAL;
879: return NULL;
880: }
881:
882: if (opt->simple_value.ptr) {
883: if (opt->type == CFGT_SEC) {
884: errno = EINVAL;
885: return NULL;
886: }
887: val = (cfg_value_t *)opt->simple_value.ptr;
888: } else {
889: if (is_set(CFGF_RESET, opt->flags)) {
890: cfg_free_value(opt);
891: opt->flags &= ~CFGF_RESET;
892: }
893:
894: if (opt->nvalues == 0 || is_set(CFGF_MULTI, opt->flags) || is_set(CFGF_LIST, opt->flags)) {
895: val = NULL;
896:
897: if (opt->type == CFGT_SEC && is_set(CFGF_TITLE, opt->flags)) {
898: unsigned int i;
899:
900: /* XXX: Check if there already is a section with the same title. */
901:
902: /*
903: * Check there are either no sections at
904: * all, or a non-NULL section title.
905: */
906: if (opt->nvalues != 0 && !value) {
907: errno = EINVAL;
908: return NULL;
909: }
910:
911: for (i = 0; i < opt->nvalues && val == NULL; i++) {
912: cfg_t *sec = opt->values[i]->section;
913:
914: if (is_set(CFGF_NOCASE, cfg->flags)) {
915: if (strcasecmp(value, sec->title) == 0)
916: val = opt->values[i];
917: } else {
918: if (strcmp(value, sec->title) == 0)
919: val = opt->values[i];
920: }
921: }
922:
923: if (val && is_set(CFGF_NO_TITLE_DUPES, opt->flags)) {
924: cfg_error(cfg, _("found duplicate title '%s'"), value);
925: return NULL;
926: }
927: }
928:
929: if (!val) {
930: val = cfg_addval(opt);
931: if (!val)
932: return NULL;
933: }
934: } else {
935: val = opt->values[0];
936: }
937: }
938:
939: switch (opt->type) {
940: case CFGT_INT:
941: if (opt->parsecb) {
942: if ((*opt->parsecb) (cfg, opt, value, &i) != 0)
943: return NULL;
944: } else {
945: if (!value) {
946: errno = EINVAL;
947: return NULL;
948: }
949: i = strtol(value, &endptr, 0);
950: if (*endptr != '\0') {
951: cfg_error(cfg, _("invalid integer value for option '%s'"), opt->name);
952: return NULL;
953: }
954: if (errno == ERANGE) {
955: cfg_error(cfg, _("integer value for option '%s' is out of range"), opt->name);
956: return NULL;
957: }
958: }
959: val->number = i;
960: break;
961:
962: case CFGT_FLOAT:
963: if (opt->parsecb) {
964: if ((*opt->parsecb) (cfg, opt, value, &f) != 0)
965: return NULL;
966: } else {
967: if (!value) {
968: errno = EINVAL;
969: return NULL;
970: }
971: f = strtod(value, &endptr);
972: if (*endptr != '\0') {
973: cfg_error(cfg, _("invalid floating point value for option '%s'"), opt->name);
974: return NULL;
975: }
976: if (errno == ERANGE) {
977: cfg_error(cfg, _("floating point value for option '%s' is out of range"), opt->name);
978: return NULL;
979: }
980: }
981: val->fpnumber = f;
982: break;
983:
984: case CFGT_STR:
985: if (opt->parsecb) {
986: s = NULL;
987: if ((*opt->parsecb) (cfg, opt, value, &s) != 0)
988: return NULL;
989: } else {
990: s = value;
991: }
992:
993: if (!s) {
994: errno = EINVAL;
995: return NULL;
996: }
997:
998: free(val->string);
999: val->string = strdup(s);
1000: if (!val->string)
1001: return NULL;
1002: break;
1003:
1004: case CFGT_SEC:
1005: if (is_set(CFGF_MULTI, opt->flags) || val->section == 0) {
1006: if (val->section) {
1007: val->section->path = NULL; /* Global search path */
1008: cfg_free(val->section);
1009: }
1010: val->section = calloc(1, sizeof(cfg_t));
1011: if (!val->section)
1012: return NULL;
1013:
1014: val->section->name = strdup(opt->name);
1015: if (!val->section->name) {
1016: free(val->section);
1017: return NULL;
1018: }
1019:
1020: val->section->flags = cfg->flags;
1021: if (is_set(CFGF_KEYSTRVAL, opt->flags))
1022: val->section->flags |= CFGF_KEYSTRVAL;
1023:
1024: val->section->filename = cfg->filename ? strdup(cfg->filename) : NULL;
1025: if (cfg->filename && !val->section->filename) {
1026: free(val->section->name);
1027: free(val->section);
1028: return NULL;
1029: }
1030:
1031: val->section->line = cfg->line;
1032: val->section->errfunc = cfg->errfunc;
1033: val->section->title = value ? strdup(value) : NULL;
1034: if (value && !val->section->title) {
1035: free(val->section->filename);
1036: free(val->section->name);
1037: free(val->section);
1038: return NULL;
1039: }
1040:
1041: val->section->opts = cfg_dupopt_array(opt->subopts);
1042: if (!val->section->opts) {
1043: if (val->section->title)
1044: free(val->section->title);
1045: if (val->section->filename)
1046: free(val->section->filename);
1047: free(val->section->name);
1048: free(val->section);
1049: return NULL;
1050: }
1051: }
1052: if (!is_set(CFGF_DEFINIT, opt->flags))
1053: cfg_init_defaults(val->section);
1054: break;
1055:
1056: case CFGT_BOOL:
1057: if (opt->parsecb) {
1058: if ((*opt->parsecb) (cfg, opt, value, &b) != 0)
1059: return NULL;
1060: } else {
1061: b = cfg_parse_boolean(value);
1062: if (b == -1) {
1063: cfg_error(cfg, _("invalid boolean value for option '%s'"), opt->name);
1064: return NULL;
1065: }
1066: }
1067: val->boolean = (cfg_bool_t)b;
1068: break;
1069:
1070: case CFGT_PTR:
1071: if (!opt->parsecb) {
1072: errno = EINVAL;
1073: return NULL;
1074: }
1075:
1076: if ((*opt->parsecb) (cfg, opt, value, &p) != 0)
1077: return NULL;
1078: if (val->ptr && opt->freecb)
1079: opt->freecb(val->ptr);
1080: val->ptr = p;
1081: break;
1082:
1083: default:
1084: cfg_error(cfg, "internal error in cfg_setopt(%s, %s)", opt->name, (value) ? (value) : "NULL");
1085: return NULL;
1086: }
1087:
1088: opt->flags |= CFGF_MODIFIED;
1089:
1090: return val;
1091: }
1092:
1093: DLLIMPORT int cfg_opt_setmulti(cfg_t *cfg, cfg_opt_t *opt, unsigned int nvalues, char **values)
1094: {
1095: cfg_opt_t old;
1096: unsigned int i;
1097:
1098: if (!opt || !nvalues) {
1099: errno = EINVAL;
1100: return CFG_FAIL;
1101: }
1102:
1103: old = *opt;
1104: opt->nvalues = 0;
1105: opt->values = 0;
1106:
1107: for (i = 0; i < nvalues; i++) {
1108: if (cfg_setopt(cfg, opt, values[i]))
1109: continue;
1110:
1111: /* ouch, revert */
1112: cfg_free_value(opt);
1113: opt->nvalues = old.nvalues;
1114: opt->values = old.values;
1115: opt->flags &= ~(CFGF_RESET | CFGF_MODIFIED);
1116: opt->flags |= old.flags & (CFGF_RESET | CFGF_MODIFIED);
1117:
1118: return CFG_FAIL;
1119: }
1120:
1121: cfg_free_value(&old);
1122: opt->flags |= CFGF_MODIFIED;
1123:
1124: return CFG_SUCCESS;
1125: }
1126:
1127: DLLIMPORT int cfg_setmulti(cfg_t *cfg, const char *name, unsigned int nvalues, char **values)
1128: {
1129: cfg_opt_t *opt;
1130:
1131: if (!cfg || !name || !values) {
1132: errno = EINVAL;
1133: return CFG_FAIL;
1134: }
1135:
1136: opt = cfg_getopt(cfg, name);
1137: if (!opt) {
1138: errno = ENOENT;
1139: return CFG_FAIL;
1140: }
1141:
1142: return cfg_opt_setmulti(cfg, opt, nvalues, values);
1143: }
1144:
1145: /* searchpath */
1146:
1147: struct cfg_searchpath_t {
1148: char *dir; /**< directory to search */
1149: cfg_searchpath_t *next; /**< next in list */
1150: };
1151:
1152: /* prepend a new cfg_searchpath_t to the linked list */
1153:
1154: DLLIMPORT int cfg_add_searchpath(cfg_t *cfg, const char *dir)
1155: {
1156: cfg_searchpath_t *p;
1157: char *d;
1158:
1159: if (!cfg || !dir) {
1160: errno = EINVAL;
1161: return CFG_FAIL;
1162: }
1163:
1164: d = cfg_tilde_expand(dir);
1165: if (!d)
1166: return CFG_FAIL;
1167:
1168: p = malloc(sizeof(cfg_searchpath_t));
1169: if (!p) {
1170: free(d);
1171: return CFG_FAIL;
1172: }
1173:
1174: p->next = cfg->path;
1175: p->dir = d;
1176: cfg->path = p;
1177:
1178: return CFG_SUCCESS;
1179: }
1180:
1181: DLLIMPORT cfg_errfunc_t cfg_set_error_function(cfg_t *cfg, cfg_errfunc_t errfunc)
1182: {
1183: cfg_errfunc_t old;
1184:
1185: if (!cfg) {
1186: errno = EINVAL;
1187: return NULL;
1188: }
1189:
1190: old = cfg->errfunc;
1191: cfg->errfunc = errfunc;
1192:
1193: return old;
1194: }
1195:
1196: DLLIMPORT cfg_print_filter_func_t cfg_set_print_filter_func(cfg_t *cfg, cfg_print_filter_func_t pff)
1197: {
1198: cfg_print_filter_func_t old;
1199:
1200: if (!cfg) {
1201: errno = EINVAL;
1202: return NULL;
1203: }
1204:
1205: old = cfg->pff;
1206: cfg->pff = pff;
1207:
1208: return old;
1209: }
1210:
1211: DLLIMPORT void cfg_error(cfg_t *cfg, const char *fmt, ...)
1212: {
1213: va_list ap;
1214:
1215: va_start(ap, fmt);
1216:
1217: if (cfg && cfg->errfunc)
1218: (*cfg->errfunc) (cfg, fmt, ap);
1219: else {
1220: if (cfg && cfg->filename && cfg->line)
1221: fprintf(stderr, "%s:%d: ", cfg->filename, cfg->line);
1222: else if (cfg && cfg->filename)
1223: fprintf(stderr, "%s: ", cfg->filename);
1224: vfprintf(stderr, fmt, ap);
1225: fprintf(stderr, "\n");
1226: }
1227:
1228: va_end(ap);
1229: }
1230:
1231: static int call_function(cfg_t *cfg, cfg_opt_t *opt, cfg_opt_t *funcopt)
1232: {
1233: int ret;
1234: const char **argv;
1235: unsigned int i;
1236:
1237: if (!cfg || !opt ||!funcopt) {
1238: errno = EINVAL;
1239: return CFG_FAIL;
1240: }
1241:
1242: /*
1243: * create am argv string vector and call the registered function
1244: */
1245: argv = calloc(funcopt->nvalues, sizeof(char *));
1246: if (!argv)
1247: return CFG_FAIL;
1248:
1249: for (i = 0; i < funcopt->nvalues; i++)
1250: argv[i] = funcopt->values[i]->string;
1251:
1252: ret = (*opt->func) (cfg, opt, funcopt->nvalues, argv);
1253: cfg_free_value(funcopt);
1254: free(argv);
1255:
1256: return ret;
1257: }
1258:
1259: static void cfg_handle_deprecated(cfg_t *cfg, cfg_opt_t *opt)
1260: {
1261: if (is_set(CFGF_DROP, opt->flags)) {
1262: cfg_error(cfg, _("dropping deprecated configuration option '%s'"), opt->name);
1263: cfg_free_value(opt);
1264: } else {
1265: cfg_error(cfg, _("found deprecated option '%s', please update configuration file."), opt->name);
1266: }
1267: }
1268:
1269: static int cfg_parse_internal(cfg_t *cfg, int level, int force_state, cfg_opt_t *force_opt)
1270: {
1271: int state = 0;
1272: char *comment = NULL;
1273: char *opttitle = NULL;
1274: cfg_opt_t *opt = NULL;
1275: cfg_value_t *val = NULL;
1276: cfg_opt_t funcopt = CFG_STR(0, 0, 0);
1277: int ignore = 0; /* ignore until this token, traverse parser w/o error */
1278: int num_values = 0; /* number of values found for a list option */
1279: int rc;
1280:
1281: if (force_state != -1)
1282: state = force_state;
1283: if (force_opt)
1284: opt = force_opt;
1285:
1286: while (1) {
1287: int tok = cfg_yylex(cfg);
1288:
1289: if (tok == 0) {
1290: /* lexer.l should have called cfg_error() */
1291: goto error;
1292: }
1293:
1294: if (tok == EOF) {
1295: if (state != 0) {
1296: cfg_error(cfg, _("premature end of file"));
1297: goto error;
1298: }
1299:
1300: if (opt && is_set(CFGF_DEPRECATED, opt->flags))
1301: cfg_handle_deprecated(cfg, opt);
1302:
1303: if (comment)
1304: free(comment);
1305:
1306: return STATE_EOF;
1307: }
1308:
1309: switch (state) {
1310: case 0: /* expecting an option name */
1311: if (opt && is_set(CFGF_DEPRECATED, opt->flags))
1312: cfg_handle_deprecated(cfg, opt);
1313:
1314: switch (tok) {
1315: case '}':
1316: if (level == 0) {
1317: cfg_error(cfg, _("unexpected closing brace"));
1318: goto error;
1319: }
1320: if (comment)
1321: free(comment);
1322:
1323: return STATE_EOF;
1324:
1325: case CFGT_STR:
1326: break;
1327:
1328: case CFGT_COMMENT:
1329: if (!is_set(CFGF_COMMENTS, cfg->flags))
1330: continue;
1331:
1332: if (comment)
1333: free(comment);
1334: comment = strdup(cfg_yylval);
1335: continue;
1336:
1337: default:
1338: cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
1339: goto error;
1340: }
1341:
1342: opt = cfg_getopt(cfg, cfg_yylval);
1343: if (!opt) {
1344: if (is_set(CFGF_IGNORE_UNKNOWN, cfg->flags)) {
1345: state = 10;
1346: break;
1347: }
1348:
1349: /* Not found, is it a dynamic key-value section? */
1350: if (is_set(CFGF_KEYSTRVAL, cfg->flags)) {
1351: opt = cfg_addopt(cfg, cfg_yylval);
1352: if (!opt)
1353: goto error;
1354:
1355: state = 1;
1356: break;
1357: }
1358:
1359: goto error;
1360: }
1361:
1362: if (opt->type == CFGT_SEC) {
1363: if (is_set(CFGF_TITLE, opt->flags))
1364: state = 6;
1365: else
1366: state = 5;
1367: } else if (opt->type == CFGT_FUNC) {
1368: state = 7;
1369: } else {
1370: state = 1;
1371: }
1372: break;
1373:
1374: case 1: /* expecting an equal sign or plus-equal sign */
1375: if (!opt)
1376: goto error;
1377:
1378: if (tok == '+') {
1379: if (!is_set(CFGF_LIST, opt->flags)) {
1380: cfg_error(cfg, _("attempt to append to non-list option '%s'"), opt->name);
1381: goto error;
1382: }
1383: /* Even if the reset flag was set by
1384: * cfg_init_defaults, appending to the defaults
1385: * should be ok.
1386: */
1387: opt->flags &= ~CFGF_RESET;
1388: } else if (tok == '=') {
1389: /* set the (temporary) reset flag to clear the old
1390: * values, since we obviously didn't want to append */
1391: opt->flags |= CFGF_RESET;
1392: } else {
1393: cfg_error(cfg, _("missing equal sign after option '%s'"), opt->name);
1394: goto error;
1395: }
1396:
1397: opt->flags |= CFGF_MODIFIED;
1398:
1399: if (is_set(CFGF_LIST, opt->flags)) {
1400: state = 3;
1401: num_values = 0;
1402: } else {
1403: state = 2;
1404: }
1405: break;
1406:
1407: case 2: /* expecting an option value */
1408: if (tok == '}' && opt && is_set(CFGF_LIST, opt->flags)) {
1409: state = 0;
1410: if (num_values == 0 && is_set(CFGF_RESET, opt->flags))
1411: /* Reset flags was set, and the empty list was
1412: * specified. Free all old values. */
1413: cfg_free_value(opt);
1414: break;
1415: }
1416:
1417: if (tok != CFGT_STR) {
1418: cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
1419: goto error;
1420: }
1421:
1422: if (cfg_setopt(cfg, opt, cfg_yylval) == 0)
1423: goto error;
1424:
1425: if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
1426: goto error;
1427:
1428: /* Inherit last read comment */
1429: cfg_opt_setcomment(opt, comment);
1430: if (comment)
1431: free(comment);
1432: comment = NULL;
1433:
1434: if (opt && is_set(CFGF_LIST, opt->flags)) {
1435: ++num_values;
1436: state = 4;
1437: } else {
1438: state = 0;
1439: }
1440: break;
1441:
1442: case 3: /* expecting an opening brace for a list option */
1443: if (tok != '{') {
1444: if (tok != CFGT_STR) {
1445: cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
1446: goto error;
1447: }
1448:
1449: if (cfg_setopt(cfg, opt, cfg_yylval) == 0)
1450: goto error;
1451: if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
1452: goto error;
1453: ++num_values;
1454: state = 0;
1455: } else {
1456: state = 2;
1457: }
1458: break;
1459:
1460: case 4: /* expecting a separator for a list option, or closing (list) brace */
1461: if (tok == ',') {
1462: state = 2;
1463: } else if (tok == '}') {
1464: state = 0;
1465: if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
1466: goto error;
1467: } else {
1468: cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
1469: goto error;
1470: }
1471: break;
1472:
1473: case 5: /* expecting an opening brace for a section */
1474: if (tok != '{') {
1475: cfg_error(cfg, _("missing opening brace for section '%s'"), opt ? opt->name : "");
1476: goto error;
1477: }
1478:
1479: val = cfg_setopt(cfg, opt, opttitle);
1480: if (!val)
1481: goto error;
1482:
1483: if (opttitle)
1484: free(opttitle);
1485: opttitle = NULL;
1486:
1487: val->section->path = cfg->path; /* Remember global search path */
1488: val->section->line = cfg->line;
1489: val->section->errfunc = cfg->errfunc;
1490: rc = cfg_parse_internal(val->section, level + 1, -1, 0);
1491: if (rc != STATE_EOF)
1492: goto error;
1493:
1494: cfg->line = val->section->line;
1495: if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
1496: goto error;
1497: state = 0;
1498: break;
1499:
1500: case 6: /* expecting a title for a section */
1501: if (tok != CFGT_STR) {
1502: cfg_error(cfg, _("missing title for section '%s'"), opt ? opt->name : "");
1503: goto error;
1504: } else {
1505: opttitle = strdup(cfg_yylval);
1506: if (!opttitle)
1507: goto error;
1508: }
1509: state = 5;
1510: break;
1511:
1512: case 7: /* expecting an opening parenthesis for a function */
1513: if (tok != '(') {
1514: cfg_error(cfg, _("missing parenthesis for function '%s'"), opt ? opt->name : "");
1515: goto error;
1516: }
1517: state = 8;
1518: break;
1519:
1520: case 8: /* expecting a function parameter or a closing paren */
1521: if (tok == ')') {
1522: if (call_function(cfg, opt, &funcopt))
1523: goto error;
1524: state = 0;
1525: } else if (tok == CFGT_STR) {
1526: val = cfg_addval(&funcopt);
1527: if (!val)
1528: goto error;
1529:
1530: val->string = strdup(cfg_yylval);
1531: if (!val->string)
1532: goto error;
1533:
1534: state = 9;
1535: } else {
1536: cfg_error(cfg, _("syntax error in call of function '%s'"), opt ? opt->name : "");
1537: goto error;
1538: }
1539: break;
1540:
1541: case 9: /* expecting a comma in a function or a closing paren */
1542: if (tok == ')') {
1543: if (call_function(cfg, opt, &funcopt))
1544: goto error;
1545: state = 0;
1546: } else if (tok == ',') {
1547: state = 8;
1548: } else {
1549: cfg_error(cfg, _("syntax error in call of function '%s'"), opt ? opt->name : "");
1550: goto error;
1551: }
1552: break;
1553:
1554: case 10: /* unknown option, mini-discard parser states: 10-15 */
1555: if (comment) {
1556: free(comment);
1557: comment = NULL;
1558: }
1559:
1560: if (tok == '+') {
1561: ignore = '=';
1562: state = 13; /* Append to list, should be followed by '=' */
1563: } else if (tok == '=') {
1564: ignore = 0;
1565: state = 14; /* Assignment, regular handling */
1566: } else if (tok == '(') {
1567: ignore = ')';
1568: state = 13; /* Function, ignore until end of param list */
1569: } else if (tok == '{') {
1570: state = 12; /* Section, ignore all until closing brace */
1571: } else if (tok == CFGT_STR) {
1572: state = 11; /* No '=' ... must be a titled section */
1573: } else if (tok == '}' && force_state == 10) {
1574: if (comment)
1575: free(comment);
1576:
1577: return STATE_CONTINUE;
1578: }
1579: break;
1580:
1581: case 11: /* unknown option, expecting start of title section */
1582: if (tok != '{') {
1583: cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
1584: goto error;
1585: }
1586: state = 12;
1587: break;
1588:
1589: case 12: /* unknown option, recursively ignore entire sub-section */
1590: rc = cfg_parse_internal(cfg, level + 1, 10, NULL);
1591: if (rc != STATE_CONTINUE)
1592: goto error;
1593: ignore = '}';
1594: state = 13;
1595: break;
1596:
1597: case 13: /* unknown option, consume tokens silently until end of func/list */
1598: if (tok != ignore)
1599: break;
1600:
1601: if (ignore == '=') {
1602: ignore = 0;
1603: state = 14;
1604: break;
1605: }
1606:
1607: /* Are we done with recursive ignore of sub-section? */
1608: if (force_state == 10) {
1609: if (comment)
1610: free(comment);
1611:
1612: return STATE_CONTINUE;
1613: }
1614:
1615: ignore = 0;
1616: state = 0;
1617: break;
1618:
1619: case 14: /* unknown option, assuming value or start of list */
1620: if (tok == '{') {
1621: ignore = '}';
1622: state = 13;
1623: break;
1624: }
1625:
1626: if (tok != CFGT_STR) {
1627: cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
1628: goto error;
1629: }
1630:
1631: ignore = 0;
1632: if (force_state == 10)
1633: state = 15;
1634: else
1635: state = 0;
1636: break;
1637:
1638: case 15: /* unknown option, dummy read of next parameter in sub-section */
1639: state = 10;
1640: break;
1641:
1642: default:
1643: cfg_error(cfg, _("Internal error in cfg_parse_internal(), unknown state %d"), state);
1644: goto error;
1645: }
1646: }
1647:
1648: if (comment)
1649: free(comment);
1650:
1651: return STATE_EOF;
1652:
1653: error:
1654: if (opttitle)
1655: free(opttitle);
1656: if (comment)
1657: free(comment);
1658:
1659: return STATE_ERROR;
1660: }
1661:
1662: DLLIMPORT int cfg_parse_fp(cfg_t *cfg, FILE *fp)
1663: {
1664: int ret;
1665:
1666: if (!cfg || !fp) {
1667: errno = EINVAL;
1668: return CFG_PARSE_ERROR;
1669: }
1670:
1671: if (!cfg->filename)
1672: cfg->filename = strdup("FILE");
1673: if (!cfg->filename)
1674: return CFG_PARSE_ERROR;
1675:
1676: cfg->line = 1;
1677: cfg_scan_fp_begin(fp);
1678: ret = cfg_parse_internal(cfg, 0, -1, NULL);
1679: cfg_scan_fp_end();
1680: if (ret == STATE_ERROR)
1681: return CFG_PARSE_ERROR;
1682:
1683: return CFG_SUCCESS;
1684: }
1685:
1686: static char *cfg_make_fullpath(const char *dir, const char *file)
1687: {
1688: int np;
1689: char *path;
1690: size_t len;
1691:
1692: if (!dir || !file) {
1693: errno = EINVAL;
1694: return NULL;
1695: }
1696:
1697: len = strlen(dir) + strlen(file) + 2;
1698: path = malloc(len);
1699: if (!path)
1700: return NULL;
1701:
1702: np = snprintf(path, len, "%s/%s", dir, file);
1703:
1704: /*
1705: * np is the number of characters that would have
1706: * been printed if there was enough room in path.
1707: * if np >= n then the snprintf() was truncated
1708: * (which must be a bug).
1709: */
1710: assert(np < (int)len);
1711:
1712: return path;
1713: }
1714:
1715: DLLIMPORT char *cfg_searchpath(cfg_searchpath_t *p, const char *file)
1716: {
1717: char *fullpath;
1718: #ifdef HAVE_SYS_STAT_H
1719: struct stat st;
1720: int err;
1721: #endif
1722:
1723: if (!p || !file) {
1724: errno = EINVAL;
1725: return NULL;
1726: }
1727:
1728: if ((fullpath = cfg_searchpath(p->next, file)) != NULL)
1729: return fullpath;
1730:
1731: if ((fullpath = cfg_make_fullpath(p->dir, file)) == NULL)
1732: return NULL;
1733:
1734: #ifdef HAVE_SYS_STAT_H
1735: err = stat((const char *)fullpath, &st);
1736: if ((!err) && S_ISREG(st.st_mode))
1737: return fullpath;
1738: #else
1739: /* needs an alternative check here for win32 */
1740: #endif
1741:
1742: free(fullpath);
1743: return NULL;
1744: }
1745:
1746: DLLIMPORT int cfg_parse(cfg_t *cfg, const char *filename)
1747: {
1748: int ret;
1749: char *fn;
1750: FILE *fp;
1751:
1752: if (!cfg || !filename) {
1753: errno = EINVAL;
1754: return CFG_FILE_ERROR;
1755: }
1756:
1757: if (cfg->path)
1758: fn = cfg_searchpath(cfg->path, filename);
1759: else
1760: fn = cfg_tilde_expand(filename);
1761: if (!fn)
1762: return CFG_FILE_ERROR;
1763:
1764: free(cfg->filename);
1765: cfg->filename = fn;
1766:
1767: fp = fopen(cfg->filename, "r");
1768: if (!fp)
1769: return CFG_FILE_ERROR;
1770:
1771: ret = cfg_parse_fp(cfg, fp);
1772: fclose(fp);
1773:
1774: return ret;
1775: }
1776:
1777: DLLIMPORT int cfg_parse_buf(cfg_t *cfg, const char *buf)
1778: {
1779: int ret;
1780: char *fn;
1781: FILE *fp;
1782:
1783: if (!cfg) {
1784: errno = EINVAL;
1785: return CFG_PARSE_ERROR;
1786: }
1787:
1788: if (!buf)
1789: return CFG_SUCCESS;
1790:
1791: fn = strdup("[buf]");
1792: if (!fn)
1793: return CFG_PARSE_ERROR;
1794:
1795: free(cfg->filename);
1796: cfg->filename = fn;
1797:
1798: fp = fmemopen((void *)buf, strlen(buf), "r");
1799: if (!fp) {
1800: /*
1801: * fmemopen() on older GLIBC versions do not accept zero
1802: * length buffers for some reason. This is a workaround.
1803: */
1804: if (strlen(buf) > 0)
1805: return CFG_FILE_ERROR;
1806:
1807: return CFG_SUCCESS;
1808: }
1809:
1810: ret = cfg_parse_fp(cfg, fp);
1811: fclose(fp);
1812:
1813: return ret;
1814: }
1815:
1816: DLLIMPORT cfg_t *cfg_init(cfg_opt_t *opts, cfg_flag_t flags)
1817: {
1818: cfg_t *cfg;
1819:
1820: cfg = calloc(1, sizeof(cfg_t));
1821: if (!cfg)
1822: return NULL;
1823:
1824: cfg->name = strdup("root");
1825: if (!cfg->name) {
1826: free(cfg);
1827: return NULL;
1828: }
1829:
1830: cfg->opts = cfg_dupopt_array(opts);
1831: if (!cfg->opts) {
1832: free(cfg->name);
1833: free(cfg);
1834: return NULL;
1835: }
1836:
1837: cfg->flags = flags;
1838: cfg->filename = 0;
1839: cfg->line = 0;
1840: cfg->errfunc = 0;
1841:
1842: #if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
1843: bindtextdomain(PACKAGE, LOCALEDIR);
1844: #endif
1845:
1846: cfg_init_defaults(cfg);
1847:
1848: return cfg;
1849: }
1850:
1851: DLLIMPORT char *cfg_tilde_expand(const char *filename)
1852: {
1853: char *expanded = 0;
1854:
1855: #ifndef _WIN32
1856: /* Do tilde expansion */
1857: if (filename[0] == '~') {
1858: struct passwd *passwd = 0;
1859: const char *file = 0;
1860:
1861: if (filename[1] == '/' || filename[1] == 0) {
1862: /* ~ or ~/path */
1863: passwd = getpwuid(geteuid());
1864: file = filename + 1;
1865: } else {
1866: /* ~user or ~user/path */
1867: char *user;
1868:
1869: file = strchr(filename, '/');
1870: if (file == 0)
1871: file = filename + strlen(filename);
1872:
1873: user = malloc(file - filename);
1874: if (!user)
1875: return NULL;
1876:
1877: strncpy(user, filename + 1, file - filename - 1);
1878: passwd = getpwnam(user);
1879: free(user);
1880: }
1881:
1882: if (passwd) {
1883: expanded = malloc(strlen(passwd->pw_dir) + strlen(file) + 1);
1884: if (!expanded)
1885: return NULL;
1886:
1887: strcpy(expanded, passwd->pw_dir);
1888: strcat(expanded, file);
1889: }
1890: }
1891: #endif
1892: if (!expanded)
1893: expanded = strdup(filename);
1894:
1895: return expanded;
1896: }
1897:
1898: DLLIMPORT int cfg_free_value(cfg_opt_t *opt)
1899: {
1900: if (!opt) {
1901: errno = EINVAL;
1902: return CFG_FAIL;
1903: }
1904:
1905: if (opt->comment && !is_set(CFGF_RESET, opt->flags)) {
1906: free(opt->comment);
1907: opt->comment = NULL;
1908: }
1909:
1910: if (opt->values) {
1911: unsigned int i;
1912:
1913: for (i = 0; i < opt->nvalues; i++) {
1914: if (opt->type == CFGT_STR) {
1915: free((void *)opt->values[i]->string);
1916: } else if (opt->type == CFGT_SEC) {
1917: opt->values[i]->section->path = NULL; /* Global search path */
1918: cfg_free(opt->values[i]->section);
1919: } else if (opt->type == CFGT_PTR && opt->freecb && opt->values[i]->ptr) {
1920: (opt->freecb) (opt->values[i]->ptr);
1921: }
1922: free(opt->values[i]);
1923: }
1924: free(opt->values);
1925: }
1926:
1927: opt->values = NULL;
1928: opt->nvalues = 0;
1929:
1930: return CFG_SUCCESS;
1931: }
1932:
1933: static void cfg_free_opt_array(cfg_opt_t *opts)
1934: {
1935: int i;
1936:
1937: for (i = 0; opts[i].name; ++i) {
1938: free((void *)opts[i].name);
1939: if (opts[i].comment)
1940: free(opts[i].comment);
1941: if (opts[i].def.parsed)
1942: free(opts[i].def.parsed);
1943: if (opts[i].def.string)
1944: free((void *)opts[i].def.string);
1945: if (opts[i].subopts)
1946: cfg_free_opt_array(opts[i].subopts);
1947: }
1948: free(opts);
1949: }
1950:
1951: static int cfg_free_searchpath(cfg_searchpath_t *p)
1952: {
1953: if (p) {
1954: cfg_free_searchpath(p->next);
1955: free(p->dir);
1956: free(p);
1957: }
1958:
1959: return CFG_SUCCESS;
1960: }
1961:
1962: DLLIMPORT int cfg_free(cfg_t *cfg)
1963: {
1964: int i;
1965: int isroot = 0;
1966:
1967: if (!cfg) {
1968: errno = EINVAL;
1969: return CFG_FAIL;
1970: }
1971:
1972: if (cfg->comment)
1973: free(cfg->comment);
1974:
1975: for (i = 0; cfg->opts[i].name; ++i)
1976: cfg_free_value(&cfg->opts[i]);
1977:
1978: cfg_free_opt_array(cfg->opts);
1979: cfg_free_searchpath(cfg->path);
1980:
1981: if (cfg->name) {
1982: isroot = !strcmp(cfg->name, "root");
1983: free(cfg->name);
1984: }
1985: if (cfg->title)
1986: free(cfg->title);
1987: if (cfg->filename)
1988: free(cfg->filename);
1989:
1990: free(cfg);
1991: if (isroot)
1992: cfg_yylex_destroy();
1993:
1994: return CFG_SUCCESS;
1995: }
1996:
1997: DLLIMPORT int cfg_include(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
1998: {
1999: (void)opt; /* Unused in this predefined include FUNC */
2000:
2001: if (!cfg || !argv) {
2002: errno = EINVAL;
2003: return CFG_FAIL;
2004: }
2005:
2006: if (argc != 1) {
2007: cfg_error(cfg, _("wrong number of arguments to cfg_include()"));
2008: return 1;
2009: }
2010:
2011: return cfg_lexer_include(cfg, argv[0]);
2012: }
2013:
2014: static cfg_value_t *cfg_opt_getval(cfg_opt_t *opt, unsigned int index)
2015: {
2016: cfg_value_t *val = 0;
2017:
2018: if (index != 0 && !is_set(CFGF_LIST, opt->flags) && !is_set(CFGF_MULTI, opt->flags)) {
2019: errno = EINVAL;
2020: return NULL;
2021: }
2022:
2023: if (opt->simple_value.ptr)
2024: val = (cfg_value_t *)opt->simple_value.ptr;
2025: else {
2026: if (is_set(CFGF_RESET, opt->flags)) {
2027: cfg_free_value(opt);
2028: opt->flags &= ~CFGF_RESET;
2029: }
2030:
2031: if (index >= opt->nvalues)
2032: val = cfg_addval(opt);
2033: else
2034: val = opt->values[index];
2035: }
2036:
2037: return val;
2038: }
2039:
2040: DLLIMPORT int cfg_opt_setcomment(cfg_opt_t *opt, char *comment)
2041: {
2042: char *oldcomment, *newcomment;
2043:
2044: if (!opt || !comment) {
2045: errno = EINVAL;
2046: return CFG_FAIL;
2047: }
2048:
2049: oldcomment = opt->comment;
2050: newcomment = strdup(comment);
2051: if (!newcomment)
2052: return CFG_FAIL;
2053:
2054: if (oldcomment)
2055: free(oldcomment);
2056: opt->comment = newcomment;
2057: opt->flags |= CFGF_COMMENTS;
2058: opt->flags |= CFGF_MODIFIED;
2059:
2060: return CFG_SUCCESS;
2061: }
2062:
2063: DLLIMPORT int cfg_setcomment(cfg_t *cfg, const char *name, char *comment)
2064: {
2065: return cfg_opt_setcomment(cfg_getopt(cfg, name), comment);
2066: }
2067:
2068: DLLIMPORT int cfg_opt_setnint(cfg_opt_t *opt, long int value, unsigned int index)
2069: {
2070: cfg_value_t *val;
2071:
2072: if (!opt || opt->type != CFGT_INT) {
2073: errno = EINVAL;
2074: return CFG_FAIL;
2075: }
2076:
2077: val = cfg_opt_getval(opt, index);
2078: if (!val)
2079: return CFG_FAIL;
2080:
2081: val->number = value;
2082: opt->flags |= CFGF_MODIFIED;
2083:
2084: return CFG_SUCCESS;
2085: }
2086:
2087: DLLIMPORT int cfg_setnint(cfg_t *cfg, const char *name, long int value, unsigned int index)
2088: {
2089: cfg_opt_t *opt;
2090:
2091: opt = cfg_getopt(cfg, name);
2092: if (opt && opt->validcb2 && (*opt->validcb2)(cfg, opt, (void *)&value) != 0)
2093: return CFG_FAIL;
2094:
2095: return cfg_opt_setnint(opt, value, index);
2096: }
2097:
2098: DLLIMPORT int cfg_setint(cfg_t *cfg, const char *name, long int value)
2099: {
2100: return cfg_setnint(cfg, name, value, 0);
2101: }
2102:
2103: DLLIMPORT int cfg_opt_setnfloat(cfg_opt_t *opt, double value, unsigned int index)
2104: {
2105: cfg_value_t *val;
2106:
2107: if (!opt || opt->type != CFGT_FLOAT) {
2108: errno = EINVAL;
2109: return CFG_FAIL;
2110: }
2111:
2112: val = cfg_opt_getval(opt, index);
2113: if (!val)
2114: return CFG_FAIL;
2115:
2116: val->fpnumber = value;
2117: opt->flags |= CFGF_MODIFIED;
2118:
2119: return CFG_SUCCESS;
2120: }
2121:
2122: DLLIMPORT int cfg_setnfloat(cfg_t *cfg, const char *name, double value, unsigned int index)
2123: {
2124: cfg_opt_t *opt;
2125:
2126: opt = cfg_getopt(cfg, name);
2127: if (opt && opt->validcb2 && (*opt->validcb2)(cfg, opt, (void *)&value) != 0)
2128: return CFG_FAIL;
2129:
2130: return cfg_opt_setnfloat(opt, value, index);
2131: }
2132:
2133: DLLIMPORT int cfg_setfloat(cfg_t *cfg, const char *name, double value)
2134: {
2135: return cfg_setnfloat(cfg, name, value, 0);
2136: }
2137:
2138: DLLIMPORT int cfg_opt_setnbool(cfg_opt_t *opt, cfg_bool_t value, unsigned int index)
2139: {
2140: cfg_value_t *val;
2141:
2142: if (!opt || opt->type != CFGT_BOOL) {
2143: errno = EINVAL;
2144: return CFG_FAIL;
2145: }
2146:
2147: val = cfg_opt_getval(opt, index);
2148: if (!val)
2149: return CFG_FAIL;
2150:
2151: val->boolean = value;
2152: opt->flags |= CFGF_MODIFIED;
2153:
2154: return CFG_SUCCESS;
2155: }
2156:
2157: DLLIMPORT int cfg_setnbool(cfg_t *cfg, const char *name, cfg_bool_t value, unsigned int index)
2158: {
2159: return cfg_opt_setnbool(cfg_getopt(cfg, name), value, index);
2160: }
2161:
2162: DLLIMPORT int cfg_setbool(cfg_t *cfg, const char *name, cfg_bool_t value)
2163: {
2164: return cfg_setnbool(cfg, name, value, 0);
2165: }
2166:
2167: DLLIMPORT int cfg_opt_setnstr(cfg_opt_t *opt, const char *value, unsigned int index)
2168: {
2169: char *newstr, *oldstr = NULL;
2170: cfg_value_t *val;
2171:
2172: if (!opt || opt->type != CFGT_STR) {
2173: errno = EINVAL;
2174: return CFG_FAIL;
2175: }
2176:
2177: val = cfg_opt_getval(opt, index);
2178: if (!val)
2179: return CFG_FAIL;
2180:
2181: if (val->string)
2182: oldstr = val->string;
2183:
2184: if (value) {
2185: newstr = strdup(value);
2186: if (!newstr)
2187: return CFG_FAIL;
2188: val->string = newstr;
2189: } else {
2190: val->string = NULL;
2191: }
2192:
2193: if (oldstr)
2194: free(oldstr);
2195: opt->flags |= CFGF_MODIFIED;
2196:
2197: return CFG_SUCCESS;
2198: }
2199:
2200: DLLIMPORT int cfg_setnstr(cfg_t *cfg, const char *name, const char *value, unsigned int index)
2201: {
2202: cfg_opt_t *opt;
2203:
2204: opt = cfg_getopt(cfg, name);
2205: if (opt && opt->validcb2 && (*opt->validcb2)(cfg, opt, (void *)value) != 0)
2206: return CFG_FAIL;
2207:
2208: return cfg_opt_setnstr(opt, value, index);
2209: }
2210:
2211: DLLIMPORT int cfg_setstr(cfg_t *cfg, const char *name, const char *value)
2212: {
2213: return cfg_setnstr(cfg, name, value, 0);
2214: }
2215:
2216: static int cfg_addlist_internal(cfg_opt_t *opt, unsigned int nvalues, va_list ap)
2217: {
2218: int result = CFG_FAIL;
2219: unsigned int i;
2220:
2221: for (i = 0; i < nvalues; i++) {
2222: switch (opt->type) {
2223: case CFGT_INT:
2224: result = cfg_opt_setnint(opt, va_arg(ap, int), opt->nvalues);
2225: break;
2226:
2227: case CFGT_FLOAT:
2228: result = cfg_opt_setnfloat(opt, va_arg(ap, double), opt->nvalues);
2229: break;
2230:
2231: case CFGT_BOOL:
2232: result = cfg_opt_setnbool(opt, va_arg(ap, cfg_bool_t), opt->nvalues);
2233: break;
2234:
2235: case CFGT_STR:
2236: result = cfg_opt_setnstr(opt, va_arg(ap, char *), opt->nvalues);
2237: break;
2238:
2239: case CFGT_FUNC:
2240: case CFGT_SEC:
2241: default:
2242: result = CFG_SUCCESS;
2243: break;
2244: }
2245: }
2246:
2247: return result;
2248: }
2249:
2250: DLLIMPORT int cfg_setlist(cfg_t *cfg, const char *name, unsigned int nvalues, ...)
2251: {
2252: va_list ap;
2253: cfg_opt_t *opt = cfg_getopt(cfg, name);
2254:
2255: if (!opt || !is_set(CFGF_LIST, opt->flags)) {
2256: errno = EINVAL;
2257: return CFG_FAIL;
2258: }
2259:
2260: cfg_free_value(opt);
2261: va_start(ap, nvalues);
2262: cfg_addlist_internal(opt, nvalues, ap);
2263: va_end(ap);
2264:
2265: return CFG_SUCCESS;
2266: }
2267:
2268: DLLIMPORT int cfg_addlist(cfg_t *cfg, const char *name, unsigned int nvalues, ...)
2269: {
2270: va_list ap;
2271: cfg_opt_t *opt = cfg_getopt(cfg, name);
2272:
2273: if (!opt || !is_set(CFGF_LIST, opt->flags)) {
2274: errno = EINVAL;
2275: return CFG_FAIL;
2276: }
2277:
2278: va_start(ap, nvalues);
2279: cfg_addlist_internal(opt, nvalues, ap);
2280: va_end(ap);
2281:
2282: return CFG_SUCCESS;
2283: }
2284:
2285: DLLIMPORT cfg_t *cfg_addtsec(cfg_t *cfg, const char *name, const char *title)
2286: {
2287: cfg_opt_t *opt;
2288: cfg_value_t *val;
2289:
2290: if (cfg_gettsec(cfg, name, title))
2291: return NULL;
2292:
2293: opt = cfg_getopt(cfg, name);
2294: if (!opt) {
2295: cfg_error(cfg, _("no such option '%s'"), name);
2296: return NULL;
2297: }
2298: val = cfg_setopt(cfg, opt, title);
2299: if (!val)
2300: return NULL;
2301:
2302: val->section->path = cfg->path; /* Remember global search path. */
2303: val->section->line = 1;
2304: val->section->errfunc = cfg->errfunc;
2305:
2306: return val->section;
2307: }
2308:
2309: DLLIMPORT int cfg_opt_rmnsec(cfg_opt_t *opt, unsigned int index)
2310: {
2311: unsigned int n;
2312: cfg_value_t *val;
2313:
2314: if (!opt || opt->type != CFGT_SEC) {
2315: errno = EINVAL;
2316: return CFG_FAIL;
2317: }
2318:
2319: n = cfg_opt_size(opt);
2320: if (index >= n)
2321: return CFG_FAIL;
2322:
2323: val = cfg_opt_getval(opt, index);
2324: if (!val)
2325: return CFG_FAIL;
2326:
2327: if (index + 1 != n) {
2328: /* not removing last, move the tail */
2329: memmove(&opt->values[index], &opt->values[index + 1], sizeof(opt->values[index]) * (n - index - 1));
2330: }
2331: --opt->nvalues;
2332:
2333: cfg_free(val->section);
2334: free(val);
2335:
2336: return CFG_SUCCESS;
2337: }
2338:
2339: DLLIMPORT int cfg_rmnsec(cfg_t *cfg, const char *name, unsigned int index)
2340: {
2341: return cfg_opt_rmnsec(cfg_getopt(cfg, name), index);
2342: }
2343:
2344: DLLIMPORT int cfg_rmsec(cfg_t *cfg, const char *name)
2345: {
2346: cfg_opt_t *opt;
2347: long int index;
2348:
2349: opt = cfg_getopt_secidx(cfg, name, &index);
2350: return cfg_opt_rmnsec(opt, index);
2351: }
2352:
2353: DLLIMPORT int cfg_opt_rmtsec(cfg_opt_t *opt, const char *title)
2354: {
2355: unsigned int i, n;
2356:
2357: if (!opt || !title) {
2358: errno = EINVAL;
2359: return CFG_FAIL;
2360: }
2361:
2362: if (!is_set(CFGF_TITLE, opt->flags))
2363: return CFG_FAIL;
2364:
2365: n = cfg_opt_size(opt);
2366: for (i = 0; i < n; i++) {
2367: cfg_t *sec = cfg_opt_getnsec(opt, i);
2368:
2369: if (!sec || !sec->title)
2370: return CFG_FAIL;
2371:
2372: if (is_set(CFGF_NOCASE, opt->flags)) {
2373: if (strcasecmp(title, sec->title) == 0)
2374: break;
2375: } else {
2376: if (strcmp(title, sec->title) == 0)
2377: break;
2378: }
2379: }
2380: if (i == n)
2381: return CFG_FAIL;
2382:
2383: return cfg_opt_rmnsec(opt, i);
2384: }
2385:
2386: DLLIMPORT int cfg_rmtsec(cfg_t *cfg, const char *name, const char *title)
2387: {
2388: return cfg_opt_rmtsec(cfg_getopt(cfg, name), title);
2389: }
2390:
2391: DLLIMPORT int cfg_opt_nprint_var(cfg_opt_t *opt, unsigned int index, FILE *fp)
2392: {
2393: const char *str;
2394:
2395: if (!opt || !fp) {
2396: errno = EINVAL;
2397: return CFG_FAIL;
2398: }
2399:
2400: switch (opt->type) {
2401: case CFGT_INT:
2402: fprintf(fp, "%ld", cfg_opt_getnint(opt, index));
2403: break;
2404:
2405: case CFGT_FLOAT:
2406: fprintf(fp, "%f", cfg_opt_getnfloat(opt, index));
2407: break;
2408:
2409: case CFGT_STR:
2410: str = cfg_opt_getnstr(opt, index);
2411: fprintf(fp, "\"");
2412: while (str && *str) {
2413: if (*str == '"')
2414: fprintf(fp, "\\\"");
2415: else if (*str == '\\')
2416: fprintf(fp, "\\\\");
2417: else
2418: fprintf(fp, "%c", *str);
2419: str++;
2420: }
2421: fprintf(fp, "\"");
2422: break;
2423:
2424: case CFGT_BOOL:
2425: fprintf(fp, "%s", cfg_opt_getnbool(opt, index) ? "true" : "false");
2426: break;
2427:
2428: case CFGT_NONE:
2429: case CFGT_SEC:
2430: case CFGT_FUNC:
2431: case CFGT_PTR:
2432: case CFGT_COMMENT:
2433: break;
2434: }
2435:
2436: return CFG_SUCCESS;
2437: }
2438:
2439: static void cfg_indent(FILE *fp, int indent)
2440: {
2441: while (indent--)
2442: fprintf(fp, " ");
2443: }
2444:
2445: static int cfg_opt_print_pff_indent(cfg_opt_t *opt, FILE *fp,
2446: cfg_print_filter_func_t pff, int indent)
2447: {
2448: if (!opt || !fp) {
2449: errno = EINVAL;
2450: return CFG_FAIL;
2451: }
2452:
2453: if (is_set(CFGF_COMMENTS, opt->flags) && opt->comment) {
2454: cfg_indent(fp, indent);
2455: fprintf(fp, "/* %s */\n", opt->comment);
2456: }
2457:
2458: if (opt->type == CFGT_SEC) {
2459: cfg_t *sec;
2460: unsigned int i;
2461:
2462: for (i = 0; i < cfg_opt_size(opt); i++) {
2463: sec = cfg_opt_getnsec(opt, i);
2464: cfg_indent(fp, indent);
2465: if (is_set(CFGF_TITLE, opt->flags))
2466: fprintf(fp, "%s \"%s\" {\n", opt->name, cfg_title(sec));
2467: else
2468: fprintf(fp, "%s {\n", opt->name);
2469: cfg_print_pff_indent(sec, fp, pff, indent + 1);
2470: cfg_indent(fp, indent);
2471: fprintf(fp, "}\n");
2472: }
2473: } else if (opt->type != CFGT_FUNC && opt->type != CFGT_NONE) {
2474: if (is_set(CFGF_LIST, opt->flags)) {
2475: cfg_indent(fp, indent);
2476: fprintf(fp, "%s = {", opt->name);
2477:
2478: if (opt->nvalues) {
2479: unsigned int i;
2480:
2481: if (opt->pf)
2482: opt->pf(opt, 0, fp);
2483: else
2484: cfg_opt_nprint_var(opt, 0, fp);
2485: for (i = 1; i < opt->nvalues; i++) {
2486: fprintf(fp, ", ");
2487: if (opt->pf)
2488: opt->pf(opt, i, fp);
2489: else
2490: cfg_opt_nprint_var(opt, i, fp);
2491: }
2492: }
2493:
2494: fprintf(fp, "}");
2495: } else {
2496: cfg_indent(fp, indent);
2497: /* comment out the option if is not set */
2498: if (cfg_opt_size(opt) == 0 ||
2499: (opt->type == CFGT_STR && !cfg_opt_getnstr(opt, 0)))
2500: fprintf(fp, "# ");
2501: fprintf(fp, "%s=", opt->name);
2502: if (opt->pf)
2503: opt->pf(opt, 0, fp);
2504: else
2505: cfg_opt_nprint_var(opt, 0, fp);
2506: }
2507:
2508: fprintf(fp, "\n");
2509: } else if (opt->pf) {
2510: cfg_indent(fp, indent);
2511: opt->pf(opt, 0, fp);
2512: fprintf(fp, "\n");
2513: }
2514:
2515: return CFG_SUCCESS;
2516: }
2517:
2518: DLLIMPORT int cfg_opt_print_indent(cfg_opt_t *opt, FILE *fp, int indent)
2519: {
2520: return cfg_opt_print_pff_indent(opt, fp, 0, indent);
2521: }
2522:
2523: DLLIMPORT int cfg_opt_print(cfg_opt_t *opt, FILE *fp)
2524: {
2525: return cfg_opt_print_pff_indent(opt, fp, 0, 0);
2526: }
2527:
2528: static int cfg_print_pff_indent(cfg_t *cfg, FILE *fp,
2529: cfg_print_filter_func_t fb_pff, int indent)
2530: {
2531: int i, result = CFG_SUCCESS;
2532:
2533: for (i = 0; cfg->opts[i].name; i++) {
2534: cfg_print_filter_func_t pff = cfg->pff ? cfg->pff : fb_pff;
2535: if (pff && pff(cfg, &cfg->opts[i]))
2536: continue;
2537: result += cfg_opt_print_pff_indent(&cfg->opts[i], fp, pff, indent);
2538: }
2539:
2540: return result;
2541: }
2542:
2543: DLLIMPORT int cfg_print_indent(cfg_t *cfg, FILE *fp, int indent)
2544: {
2545: return cfg_print_pff_indent(cfg, fp, 0, indent);
2546: }
2547:
2548: DLLIMPORT int cfg_print(cfg_t *cfg, FILE *fp)
2549: {
2550: return cfg_print_pff_indent(cfg, fp, 0, 0);
2551: }
2552:
2553: DLLIMPORT cfg_print_func_t cfg_opt_set_print_func(cfg_opt_t *opt, cfg_print_func_t pf)
2554: {
2555: cfg_print_func_t oldpf;
2556:
2557: if (!opt) {
2558: errno = EINVAL;
2559: return NULL;
2560: }
2561:
2562: oldpf = opt->pf;
2563: opt->pf = pf;
2564:
2565: return oldpf;
2566: }
2567:
2568: DLLIMPORT cfg_print_func_t cfg_set_print_func(cfg_t *cfg, const char *name, cfg_print_func_t pf)
2569: {
2570: return cfg_opt_set_print_func(cfg_getopt(cfg, name), pf);
2571: }
2572:
2573: static cfg_opt_t *cfg_getopt_array(cfg_opt_t *rootopts, int cfg_flags, const char *name)
2574: {
2575: unsigned int i;
2576: cfg_opt_t *opts = rootopts;
2577:
2578: if (!rootopts || !name) {
2579: errno = EINVAL;
2580: return NULL;
2581: }
2582:
2583: while (name && *name) {
2584: cfg_t *seccfg;
2585: char *secname;
2586: size_t len = strcspn(name, "|");
2587:
2588: if (name[len] == 0 /*len == strlen(name) */ )
2589: /* no more subsections */
2590: break;
2591:
2592: if (len) {
2593: cfg_opt_t *secopt;
2594:
2595: secname = strndup(name, len);
2596: if (!secname)
2597: return NULL;
2598:
2599: secopt = cfg_getopt_array(opts, cfg_flags, secname);
2600: free(secname);
2601: if (!secopt) {
2602: /*fprintf(stderr, "section not found\n"); */
2603: return NULL;
2604: }
2605: if (secopt->type != CFGT_SEC) {
2606: /*fprintf(stderr, "not a section!\n"); */
2607: return NULL;
2608: }
2609:
2610: if (!is_set(CFGF_MULTI, secopt->flags) && (seccfg = cfg_opt_getnsec(secopt, 0)) != 0)
2611: opts = seccfg->opts;
2612: else
2613: opts = secopt->subopts;
2614:
2615: if (!opts) {
2616: /*fprintf(stderr, "section have no subopts!?\n"); */
2617: return NULL;
2618: }
2619: }
2620: name += len;
2621: name += strspn(name, "|");
2622: }
2623:
2624: for (i = 0; opts[i].name; i++) {
2625: if (is_set(CFGF_NOCASE, cfg_flags)) {
2626: if (strcasecmp(opts[i].name, name) == 0)
2627: return &opts[i];
2628: } else {
2629: if (strcmp(opts[i].name, name) == 0)
2630: return &opts[i];
2631: }
2632: }
2633:
2634: return NULL;
2635: }
2636:
2637: DLLIMPORT cfg_validate_callback_t cfg_set_validate_func(cfg_t *cfg, const char *name, cfg_validate_callback_t vf)
2638: {
2639: cfg_opt_t *opt;
2640: cfg_validate_callback_t oldvf;
2641:
2642: opt = cfg_getopt_array(cfg->opts, cfg->flags, name);
2643: if (!opt)
2644: return NULL;
2645:
2646: oldvf = opt->validcb;
2647: opt->validcb = vf;
2648:
2649: return oldvf;
2650: }
2651:
2652: DLLIMPORT cfg_validate_callback2_t cfg_set_validate_func2(cfg_t *cfg, const char *name, cfg_validate_callback2_t vf)
2653: {
2654: cfg_opt_t *opt;
2655: cfg_validate_callback2_t oldvf;
2656:
2657: opt = cfg_getopt_array(cfg->opts, cfg->flags, name);
2658: if (!opt)
2659: return NULL;
2660:
2661: oldvf = opt->validcb2;
2662: opt->validcb2 = vf;
2663:
2664: return oldvf;
2665: }
2666:
2667: /**
2668: * Local Variables:
2669: * indent-tabs-mode: t
2670: * c-file-style: "linux"
2671: * End:
2672: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>