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