Annotation of embedaddon/confuse/src/confuse.c, revision 1.1.1.2

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>