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  */
strdup(const char * str)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
strndup(const char * s,size_t n)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
strcasecmp(const char * s1,const char * s2)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 
cfg_getopt_leaf(cfg_t * cfg,const char * name)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 
parse_title(const char * name,size_t * len)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 
cfg_opt_gettsecidx(cfg_opt_t * opt,const char * title)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 
cfg_getopt_secidx(cfg_t * cfg,const char * name,long int * index)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 
cfg_getnopt(cfg_t * cfg,unsigned int index)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 
cfg_getopt(cfg_t * cfg,const char * name)368 DLLIMPORT cfg_opt_t *cfg_getopt(cfg_t *cfg, const char *name)
369 {
370 	return cfg_getopt_secidx(cfg, name, NULL);
371 }
372 
cfg_title(cfg_t * cfg)373 DLLIMPORT const char *cfg_title(cfg_t *cfg)
374 {
375 	if (cfg)
376 		return cfg->title;
377 	return NULL;
378 }
379 
cfg_name(cfg_t * cfg)380 DLLIMPORT const char *cfg_name(cfg_t *cfg)
381 {
382 	if (cfg)
383 		return cfg->name;
384 	return NULL;
385 }
386 
cfg_opt_name(cfg_opt_t * opt)387 DLLIMPORT const char *cfg_opt_name(cfg_opt_t *opt)
388 {
389 	if (opt)
390 		return opt->name;
391 	return NULL;
392 }
393 
cfg_opt_getstr(cfg_opt_t * opt)394 DLLIMPORT const char *cfg_opt_getstr(cfg_opt_t *opt)
395 {
396 	return cfg_opt_getnstr(opt, 0);
397 }
398 
cfg_opt_size(cfg_opt_t * opt)399 DLLIMPORT unsigned int cfg_opt_size(cfg_opt_t *opt)
400 {
401 	if (opt)
402 		return opt->nvalues;
403 	return 0;
404 }
405 
cfg_size(cfg_t * cfg,const char * name)406 DLLIMPORT unsigned int cfg_size(cfg_t *cfg, const char *name)
407 {
408 	return cfg_opt_size(cfg_getopt(cfg, name));
409 }
410 
cfg_opt_getcomment(cfg_opt_t * opt)411 DLLIMPORT char *cfg_opt_getcomment(cfg_opt_t *opt)
412 {
413 	if (opt)
414 		return opt->comment;
415 
416 	return NULL;
417 }
418 
cfg_getcomment(cfg_t * cfg,const char * name)419 DLLIMPORT char *cfg_getcomment(cfg_t *cfg, const char *name)
420 {
421 	return cfg_opt_getcomment(cfg_getopt(cfg, name));
422 }
423 
cfg_opt_getnint(cfg_opt_t * opt,unsigned int index)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 
cfg_getnint(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_getint(cfg_t * cfg,const char * name)444 DLLIMPORT signed long cfg_getint(cfg_t *cfg, const char *name)
445 {
446 	return cfg_getnint(cfg, name, 0);
447 }
448 
cfg_opt_getnfloat(cfg_opt_t * opt,unsigned int index)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 
cfg_getnfloat(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_getfloat(cfg_t * cfg,const char * name)469 DLLIMPORT double cfg_getfloat(cfg_t *cfg, const char *name)
470 {
471 	return cfg_getnfloat(cfg, name, 0);
472 }
473 
cfg_opt_getnbool(cfg_opt_t * opt,unsigned int index)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 
cfg_getnbool(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_getbool(cfg_t * cfg,const char * name)494 DLLIMPORT cfg_bool_t cfg_getbool(cfg_t *cfg, const char *name)
495 {
496 	return cfg_getnbool(cfg, name, 0);
497 }
498 
cfg_opt_getnstr(cfg_opt_t * opt,unsigned int index)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 
cfg_getnstr(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_getstr(cfg_t * cfg,const char * name)519 DLLIMPORT char *cfg_getstr(cfg_t *cfg, const char *name)
520 {
521 	return cfg_getnstr(cfg, name, 0);
522 }
523 
cfg_opt_getnptr(cfg_opt_t * opt,unsigned int index)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 
cfg_getnptr(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_getptr(cfg_t * cfg,const char * name)544 DLLIMPORT void *cfg_getptr(cfg_t *cfg, const char *name)
545 {
546 	return cfg_getnptr(cfg, name, 0);
547 }
548 
cfg_opt_getnsec(cfg_opt_t * opt,unsigned int index)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 
cfg_getnsec(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_opt_gettsec(cfg_opt_t * opt,const char * title)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 
cfg_gettsec(cfg_t * cfg,const char * name,const char * title)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 
cfg_getsec(cfg_t * cfg,const char * name)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 
cfg_addval(cfg_opt_t * opt)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 
cfg_addopt(cfg_t * cfg,char * key)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 
cfg_numopts(cfg_opt_t * opts)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 
cfg_num(cfg_t * cfg)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 
cfg_dupopt_array(cfg_opt_t * opts)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 
cfg_parse_boolean(const char * s)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 
cfg_init_defaults(cfg_t * cfg)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 
cfg_setopt(cfg_t * cfg,cfg_opt_t * opt,const char * value)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 
cfg_opt_setmulti(cfg_t * cfg,cfg_opt_t * opt,unsigned int nvalues,char ** values)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 
cfg_setmulti(cfg_t * cfg,const char * name,unsigned int nvalues,char ** values)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 
cfg_add_searchpath(cfg_t * cfg,const char * dir)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 
cfg_set_error_function(cfg_t * cfg,cfg_errfunc_t errfunc)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 
cfg_set_print_filter_func(cfg_t * cfg,cfg_print_filter_func_t pff)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 
cfg_error(cfg_t * cfg,const char * fmt,...)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 
call_function(cfg_t * cfg,cfg_opt_t * opt,cfg_opt_t * funcopt)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 
cfg_handle_deprecated(cfg_t * cfg,cfg_opt_t * opt)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 
cfg_parse_internal(cfg_t * cfg,int level,int force_state,cfg_opt_t * force_opt)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 
cfg_parse_fp(cfg_t * cfg,FILE * fp)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 
cfg_make_fullpath(const char * dir,const char * file)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 
cfg_searchpath(cfg_searchpath_t * p,const char * file)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 
cfg_parse(cfg_t * cfg,const char * filename)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 
cfg_parse_buf(cfg_t * cfg,const char * buf)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 
cfg_init(cfg_opt_t * opts,cfg_flag_t flags)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 
cfg_tilde_expand(const char * filename)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 
cfg_free_value(cfg_opt_t * opt)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 
cfg_free_opt_array(cfg_opt_t * opts)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 
cfg_free_searchpath(cfg_searchpath_t * p)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 
cfg_free(cfg_t * cfg)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 
cfg_include(cfg_t * cfg,cfg_opt_t * opt,int argc,const char ** argv)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 
cfg_opt_getval(cfg_opt_t * opt,unsigned int index)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 
cfg_opt_setcomment(cfg_opt_t * opt,char * comment)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 
cfg_setcomment(cfg_t * cfg,const char * name,char * comment)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 
cfg_opt_setnint(cfg_opt_t * opt,long int value,unsigned int index)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 
cfg_setnint(cfg_t * cfg,const char * name,long int value,unsigned int index)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 
cfg_setint(cfg_t * cfg,const char * name,long int value)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 
cfg_opt_setnfloat(cfg_opt_t * opt,double value,unsigned int index)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 
cfg_setnfloat(cfg_t * cfg,const char * name,double value,unsigned int index)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 
cfg_setfloat(cfg_t * cfg,const char * name,double value)2133 DLLIMPORT int cfg_setfloat(cfg_t *cfg, const char *name, double value)
2134 {
2135 	return cfg_setnfloat(cfg, name, value, 0);
2136 }
2137 
cfg_opt_setnbool(cfg_opt_t * opt,cfg_bool_t value,unsigned int index)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 
cfg_setnbool(cfg_t * cfg,const char * name,cfg_bool_t value,unsigned int index)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 
cfg_setbool(cfg_t * cfg,const char * name,cfg_bool_t value)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 
cfg_opt_setnstr(cfg_opt_t * opt,const char * value,unsigned int index)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 
cfg_setnstr(cfg_t * cfg,const char * name,const char * value,unsigned int index)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 
cfg_setstr(cfg_t * cfg,const char * name,const char * value)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 
cfg_addlist_internal(cfg_opt_t * opt,unsigned int nvalues,va_list ap)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 
cfg_setlist(cfg_t * cfg,const char * name,unsigned int nvalues,...)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 
cfg_addlist(cfg_t * cfg,const char * name,unsigned int nvalues,...)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 
cfg_addtsec(cfg_t * cfg,const char * name,const char * title)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 
cfg_opt_rmnsec(cfg_opt_t * opt,unsigned int index)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 
cfg_rmnsec(cfg_t * cfg,const char * name,unsigned int index)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 
cfg_rmsec(cfg_t * cfg,const char * name)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 
cfg_opt_rmtsec(cfg_opt_t * opt,const char * title)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 
cfg_rmtsec(cfg_t * cfg,const char * name,const char * title)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 
cfg_opt_nprint_var(cfg_opt_t * opt,unsigned int index,FILE * fp)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 
cfg_indent(FILE * fp,int indent)2439 static void cfg_indent(FILE *fp, int indent)
2440 {
2441 	while (indent--)
2442 		fprintf(fp, "  ");
2443 }
2444 
cfg_opt_print_pff_indent(cfg_opt_t * opt,FILE * fp,cfg_print_filter_func_t pff,int indent)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 
cfg_opt_print_indent(cfg_opt_t * opt,FILE * fp,int indent)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 
cfg_opt_print(cfg_opt_t * opt,FILE * fp)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 
cfg_print_pff_indent(cfg_t * cfg,FILE * fp,cfg_print_filter_func_t fb_pff,int indent)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 
cfg_print_indent(cfg_t * cfg,FILE * fp,int indent)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 
cfg_print(cfg_t * cfg,FILE * fp)2548 DLLIMPORT int cfg_print(cfg_t *cfg, FILE *fp)
2549 {
2550 	return cfg_print_pff_indent(cfg, fp, 0, 0);
2551 }
2552 
cfg_opt_set_print_func(cfg_opt_t * opt,cfg_print_func_t pf)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 
cfg_set_print_func(cfg_t * cfg,const char * name,cfg_print_func_t pf)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 
cfg_getopt_array(cfg_opt_t * rootopts,int cfg_flags,const char * name)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 
cfg_set_validate_func(cfg_t * cfg,const char * name,cfg_validate_callback_t vf)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 
cfg_set_validate_func2(cfg_t * cfg,const char * name,cfg_validate_callback2_t vf)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  */
2673