1/*
2 * Copyright (c) 2002,2003,2007 Martin Hedenfalk <martin@bzero.se>
3 *
4 * Permission to use, copy, modify, and 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#define _GNU_SOURCE
22#include <sys/types.h>
23#include <string.h>
24#include <stdlib.h>
25#include <assert.h>
26#include <errno.h>
27#ifndef _WIN32
28# include <pwd.h>
29#endif
30#ifdef HAVE_UNISTD_H
31# include <unistd.h>
32#endif
33#include <ctype.h>
34
35#include "confuse.h"
36
37#define is_set(f, x) (((f) & (x)) == (f))
38
39#if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
40# include <locale.h>
41# include <libintl.h>
42# define _(str) dgettext(PACKAGE, str)
43#else
44# define _(str) str
45#endif
46#define N_(str) str
47
48extern FILE *cfg_yyin;
49extern int cfg_yylex(cfg_t *cfg);
50extern int cfg_lexer_include(cfg_t *cfg, const char *fname);
51extern void cfg_scan_string_begin(const char *buf);
52extern void cfg_scan_string_end(void);
53extern void cfg_scan_fp_begin(FILE *fp);
54extern void cfg_scan_fp_end(void);
55extern char *cfg_qstring;
56
57char *cfg_yylval = 0;
58
59const char confuse_version[] = PACKAGE_VERSION;
60const char confuse_copyright[] = PACKAGE_STRING" by Martin Hedenfalk <martin@bzero.se>";
61const char confuse_author[] = "Martin Hedenfalk <martin@bzero.se>";
62
63static int cfg_parse_internal(cfg_t *cfg, int level,
64                              int force_state, cfg_opt_t *force_opt);
65
66#define STATE_CONTINUE 0
67#define STATE_EOF -1
68#define STATE_ERROR 1
69
70#ifndef HAVE_STRDUP
71# ifdef HAVE__STRDUP
72#  define strdup _strdup
73# else
74static char *strdup(const char *s)
75{
76    char *r;
77
78    if(s == 0 || *s == 0)
79        return 0;
80
81    r = malloc(strlen(s) + 1);
82    assert(r);
83    strcpy(r, s);
84    return r;
85}
86# endif
87#endif
88
89#ifndef HAVE_STRNDUP
90static char *strndup(const char *s, size_t n)
91{
92    char *r;
93
94    if(s == 0)
95        return 0;
96
97    r = malloc(n + 1);
98    assert(r);
99    strncpy(r, s, n);
100    r[n] = 0;
101    return r;
102}
103#endif
104
105#ifndef HAVE_STRCASECMP
106int strcasecmp(const char *s1, const char *s2)
107{
108    assert(s1);
109    assert(s2);
110
111    while(*s1)
112    {
113	int c1 = tolower(*(const unsigned char *)s1);
114	int c2 = tolower(*(const unsigned char *)s2);
115	if(c1 < c2)
116	    return -1;
117	if(c1 > c2)
118	    return +1;
119
120	++s1;
121	++s2;
122    }
123
124    if(*s2 != 0)
125	return -1;
126
127    return 0;
128}
129#endif
130
131DLLIMPORT cfg_opt_t *cfg_getopt(cfg_t *cfg, const char *name)
132{
133    unsigned int i;
134    cfg_t *sec = cfg;
135
136    assert(cfg && cfg->name && name);
137
138    while(name && *name)
139    {
140        char *secname;
141        size_t len = strcspn(name, "|");
142        if(name[len] == 0 /*len == strlen(name)*/)
143            /* no more subsections */
144            break;
145        if(len)
146        {
147            secname = strndup(name, len);
148            sec = cfg_getsec(sec, secname);
149            if(sec == 0)
150                cfg_error(cfg, _("no such option '%s'"), secname);
151            free(secname);
152            if(sec == 0)
153                return 0;
154        }
155        name += len;
156        name += strspn(name, "|");
157    }
158
159    for(i = 0; sec->opts[i].name; i++)
160    {
161        if(is_set(CFGF_NOCASE, sec->flags))
162        {
163            if(strcasecmp(sec->opts[i].name, name) == 0)
164                return &sec->opts[i];
165        }
166        else
167        {
168            if(strcmp(sec->opts[i].name, name) == 0)
169                return &sec->opts[i];
170        }
171    }
172    cfg_error(cfg, _("no such option '%s'"), name);
173    return 0;
174}
175
176DLLIMPORT const char *cfg_title(cfg_t *cfg)
177{
178    if(cfg)
179        return cfg->title;
180    return 0;
181}
182
183DLLIMPORT const char *cfg_name(cfg_t *cfg)
184{
185    if(cfg)
186        return cfg->name;
187    return 0;
188}
189
190DLLIMPORT const char *cfg_opt_name(cfg_opt_t *opt)
191{
192    if(opt)
193        return opt->name;
194    return 0;
195}
196
197DLLIMPORT unsigned int cfg_opt_size(cfg_opt_t *opt)
198{
199    if(opt)
200        return opt->nvalues;
201    return 0;
202}
203
204DLLIMPORT unsigned int cfg_size(cfg_t *cfg, const char *name)
205{
206    return cfg_opt_size(cfg_getopt(cfg, name));
207}
208
209DLLIMPORT signed long cfg_opt_getnint(cfg_opt_t *opt, unsigned int index)
210{
211    assert(opt && opt->type == CFGT_INT);
212    if(opt->values && index < opt->nvalues)
213        return opt->values[index]->number;
214    else if(opt->simple_value)
215        return *(signed long *)opt->simple_value;
216    else
217        return 0;
218}
219
220DLLIMPORT signed long cfg_getnint(cfg_t *cfg, const char *name,
221                                  unsigned int index)
222{
223    return cfg_opt_getnint(cfg_getopt(cfg, name), index);
224}
225
226DLLIMPORT signed long cfg_getint(cfg_t *cfg, const char *name)
227{
228    return cfg_getnint(cfg, name, 0);
229}
230
231DLLIMPORT double cfg_opt_getnfloat(cfg_opt_t *opt, unsigned int index)
232{
233    assert(opt && opt->type == CFGT_FLOAT);
234    if(opt->values && index < opt->nvalues)
235        return opt->values[index]->fpnumber;
236    else if(opt->simple_value)
237        return *(double *)opt->simple_value;
238    else
239        return 0;
240}
241
242DLLIMPORT double cfg_getnfloat(cfg_t *cfg, const char *name,
243                               unsigned int index)
244{
245    return cfg_opt_getnfloat(cfg_getopt(cfg, name), index);
246}
247
248DLLIMPORT double cfg_getfloat(cfg_t *cfg, const char *name)
249{
250    return cfg_getnfloat(cfg, name, 0);
251}
252
253DLLIMPORT cfg_bool_t cfg_opt_getnbool(cfg_opt_t *opt, unsigned int index)
254{
255    assert(opt && opt->type == CFGT_BOOL);
256    if(opt->values && index < opt->nvalues)
257        return opt->values[index]->boolean;
258    else if(opt->simple_value)
259        return *(cfg_bool_t *)opt->simple_value;
260    else
261        return cfg_false;
262}
263
264DLLIMPORT cfg_bool_t cfg_getnbool(cfg_t *cfg, const char *name,
265                                  unsigned int index)
266{
267    return cfg_opt_getnbool(cfg_getopt(cfg, name), index);
268}
269
270DLLIMPORT cfg_bool_t cfg_getbool(cfg_t *cfg, const char *name)
271{
272    return cfg_getnbool(cfg, name, 0);
273}
274
275DLLIMPORT char *cfg_opt_getnstr(cfg_opt_t *opt, unsigned int index)
276{
277    assert(opt && opt->type == CFGT_STR);
278    if(opt->values && index < opt->nvalues)
279        return opt->values[index]->string;
280    else if(opt->simple_value)
281        return *(char **)opt->simple_value;
282    else
283        return 0;
284}
285
286DLLIMPORT char *cfg_getnstr(cfg_t *cfg, const char *name, unsigned int index)
287{
288    return cfg_opt_getnstr(cfg_getopt(cfg, name), index);
289}
290
291DLLIMPORT char *cfg_getstr(cfg_t *cfg, const char *name)
292{
293    return cfg_getnstr(cfg, name, 0);
294}
295
296DLLIMPORT void *cfg_opt_getnptr(cfg_opt_t *opt, unsigned int index)
297{
298    assert(opt && opt->type == CFGT_PTR);
299    if(opt->values && index < opt->nvalues)
300        return opt->values[index]->ptr;
301    else if(opt->simple_value)
302        return *(void **)opt->simple_value;
303    else
304        return 0;
305}
306
307DLLIMPORT void *cfg_getnptr(cfg_t *cfg, const char *name, unsigned int index)
308{
309    return cfg_opt_getnptr(cfg_getopt(cfg, name), index);
310}
311
312DLLIMPORT void *cfg_getptr(cfg_t *cfg, const char *name)
313{
314    return cfg_getnptr(cfg, name, 0);
315}
316
317DLLIMPORT cfg_t *cfg_opt_getnsec(cfg_opt_t *opt, unsigned int index)
318{
319    assert(opt && opt->type == CFGT_SEC);
320    if(opt->values && index < opt->nvalues)
321        return opt->values[index]->section;
322    return 0;
323}
324
325DLLIMPORT cfg_t *cfg_getnsec(cfg_t *cfg, const char *name, unsigned int index)
326{
327    return cfg_opt_getnsec(cfg_getopt(cfg, name), index);
328}
329
330DLLIMPORT cfg_t *cfg_opt_gettsec(cfg_opt_t *opt, const char *title)
331{
332    unsigned int i, n;
333
334    assert(opt && title);
335    if(!is_set(CFGF_TITLE, opt->flags))
336        return 0;
337    n = cfg_opt_size(opt);
338    for(i = 0; i < n; i++)
339    {
340        cfg_t *sec = cfg_opt_getnsec(opt, i);
341        assert(sec && sec->title);
342        if(is_set(CFGF_NOCASE, opt->flags))
343        {
344            if(strcasecmp(title, sec->title) == 0)
345                return sec;
346        }
347        else
348        {
349            if(strcmp(title, sec->title) == 0)
350                return sec;
351        }
352    }
353    return 0;
354}
355
356DLLIMPORT cfg_t *cfg_gettsec(cfg_t *cfg, const char *name, const char *title)
357{
358    return cfg_opt_gettsec(cfg_getopt(cfg, name), title);
359}
360
361DLLIMPORT cfg_t *cfg_getsec(cfg_t *cfg, const char *name)
362{
363    return cfg_getnsec(cfg, name, 0);
364}
365
366static cfg_value_t *cfg_addval(cfg_opt_t *opt)
367{
368    opt->values = realloc(opt->values,
369                                  (opt->nvalues+1) * sizeof(cfg_value_t *));
370    assert(opt->values);
371    opt->values[opt->nvalues] = malloc(sizeof(cfg_value_t));
372    memset(opt->values[opt->nvalues], 0, sizeof(cfg_value_t));
373    return opt->values[opt->nvalues++];
374}
375
376DLLIMPORT int cfg_numopts(cfg_opt_t *opts)
377{
378    int n;
379
380    for(n = 0; opts[n].name; n++)
381        /* do nothing */ ;
382    return n;
383}
384
385static cfg_opt_t *cfg_dupopt_array(cfg_opt_t *opts)
386{
387    int i;
388    cfg_opt_t *dupopts;
389    int n = cfg_numopts(opts);
390
391    dupopts = calloc(n+1, sizeof(cfg_opt_t));
392    memcpy(dupopts, opts, n * sizeof(cfg_opt_t));
393
394    for(i = 0; i < n; i++)
395    {
396        dupopts[i].name = strdup(opts[i].name);
397        if(opts[i].type == CFGT_SEC && opts[i].subopts)
398            dupopts[i].subopts = cfg_dupopt_array(opts[i].subopts);
399
400        if(is_set(CFGF_LIST, opts[i].flags) || opts[i].type == CFGT_FUNC)
401            dupopts[i].def.parsed = opts[i].def.parsed ? strdup(opts[i].def.parsed) : 0;
402        else if(opts[i].type == CFGT_STR)
403            dupopts[i].def.string = opts[i].def.string ? strdup(opts[i].def.string) : 0;
404    }
405
406    return dupopts;
407}
408
409DLLIMPORT int cfg_parse_boolean(const char *s)
410{
411    if(strcasecmp(s, "true") == 0
412       || strcasecmp(s, "on") == 0
413       || strcasecmp(s, "yes") == 0)
414        return 1;
415    else if(strcasecmp(s, "false") == 0
416            || strcasecmp(s, "off") == 0
417            || strcasecmp(s, "no") == 0)
418        return 0;
419    return -1;
420}
421
422static void cfg_init_defaults(cfg_t *cfg)
423{
424    int i;
425
426    for(i = 0; cfg->opts[i].name; i++)
427    {
428        /* libConfuse doesn't handle default values for "simple" options */
429        if(cfg->opts[i].simple_value || is_set(CFGF_NODEFAULT, cfg->opts[i].flags))
430            continue;
431
432        if(cfg->opts[i].type != CFGT_SEC)
433        {
434            cfg->opts[i].flags |= CFGF_DEFINIT;
435
436            if(is_set(CFGF_LIST, cfg->opts[i].flags) ||
437               cfg->opts[i].def.parsed)
438            {
439                int xstate, ret;
440
441                /* If it's a list, but no default value was given,
442                 * keep the option uninitialized.
443                 */
444                if(cfg->opts[i].def.parsed == 0 ||
445                   cfg->opts[i].def.parsed[0] == 0)
446                    continue;
447
448                /* setup scanning from the string specified for the
449                 * "default" value, force the correct state and option
450                 */
451
452                if(is_set(CFGF_LIST, cfg->opts[i].flags))
453                    /* lists must be surrounded by {braces} */
454                    xstate = 3;
455                else if(cfg->opts[i].type == CFGT_FUNC)
456                    xstate = 0;
457                else
458                    xstate = 2;
459
460                cfg_scan_string_begin(cfg->opts[i].def.parsed);
461                do
462                {
463                    ret = cfg_parse_internal(cfg, 1, xstate, &cfg->opts[i]);
464                    xstate = -1;
465                } while(ret == STATE_CONTINUE);
466                cfg_scan_string_end();
467                if(ret == STATE_ERROR)
468                {
469                    /*
470                     * If there was an error parsing the default string,
471                     * the initialization of the default value could be
472                     * inconsistent or empty. What to do? It's a
473                     * programming error and not an end user input
474                     * error. Lets print a message and abort...
475                     */
476                    fprintf(stderr, "Parse error in default value '%s'"
477                            " for option '%s'\n",
478                            cfg->opts[i].def.parsed, cfg->opts[i].name);
479                    fprintf(stderr, "Check your initialization macros and the"
480                            " libConfuse documentation\n");
481                    abort();
482                }
483            }
484            else
485            {
486                switch(cfg->opts[i].type)
487                {
488                    case CFGT_INT:
489                        cfg_opt_setnint(&cfg->opts[i],
490                                        cfg->opts[i].def.number, 0);
491                        break;
492                    case CFGT_FLOAT:
493                        cfg_opt_setnfloat(&cfg->opts[i],
494                                          cfg->opts[i].def.fpnumber, 0);
495                        break;
496                    case CFGT_BOOL:
497                        cfg_opt_setnbool(&cfg->opts[i],
498                                         cfg->opts[i].def.boolean, 0);
499                        break;
500                    case CFGT_STR:
501                        cfg_opt_setnstr(&cfg->opts[i],
502                                        cfg->opts[i].def.string, 0);
503                        break;
504                    case CFGT_FUNC:
505                    case CFGT_PTR:
506                        break;
507                    default:
508                        cfg_error(cfg,
509                                  "internal error in cfg_init_defaults(%s)",
510                                  cfg->opts[i].name);
511                        break;
512                }
513            }
514
515            /* The default value should only be returned if no value
516             * is given in the configuration file, so we set the RESET
517             * flag here. When/If cfg_setopt() is called, the value(s)
518             * will be freed and the flag unset.
519             */
520            cfg->opts[i].flags |= CFGF_RESET;
521        } /* end if cfg->opts[i].type != CFGT_SEC */
522        else if(!is_set(CFGF_MULTI, cfg->opts[i].flags))
523        {
524            cfg_setopt(cfg, &cfg->opts[i], 0);
525            cfg->opts[i].flags |= CFGF_DEFINIT;
526        }
527    }
528}
529
530DLLIMPORT cfg_value_t *
531cfg_setopt(cfg_t *cfg, cfg_opt_t *opt, char *value)
532{
533    cfg_value_t *val = 0;
534    int b;
535    char *s;
536    double f;
537    long int i;
538    void *p;
539    char *endptr;
540
541    assert(cfg && opt);
542
543    if(opt->simple_value)
544    {
545        assert(opt->type != CFGT_SEC);
546        val = (cfg_value_t *)opt->simple_value;
547    }
548    else
549    {
550        if(is_set(CFGF_RESET, opt->flags))
551        {
552            cfg_free_value(opt);
553            opt->flags &= ~CFGF_RESET;
554        }
555
556        if(opt->nvalues == 0 || is_set(CFGF_MULTI, opt->flags) ||
557           is_set(CFGF_LIST, opt->flags))
558        {
559            val = 0;
560            if(opt->type == CFGT_SEC && is_set(CFGF_TITLE, opt->flags))
561            {
562                unsigned int i;
563
564                /* Check if there already is a section with the same title.
565                 */
566
567		/* Assert that there are either no sections at all, or a
568		 * non-NULL section title. */
569                assert(opt->nvalues == 0 || value);
570
571                for(i = 0; i < opt->nvalues && val == NULL; i++)
572                {
573                    cfg_t *sec = opt->values[i]->section;
574                    if(is_set(CFGF_NOCASE, cfg->flags))
575                    {
576                        if(strcasecmp(value, sec->title) == 0)
577                            val = opt->values[i];
578                    }
579                    else
580                    {
581                        if(strcmp(value, sec->title) == 0)
582                            val = opt->values[i];
583                    }
584                }
585                if(val && is_set(CFGF_NO_TITLE_DUPES, opt->flags))
586                {
587                    cfg_error(cfg, _("found duplicate title '%s'"), value);
588                    return 0;
589                }
590            }
591            if(val == NULL)
592                val = cfg_addval(opt);
593        }
594        else
595            val = opt->values[0];
596    }
597
598    switch(opt->type)
599    {
600        case CFGT_INT:
601            if(opt->parsecb)
602            {
603                if((*opt->parsecb)(cfg, opt, value, &i) != 0)
604                    return 0;
605                val->number = i;
606            }
607            else
608            {
609                val->number = strtol(value, &endptr, 0);
610                if(*endptr != '\0')
611                {
612                    cfg_error(cfg, _("invalid integer value for option '%s'"),
613                              opt->name);
614                    return 0;
615                }
616                if(errno == ERANGE)
617                {
618                    cfg_error(cfg,
619                          _("integer value for option '%s' is out of range"),
620                              opt->name);
621                    return 0;
622                }
623            }
624            break;
625
626        case CFGT_FLOAT:
627            if(opt->parsecb)
628            {
629                if((*opt->parsecb)(cfg, opt, value, &f) != 0)
630                    return 0;
631                val->fpnumber = f;
632            }
633            else
634            {
635                val->fpnumber = strtod(value, &endptr);
636                if(*endptr != '\0')
637                {
638                    cfg_error(cfg,
639                          _("invalid floating point value for option '%s'"),
640                              opt->name);
641                    return 0;
642                }
643                if(errno == ERANGE)
644                {
645                    cfg_error(cfg,
646                  _("floating point value for option '%s' is out of range"),
647                              opt->name);
648                    return 0;
649                }
650            }
651            break;
652
653        case CFGT_STR:
654            free(val->string);
655            if(opt->parsecb)
656            {
657                s = 0;
658                if((*opt->parsecb)(cfg, opt, value, &s) != 0)
659                    return 0;
660                val->string = strdup(s);
661            }
662            else
663                val->string = strdup(value);
664            break;
665
666        case CFGT_SEC:
667            if(is_set(CFGF_MULTI, opt->flags) || val->section == 0)
668            {
669                cfg_free(val->section);
670                val->section = calloc(1, sizeof(cfg_t));
671                assert(val->section);
672                val->section->name = strdup(opt->name);
673                val->section->opts = cfg_dupopt_array(opt->subopts);
674                val->section->flags = cfg->flags;
675                val->section->filename = cfg->filename ? strdup(cfg->filename) : 0;
676                val->section->line = cfg->line;
677                val->section->errfunc = cfg->errfunc;
678                val->section->title = value;
679            }
680            if(!is_set(CFGF_DEFINIT, opt->flags))
681                cfg_init_defaults(val->section);
682            break;
683
684        case CFGT_BOOL:
685            if(opt->parsecb)
686            {
687                if((*opt->parsecb)(cfg, opt, value, &b) != 0)
688                    return 0;
689            }
690            else
691            {
692                b = cfg_parse_boolean(value);
693                if(b == -1)
694                {
695                    cfg_error(cfg, _("invalid boolean value for option '%s'"),
696                              opt->name);
697                    return 0;
698                }
699            }
700            val->boolean = (cfg_bool_t)b;
701            break;
702
703        case CFGT_PTR:
704            assert(opt->parsecb);
705            if((*opt->parsecb)(cfg, opt, value, &p) != 0)
706                return 0;
707            val->ptr = p;
708            break;
709
710        default:
711            cfg_error(cfg, "internal error in cfg_setopt(%s, %s)",
712                      opt->name, value);
713            assert(0);
714            break;
715    }
716    return val;
717}
718
719DLLIMPORT cfg_errfunc_t cfg_set_error_function(cfg_t *cfg,
720                                               cfg_errfunc_t errfunc)
721{
722    cfg_errfunc_t old;
723
724    assert(cfg);
725    old = cfg->errfunc;
726    cfg->errfunc = errfunc;
727    return old;
728}
729
730DLLIMPORT void cfg_error(cfg_t *cfg, const char *fmt, ...)
731{
732    va_list ap;
733
734    va_start(ap, fmt);
735
736    if(cfg && cfg->errfunc)
737        (*cfg->errfunc)(cfg, fmt, ap);
738    else
739    {
740        if(cfg && cfg->filename && cfg->line)
741            fprintf(stderr, "%s:%d: ", cfg->filename, cfg->line);
742        else if(cfg && cfg->filename)
743            fprintf(stderr, "%s: ", cfg->filename);
744        vfprintf(stderr, fmt, ap);
745        fprintf(stderr, "\n");
746    }
747
748    va_end(ap);
749}
750
751static int call_function(cfg_t *cfg, cfg_opt_t *opt, cfg_opt_t *funcopt)
752{
753    int ret;
754    const char **argv;
755    unsigned int i;
756
757    /* create a regular argv string vector and call
758     * the registered function
759     */
760    argv = calloc(funcopt->nvalues, sizeof(char *));
761    for(i = 0; i < funcopt->nvalues; i++)
762        argv[i] = funcopt->values[i]->string;
763    ret = (*opt->func)(cfg, opt, funcopt->nvalues, argv);
764    cfg_free_value(funcopt);
765    free(argv);
766    return ret;
767}
768
769static int cfg_parse_internal(cfg_t *cfg, int level,
770                              int force_state, cfg_opt_t *force_opt)
771{
772    int state = 0;
773    char *opttitle = 0;
774    cfg_opt_t *opt = 0;
775    cfg_value_t *val = 0;
776    cfg_opt_t funcopt = CFG_STR(0, 0, 0);
777    int num_values = 0; /* number of values found for a list option */
778    int rc;
779
780    if(force_state != -1)
781        state = force_state;
782    if(force_opt)
783        opt = force_opt;
784
785    while(1)
786    {
787        int tok = cfg_yylex(cfg);
788
789        if(tok == 0)
790        {
791            /* lexer.l should have called cfg_error */
792            return STATE_ERROR;
793        }
794
795        if(tok == EOF)
796        {
797            if(state != 0)
798            {
799                cfg_error(cfg, _("premature end of file"));
800                return STATE_ERROR;
801            }
802            return STATE_EOF;
803        }
804
805        switch(state)
806        {
807            case 0: /* expecting an option name */
808                if(tok == '}')
809                {
810                    if(level == 0)
811                    {
812                        cfg_error(cfg, _("unexpected closing brace"));
813                        return STATE_ERROR;
814                    }
815                    return STATE_EOF;
816                }
817                if(tok != CFGT_STR)
818                {
819                    cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
820                    return STATE_ERROR;
821                }
822                opt = cfg_getopt(cfg, cfg_yylval);
823                if(opt == 0)
824                    return STATE_ERROR;
825                if(opt->type == CFGT_SEC)
826                {
827                    if(is_set(CFGF_TITLE, opt->flags))
828                        state = 6;
829                    else
830                        state = 5;
831                }
832                else if(opt->type == CFGT_FUNC)
833                {
834                    state = 7;
835                }
836                else
837                    state = 1;
838                break;
839
840            case 1: /* expecting an equal sign or plus-equal sign */
841                if(tok == '+')
842                {
843                    if(!is_set(CFGF_LIST, opt->flags))
844                    {
845                        cfg_error(cfg,
846                                  _("attempt to append to non-list option '%s'"),
847                                  opt->name);
848                        return STATE_ERROR;
849                    }
850                    /* Even if the reset flag was set by
851                     * cfg_init_defaults, appending to the defaults
852                     * should be ok.
853                     */
854                    opt->flags &= ~CFGF_RESET;
855                }
856                else if(tok == '=')
857                {
858                    /* set the (temporary) reset flag to clear the old
859                     * values, since we obviously didn't want to append */
860                    opt->flags |= CFGF_RESET;
861                }
862                else
863                {
864                    cfg_error(cfg, _("missing equal sign after option '%s'"),
865                              opt->name);
866                    return STATE_ERROR;
867                }
868                if(is_set(CFGF_LIST, opt->flags))
869                {
870                    state = 3;
871                    num_values = 0;
872                } else
873                    state = 2;
874                break;
875
876            case 2: /* expecting an option value */
877                if(tok == '}' && is_set(CFGF_LIST, opt->flags))
878                {
879                    state = 0;
880                    if(num_values == 0 && is_set(CFGF_RESET, opt->flags))
881                        /* Reset flags was set, and the empty list was
882                         * specified. Free all old values. */
883                        cfg_free_value(opt);
884                    break;
885                }
886
887                if(tok != CFGT_STR)
888                {
889                    cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
890                    return STATE_ERROR;
891                }
892
893                if(cfg_setopt(cfg, opt, cfg_yylval) == 0)
894                    return STATE_ERROR;
895                if(opt->validcb && (*opt->validcb)(cfg, opt) != 0)
896                    return STATE_ERROR;
897                if(is_set(CFGF_LIST, opt->flags))
898                {
899                    ++num_values;
900                    state = 4;
901                }
902                else
903                    state = 0;
904                break;
905
906            case 3: /* expecting an opening brace for a list option */
907                if(tok != '{')
908                {
909                    if(tok != CFGT_STR)
910                    {
911                        cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
912                        return STATE_ERROR;
913                    }
914
915                    if(cfg_setopt(cfg, opt, cfg_yylval) == 0)
916                        return STATE_ERROR;
917                    if(opt->validcb && (*opt->validcb)(cfg, opt) != 0)
918                        return STATE_ERROR;
919                    ++num_values;
920                    state = 0;
921                }
922                else
923                    state = 2;
924                break;
925
926            case 4: /* expecting a separator for a list option, or
927                     * closing (list) brace */
928                if(tok == ',')
929                    state = 2;
930                else if(tok == '}')
931                {
932                    state = 0;
933                    if(opt->validcb && (*opt->validcb)(cfg, opt) != 0)
934                        return STATE_ERROR;
935                }
936                else
937                {
938                    cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
939                    return STATE_ERROR;
940                }
941                break;
942
943            case 5: /* expecting an opening brace for a section */
944                if(tok != '{')
945                {
946                    cfg_error(cfg, _("missing opening brace for section '%s'"),
947                              opt->name);
948                    return STATE_ERROR;
949                }
950
951                val = cfg_setopt(cfg, opt, opttitle);
952                opttitle = 0;
953                if(!val)
954                    return STATE_ERROR;
955
956                val->section->line = cfg->line;
957		val->section->errfunc = cfg->errfunc;
958                rc = cfg_parse_internal(val->section, level+1,-1,0);
959                cfg->line = val->section->line;
960                if(rc != STATE_EOF)
961                    return STATE_ERROR;
962                if(opt->validcb && (*opt->validcb)(cfg, opt) != 0)
963                    return STATE_ERROR;
964                state = 0;
965                break;
966
967            case 6: /* expecting a title for a section */
968                if(tok != CFGT_STR)
969                {
970                    cfg_error(cfg, _("missing title for section '%s'"),
971                              opt->name);
972                    return STATE_ERROR;
973                }
974                else
975                    opttitle = strdup(cfg_yylval);
976                state = 5;
977                break;
978
979            case 7: /* expecting an opening parenthesis for a function */
980                if(tok != '(')
981                {
982                    cfg_error(cfg, _("missing parenthesis for function '%s'"),
983                              opt->name);
984                    return STATE_ERROR;
985                }
986                state = 8;
987                break;
988
989            case 8: /* expecting a function parameter or a closing paren */
990                if(tok == ')')
991                {
992                    int ret = call_function(cfg, opt, &funcopt);
993                    if(ret != 0)
994                        return STATE_ERROR;
995                    state = 0;
996                }
997                else if(tok == CFGT_STR)
998                {
999                    val = cfg_addval(&funcopt);
1000                    val->string = strdup(cfg_yylval);
1001                    state = 9;
1002                }
1003                else
1004                {
1005                    cfg_error(cfg, _("syntax error in call of function '%s'"),
1006                              opt->name);
1007                    return STATE_ERROR;
1008                }
1009                break;
1010
1011            case 9: /* expecting a comma in a function or a closing paren */
1012                if(tok == ')')
1013                {
1014                    int ret = call_function(cfg, opt, &funcopt);
1015                    if(ret != 0)
1016                        return STATE_ERROR;
1017                    state = 0;
1018                }
1019                else if(tok == ',')
1020                    state = 8;
1021                else
1022                {
1023                    cfg_error(cfg, _("syntax error in call of function '%s'"),
1024                              opt->name);
1025                    return STATE_ERROR;
1026                }
1027                break;
1028
1029            default:
1030                /* missing state, internal error, abort */
1031                assert(0);
1032        }
1033    }
1034
1035    return STATE_EOF;
1036}
1037
1038DLLIMPORT int cfg_parse_fp(cfg_t *cfg, FILE *fp)
1039{
1040    int ret;
1041    assert(cfg && fp);
1042
1043    if(cfg->filename == 0)
1044        cfg->filename = strdup("FILE");
1045    cfg->line = 1;
1046
1047    cfg_yyin = fp;
1048    cfg_scan_fp_begin(cfg_yyin);
1049    ret = cfg_parse_internal(cfg, 0, -1, 0);
1050    cfg_scan_fp_end();
1051    if(ret == STATE_ERROR)
1052        return CFG_PARSE_ERROR;
1053    return CFG_SUCCESS;
1054}
1055
1056DLLIMPORT int cfg_parse(cfg_t *cfg, const char *filename)
1057{
1058    int ret;
1059    FILE *fp;
1060
1061    assert(cfg && filename);
1062
1063    free(cfg->filename);
1064    cfg->filename = cfg_tilde_expand(filename);
1065    fp = fopen(cfg->filename, "r");
1066    if(fp == 0)
1067        return CFG_FILE_ERROR;
1068    ret = cfg_parse_fp(cfg, fp);
1069    fclose(fp);
1070    return ret;
1071}
1072
1073DLLIMPORT int cfg_parse_buf(cfg_t *cfg, const char *buf)
1074{
1075    int ret;
1076
1077    assert(cfg);
1078    if(buf == 0)
1079        return CFG_SUCCESS;
1080
1081    free(cfg->filename);
1082    cfg->filename = strdup("[buf]");
1083    cfg->line = 1;
1084
1085    cfg_scan_string_begin(buf);
1086    ret = cfg_parse_internal(cfg, 0, -1, 0);
1087    cfg_scan_string_end();
1088    if(ret == STATE_ERROR)
1089        return CFG_PARSE_ERROR;
1090    return CFG_SUCCESS;
1091}
1092
1093DLLIMPORT cfg_t *cfg_init(cfg_opt_t *opts, cfg_flag_t flags)
1094{
1095    cfg_t *cfg;
1096
1097    cfg = calloc(1, sizeof(cfg_t));
1098    assert(cfg);
1099
1100    cfg->name = strdup("root");
1101    cfg->opts = cfg_dupopt_array(opts);
1102    cfg->flags = flags;
1103    cfg->filename = 0;
1104    cfg->line = 0;
1105    cfg->errfunc = 0;
1106
1107    cfg_init_defaults(cfg);
1108
1109#if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
1110    setlocale(LC_MESSAGES, "");
1111    setlocale(LC_CTYPE, "");
1112    bindtextdomain(PACKAGE, LOCALEDIR);
1113#endif
1114
1115    return cfg;
1116}
1117
1118DLLIMPORT char *cfg_tilde_expand(const char *filename)
1119{
1120    char *expanded = 0;
1121
1122#ifndef _WIN32
1123    /* do tilde expansion
1124     */
1125    if(filename[0] == '~')
1126    {
1127        struct passwd *passwd = 0;
1128        const char *file = 0;
1129
1130        if(filename[1] == '/' || filename[1] == 0)
1131        {
1132            /* ~ or ~/path */
1133            passwd = getpwuid(geteuid());
1134            file = filename + 1;
1135        }
1136        else
1137        {
1138            /* ~user or ~user/path */
1139            char *user;
1140
1141            file = strchr(filename, '/');
1142            if(file == 0)
1143                file = filename + strlen(filename);
1144            user = malloc(file - filename);
1145            strncpy(user, filename + 1, file - filename - 1);
1146            passwd = getpwnam(user);
1147            free(user);
1148        }
1149
1150        if(passwd)
1151        {
1152            expanded = malloc(strlen(passwd->pw_dir) + strlen(file) + 1);
1153            strcpy(expanded, passwd->pw_dir);
1154            strcat(expanded, file);
1155        }
1156    }
1157#endif
1158    if(!expanded)
1159        expanded = strdup(filename);
1160    return expanded;
1161}
1162
1163DLLIMPORT void cfg_free_value(cfg_opt_t *opt)
1164{
1165    unsigned int i;
1166
1167    if(opt == 0)
1168        return;
1169
1170    if(opt->values)
1171    {
1172        for(i = 0; i < opt->nvalues; i++)
1173        {
1174            if(opt->type == CFGT_STR)
1175                free(opt->values[i]->string);
1176            else if(opt->type == CFGT_SEC)
1177                cfg_free(opt->values[i]->section);
1178            else if(opt->type == CFGT_PTR && opt->freecb && opt->values[i]->ptr)
1179                (opt->freecb)(opt->values[i]->ptr);
1180            free(opt->values[i]);
1181        }
1182        free(opt->values);
1183    }
1184    opt->values = 0;
1185    opt->nvalues = 0;
1186}
1187
1188static void cfg_free_opt_array(cfg_opt_t *opts)
1189{
1190    int i;
1191
1192    for(i = 0; opts[i].name; ++i)
1193    {
1194        free(opts[i].name);
1195        if(opts[i].type == CFGT_FUNC || is_set(CFGF_LIST, opts[i].flags))
1196            free(opts[i].def.parsed);
1197        else if(opts[i].type == CFGT_STR)
1198            free(opts[i].def.string);
1199        else if(opts[i].type == CFGT_SEC)
1200            cfg_free_opt_array(opts[i].subopts);
1201    }
1202    free(opts);
1203}
1204
1205DLLIMPORT void cfg_free(cfg_t *cfg)
1206{
1207    int i;
1208
1209    if(cfg == 0)
1210        return;
1211
1212    for(i = 0; cfg->opts[i].name; ++i)
1213        cfg_free_value(&cfg->opts[i]);
1214
1215    cfg_free_opt_array(cfg->opts);
1216
1217    free(cfg->name);
1218    free(cfg->title);
1219    free(cfg->filename);
1220
1221    free(cfg);
1222}
1223
1224DLLIMPORT int cfg_include(cfg_t *cfg, cfg_opt_t *opt, int argc,
1225                          const char **argv)
1226{
1227    opt = NULL;
1228    if(argc != 1)
1229    {
1230        cfg_error(cfg, _("wrong number of arguments to cfg_include()"));
1231        return 1;
1232    }
1233    return cfg_lexer_include(cfg, argv[0]);
1234}
1235
1236static cfg_value_t *cfg_opt_getval(cfg_opt_t *opt, unsigned int index)
1237{
1238    cfg_value_t *val = 0;
1239
1240    assert(index == 0 || is_set(CFGF_LIST, opt->flags));
1241
1242    if(opt->simple_value)
1243        val = (cfg_value_t *)opt->simple_value;
1244    else
1245    {
1246        if(is_set(CFGF_RESET, opt->flags))
1247        {
1248            cfg_free_value(opt);
1249            opt->flags &= ~CFGF_RESET;
1250        }
1251
1252        if(index >= opt->nvalues)
1253            val = cfg_addval(opt);
1254        else
1255            val = opt->values[index];
1256    }
1257    return val;
1258}
1259
1260DLLIMPORT void cfg_opt_setnint(cfg_opt_t *opt, long int value,
1261                               unsigned int index)
1262{
1263    cfg_value_t *val;
1264    assert(opt && opt->type == CFGT_INT);
1265    val = cfg_opt_getval(opt, index);
1266    val->number = value;
1267}
1268
1269DLLIMPORT void cfg_setnint(cfg_t *cfg, const char *name,
1270                     long int value, unsigned int index)
1271{
1272    cfg_opt_setnint(cfg_getopt(cfg, name), value, index);
1273}
1274
1275DLLIMPORT void cfg_setint(cfg_t *cfg, const char *name, long int value)
1276{
1277    cfg_setnint(cfg, name, value, 0);
1278}
1279
1280DLLIMPORT void cfg_opt_setnfloat(cfg_opt_t *opt, double value,
1281                                 unsigned int index)
1282{
1283    cfg_value_t *val;
1284    assert(opt && opt->type == CFGT_FLOAT);
1285    val = cfg_opt_getval(opt, index);
1286    val->fpnumber = value;
1287}
1288
1289DLLIMPORT void cfg_setnfloat(cfg_t *cfg, const char *name,
1290                   double value, unsigned int index)
1291{
1292    cfg_opt_setnfloat(cfg_getopt(cfg, name), value, index);
1293}
1294
1295DLLIMPORT void cfg_setfloat(cfg_t *cfg, const char *name, double value)
1296{
1297    cfg_setnfloat(cfg, name, value, 0);
1298}
1299
1300DLLIMPORT void cfg_opt_setnbool(cfg_opt_t *opt, cfg_bool_t value,
1301                                unsigned int index)
1302{
1303    cfg_value_t *val;
1304    assert(opt && opt->type == CFGT_BOOL);
1305    val = cfg_opt_getval(opt, index);
1306    val->boolean = value;
1307}
1308
1309DLLIMPORT void cfg_setnbool(cfg_t *cfg, const char *name,
1310                            cfg_bool_t value, unsigned int index)
1311{
1312    cfg_opt_setnbool(cfg_getopt(cfg, name), value, index);
1313}
1314
1315DLLIMPORT void cfg_setbool(cfg_t *cfg, const char *name, cfg_bool_t value)
1316{
1317    cfg_setnbool(cfg, name, value, 0);
1318}
1319
1320DLLIMPORT void cfg_opt_setnstr(cfg_opt_t *opt, const char *value,
1321                               unsigned int index)
1322{
1323    cfg_value_t *val;
1324    assert(opt && opt->type == CFGT_STR);
1325    val = cfg_opt_getval(opt, index);
1326    free(val->string);
1327    val->string = value ? strdup(value) : 0;
1328}
1329
1330DLLIMPORT void cfg_setnstr(cfg_t *cfg, const char *name,
1331                           const char *value, unsigned int index)
1332{
1333    cfg_opt_setnstr(cfg_getopt(cfg, name), value, index);
1334}
1335
1336DLLIMPORT void cfg_setstr(cfg_t *cfg, const char *name, const char *value)
1337{
1338    cfg_setnstr(cfg, name, value, 0);
1339}
1340
1341static void cfg_addlist_internal(cfg_opt_t *opt,
1342                                 unsigned int nvalues, va_list ap)
1343{
1344    unsigned int i;
1345
1346    for(i = 0; i < nvalues; i++)
1347    {
1348        switch(opt->type)
1349        {
1350            case CFGT_INT:
1351                cfg_opt_setnint(opt, va_arg(ap, int), opt->nvalues);
1352                break;
1353            case CFGT_FLOAT:
1354                cfg_opt_setnfloat(opt, va_arg(ap, double),
1355                                  opt->nvalues);
1356                break;
1357            case CFGT_BOOL:
1358                cfg_opt_setnbool(opt, va_arg(ap, cfg_bool_t),
1359                                 opt->nvalues);
1360                break;
1361            case CFGT_STR:
1362                cfg_opt_setnstr(opt, va_arg(ap, char*), opt->nvalues);
1363                break;
1364            case CFGT_FUNC:
1365            case CFGT_SEC:
1366            default:
1367                break;
1368        }
1369    }
1370}
1371
1372DLLIMPORT void cfg_setlist(cfg_t *cfg, const char *name,
1373                           unsigned int nvalues, ...)
1374{
1375    va_list ap;
1376    cfg_opt_t *opt = cfg_getopt(cfg, name);
1377
1378    assert(opt && is_set(CFGF_LIST, opt->flags));
1379
1380    cfg_free_value(opt);
1381    va_start(ap, nvalues);
1382    cfg_addlist_internal(opt, nvalues, ap);
1383    va_end(ap);
1384}
1385
1386DLLIMPORT void cfg_addlist(cfg_t *cfg, const char *name,
1387                           unsigned int nvalues, ...)
1388{
1389    va_list ap;
1390    cfg_opt_t *opt = cfg_getopt(cfg, name);
1391
1392    assert(opt && is_set(CFGF_LIST, opt->flags));
1393
1394    va_start(ap, nvalues);
1395    cfg_addlist_internal(opt, nvalues, ap);
1396    va_end(ap);
1397}
1398
1399DLLIMPORT void cfg_opt_nprint_var(cfg_opt_t *opt, unsigned int index, FILE *fp)
1400{
1401    const char *str;
1402
1403    assert(opt && fp);
1404    switch(opt->type)
1405    {
1406        case CFGT_INT:
1407            fprintf(fp, "%ld", cfg_opt_getnint(opt, index));
1408            break;
1409        case CFGT_FLOAT:
1410            fprintf(fp, "%lf", cfg_opt_getnfloat(opt, index));
1411            break;
1412        case CFGT_STR:
1413            str = cfg_opt_getnstr(opt, index);
1414            fprintf(fp, "\"");
1415            while (str && *str)
1416	    {
1417                if(*str == '"')
1418                    fprintf(fp, "\\\"");
1419                else if(*str == '\\')
1420                    fprintf(fp, "\\\\");
1421                else
1422                    fprintf(fp, "%c", *str);
1423                str++;
1424            }
1425            fprintf(fp, "\"");
1426            break;
1427        case CFGT_BOOL:
1428            fprintf(fp, "%s", cfg_opt_getnbool(opt, index) ? "true" : "false");
1429            break;
1430        case CFGT_NONE:
1431        case CFGT_SEC:
1432        case CFGT_FUNC:
1433        case CFGT_PTR:
1434            break;
1435    }
1436}
1437
1438static void cfg_indent(FILE *fp, int indent)
1439{
1440    while(indent--)
1441        fprintf(fp, "  ");
1442}
1443
1444DLLIMPORT void cfg_opt_print_indent(cfg_opt_t *opt, FILE *fp, int indent)
1445{
1446    assert(opt && fp);
1447
1448    if(opt->type == CFGT_SEC)
1449    {
1450        cfg_t *sec;
1451        unsigned int i;
1452
1453        for(i = 0; i < cfg_opt_size(opt); i++)
1454        {
1455            sec = cfg_opt_getnsec(opt, i);
1456            cfg_indent(fp, indent);
1457            if(is_set(CFGF_TITLE, opt->flags))
1458                fprintf(fp, "%s \"%s\" {\n", opt->name, cfg_title(sec));
1459            else
1460                fprintf(fp, "%s {\n", opt->name);
1461            cfg_print_indent(sec, fp, indent + 1);
1462            cfg_indent(fp, indent);
1463            fprintf(fp, "}\n");
1464        }
1465    }
1466    else if(opt->type != CFGT_FUNC && opt->type != CFGT_NONE)
1467    {
1468        if(is_set(CFGF_LIST, opt->flags))
1469        {
1470            unsigned int i;
1471
1472            cfg_indent(fp, indent);
1473            fprintf(fp, "%s = {", opt->name);
1474
1475            if(opt->nvalues)
1476            {
1477                if(opt->pf)
1478                    opt->pf(opt, 0, fp);
1479                else
1480                    cfg_opt_nprint_var(opt, 0, fp);
1481                for(i = 1; i < opt->nvalues; i++)
1482                {
1483                    fprintf(fp, ", ");
1484                    if(opt->pf)
1485                        opt->pf(opt, i, fp);
1486                    else
1487                        cfg_opt_nprint_var(opt, i, fp);
1488                }
1489            }
1490
1491            fprintf(fp, "}");
1492        }
1493        else
1494        {
1495            cfg_indent(fp, indent);
1496            /* comment out the option if is not set */
1497            if(opt->simple_value)
1498            {
1499                if(opt->type == CFGT_STR && *((char **)opt->simple_value) == 0)
1500                    fprintf(fp, "# ");
1501            }
1502            else
1503            {
1504                if(cfg_opt_size(opt) == 0 || (
1505                       opt->type == CFGT_STR && (opt->values[0]->string == 0 ||
1506                                             opt->values[0]->string[0] == 0)))
1507                    fprintf(fp, "# ");
1508            }
1509            fprintf(fp, "%s = ", opt->name);
1510            if(opt->pf)
1511                opt->pf(opt, 0, fp);
1512            else
1513                cfg_opt_nprint_var(opt, 0, fp);
1514        }
1515
1516        fprintf(fp, "\n");
1517    }
1518    else if(opt->pf)
1519    {
1520        cfg_indent(fp, indent);
1521        opt->pf(opt, 0, fp);
1522        fprintf(fp, "\n");
1523    }
1524}
1525
1526DLLIMPORT void cfg_opt_print(cfg_opt_t *opt, FILE *fp)
1527{
1528    cfg_opt_print_indent(opt, fp, 0);
1529}
1530
1531DLLIMPORT void cfg_print_indent(cfg_t *cfg, FILE *fp, int indent)
1532{
1533    int i;
1534
1535    for(i = 0; cfg->opts[i].name; i++)
1536        cfg_opt_print_indent(&cfg->opts[i], fp, indent);
1537}
1538
1539DLLIMPORT void cfg_print(cfg_t *cfg, FILE *fp)
1540{
1541    cfg_print_indent(cfg, fp, 0);
1542}
1543
1544DLLIMPORT cfg_print_func_t cfg_opt_set_print_func(cfg_opt_t *opt,
1545                                                  cfg_print_func_t pf)
1546{
1547    cfg_print_func_t oldpf;
1548
1549    assert(opt);
1550    oldpf = opt->pf;
1551    opt->pf = pf;
1552
1553    return oldpf;
1554}
1555
1556DLLIMPORT cfg_print_func_t cfg_set_print_func(cfg_t *cfg, const char *name,
1557                                              cfg_print_func_t pf)
1558{
1559    return cfg_opt_set_print_func(cfg_getopt(cfg, name), pf);
1560}
1561
1562static cfg_opt_t *cfg_getopt_array(cfg_opt_t *rootopts, int cfg_flags, const char *name)
1563{
1564    unsigned int i;
1565    cfg_opt_t *opts = rootopts;
1566
1567    assert(rootopts && name);
1568
1569    while(name && *name)
1570    {
1571        cfg_t *seccfg;
1572        char *secname;
1573        size_t len = strcspn(name, "|");
1574        if(name[len] == 0 /*len == strlen(name)*/)
1575            /* no more subsections */
1576            break;
1577        if(len)
1578        {
1579            cfg_opt_t *secopt;
1580            secname = strndup(name, len);
1581            secopt = cfg_getopt_array(opts, cfg_flags, secname);
1582            free(secname);
1583            if(secopt == 0)
1584            {
1585                /*fprintf(stderr, "section not found\n");*/
1586                return 0;
1587            }
1588            if(secopt->type != CFGT_SEC)
1589            {
1590                /*fprintf(stderr, "not a section!\n");*/
1591                return 0;
1592            }
1593
1594            if(!is_set(CFGF_MULTI, secopt->flags) &&
1595	       (seccfg = cfg_opt_getnsec(secopt, 0)) != 0)
1596	    {
1597                opts = seccfg->opts;
1598	    }
1599            else
1600                opts = secopt->subopts;
1601            if(opts == 0)
1602            {
1603                /*fprintf(stderr, "section have no subopts!?\n");*/
1604                return 0;
1605            }
1606        }
1607        name += len;
1608        name += strspn(name, "|");
1609    }
1610
1611    for(i = 0; opts[i].name; i++)
1612    {
1613        if(is_set(CFGF_NOCASE, cfg_flags))
1614        {
1615            if(strcasecmp(opts[i].name, name) == 0)
1616                return &opts[i];
1617        }
1618        else
1619        {
1620            if(strcmp(opts[i].name, name) == 0)
1621                return &opts[i];
1622        }
1623    }
1624    return 0;
1625}
1626
1627DLLIMPORT cfg_validate_callback_t cfg_set_validate_func(cfg_t *cfg,
1628                                                        const char *name,
1629                                                        cfg_validate_callback_t vf)
1630{
1631    cfg_opt_t *opt = cfg_getopt_array(cfg->opts, cfg->flags, name);
1632    cfg_validate_callback_t oldvf;
1633    assert(opt);
1634    oldvf = opt->validcb;
1635    opt->validcb = vf;
1636    return oldvf;
1637}
1638
1639