Diff for /embedaddon/confuse/src/confuse.c between versions 1.1.1.1 and 1.1.1.2

version 1.1.1.1, 2017/01/24 14:48:56 version 1.1.1.2, 2021/03/17 00:49:17
Line 1 Line 1
 /*  /*
 * Copyright (c) 2002,2003,2007 Martin Hedenfalk <martin@bzero.se> * Copyright (c) 2002-2017  Martin Hedenfalk <martin@bzero.se>
  *   *
 * Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.   * copyright notice and this permission notice appear in all copies.
  *   *
Line 18 Line 18
 # include <config.h>  # include <config.h>
 #endif  #endif
   
 #define _GNU_SOURCE  
 #include <sys/types.h>  #include <sys/types.h>
 #include <string.h>  #include <string.h>
   #ifdef HAVE_STRINGS_H
   # include <strings.h>
   #endif
 #include <stdlib.h>  #include <stdlib.h>
 #include <assert.h>  #include <assert.h>
 #include <errno.h>  #include <errno.h>
Line 32 Line 34
 #endif  #endif
 #include <ctype.h>  #include <ctype.h>
   
   #ifdef HAVE_SYS_STAT_H
   # include <sys/stat.h>
   # ifndef S_ISREG
   #  define S_ISREG(mode) ((mode) & S_IFREG)
   # endif
   #endif
   
   #include "compat.h"
 #include "confuse.h"  #include "confuse.h"
   
 #define is_set(f, x) (((f) & (x)) == (f))  #define is_set(f, x) (((f) & (x)) == (f))
Line 45 Line 55
 #endif  #endif
 #define N_(str) str  #define N_(str) str
   
extern FILE *cfg_yyin;const char confuse_version[] = PACKAGE_VERSION;
extern int cfg_yylex(cfg_t *cfg);const char confuse_copyright[] = PACKAGE_STRING " by Martin Hedenfalk <martin@bzero.se>";
extern int cfg_lexer_include(cfg_t *cfg, const char *fname);const char confuse_author[] = "Martin Hedenfalk <martin@bzero.se>";
extern void cfg_scan_string_begin(const char *buf); 
extern void cfg_scan_string_end(void); 
extern void cfg_scan_fp_begin(FILE *fp); 
extern void cfg_scan_fp_end(void); 
extern char *cfg_qstring; 
   
 char *cfg_yylval = 0;  char *cfg_yylval = 0;
   
const char confuse_version[] = PACKAGE_VERSION;extern int  cfg_yylex(cfg_t *cfg);
const char confuse_copyright[] = PACKAGE_STRING" by Martin Hedenfalk <martin@bzero.se>";extern void cfg_yylex_destroy(void);
const char confuse_author[] = "Martin Hedenfalk <martin@bzero.se>";extern int  cfg_lexer_include(cfg_t *cfg, const char *fname);
 extern void cfg_scan_fp_begin(FILE *fp);
 extern void cfg_scan_fp_end(void);
   
static int cfg_parse_internal(cfg_t *cfg, int level,static int cfg_parse_internal(cfg_t *cfg, int level, int force_state, cfg_opt_t *force_opt);
                              int force_state, cfg_opt_t *force_opt);static void cfg_free_opt_array(cfg_opt_t *opts);
 static int cfg_print_pff_indent(cfg_t *cfg, FILE *fp,
                                 cfg_print_filter_func_t fb_pff, int indent);
   
 #define STATE_CONTINUE 0  #define STATE_CONTINUE 0
 #define STATE_EOF -1  #define STATE_EOF -1
 #define STATE_ERROR 1  #define STATE_ERROR 1
   
   #ifndef HAVE_FMEMOPEN
   extern FILE *fmemopen(void *buf, size_t size, const char *type);
   #endif
   
   #ifndef HAVE_REALLOCARRAY
   extern void *reallocarray(void *optr, size_t nmemb, size_t size);
   #endif
   
 #ifndef HAVE_STRDUP  #ifndef HAVE_STRDUP
# ifdef HAVE__STRDUP/*
#  define strdup _strdup * Copyright (c) 1988, 1993
# else *      The Regents of the University of California.  All rights reserved.
static char *strdup(const char *s) *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
 static char *strdup(const char *str)
 {  {
    char *r;        size_t len;
         char *dup;
   
    if(s == 0 || *s == 0)        len = strlen(str) + 1;
        return 0;        dup = calloc(len, sizeof(char));
         if (!dup)
                 return NULL;
   
    r = malloc(strlen(s) + 1);        memcpy(dup, str, len);
    assert(r);
    strcpy(r, s);        return dup;
    return r; 
 }  }
 # endif  
 #endif  #endif
   
 #ifndef HAVE_STRNDUP  #ifndef HAVE_STRNDUP
 static char *strndup(const char *s, size_t n)  static char *strndup(const char *s, size_t n)
 {  {
    char *r;        char *r;
   
    if(s == 0)        r = malloc(n + 1);
        return 0;        if (!r)
                 return NULL;
   
    r = malloc(n + 1);        strncpy(r, s, n);
    assert(r);        r[n] = 0;
    strncpy(r, s, n);
    r[n] = 0;        return r;
    return r; 
 }  }
 #endif  #endif
   
 #ifndef HAVE_STRCASECMP  #ifndef HAVE_STRCASECMP
 int strcasecmp(const char *s1, const char *s2)  int strcasecmp(const char *s1, const char *s2)
 {  {
    assert(s1);        assert(s1);
    assert(s2);        assert(s2);
   
    while(*s1)        while (*s1) {
    {                int c1 = tolower(*(const unsigned char *)s1);
        int c1 = tolower(*(const unsigned char *)s1);                int c2 = tolower(*(const unsigned char *)s2);
        int c2 = tolower(*(const unsigned char *)s2); 
        if(c1 < c2) 
            return -1; 
        if(c1 > c2) 
            return +1; 
   
        ++s1;                if (c1 < c2)
        ++s2;                        return -1;
    }                if (c1 > c2)
                         return +1;
   
    if(*s2 != 0)                ++s1;
        return -1;                ++s2;
         }
   
    return 0;        if (*s2 != 0)
                 return -1;
 
         return 0;
 }  }
 #endif  #endif
   
DLLIMPORT cfg_opt_t *cfg_getopt(cfg_t *cfg, const char *name)static cfg_opt_t *cfg_getopt_leaf(cfg_t *cfg, const char *name)
 {  {
    unsigned int i;        unsigned int i;
    cfg_t *sec = cfg; 
   
    assert(cfg && cfg->name && name);        for (i = 0; cfg->opts && cfg->opts[i].name; i++) {
                 if (is_set(CFGF_NOCASE, cfg->flags)) {
                         if (strcasecmp(cfg->opts[i].name, name) == 0)
                                 return &cfg->opts[i];
                 } else {
                         if (strcmp(cfg->opts[i].name, name) == 0)
                                 return &cfg->opts[i];
                 }
         }
   
    while(name && *name)        return NULL;
    {}
        char *secname; 
        size_t len = strcspn(name, "|"); 
        if(name[len] == 0 /*len == strlen(name)*/) 
            /* no more subsections */ 
            break; 
        if(len) 
        { 
            secname = strndup(name, len); 
            sec = cfg_getsec(sec, secname); 
            if(sec == 0) 
                cfg_error(cfg, _("no such option '%s'"), secname); 
            free(secname); 
            if(sec == 0) 
                return 0; 
        } 
        name += len; 
        name += strspn(name, "|"); 
    } 
   
    for(i = 0; sec->opts[i].name; i++)static char *parse_title(const char *name, size_t *len)
    {{
        if(is_set(CFGF_NOCASE, sec->flags))        const char *escapes = "'\\";
        {        char *title;
            if(strcasecmp(sec->opts[i].name, name) == 0)        char *end;
                return &sec->opts[i];        char *ch;
        }
        else        if (*name != '\'') {
        {                *len = strcspn(name, "|");
            if(strcmp(sec->opts[i].name, name) == 0)                if (!*len)
                return &sec->opts[i];                        return NULL;
        }                return strndup(name, *len);
    }        }
    cfg_error(cfg, _("no such option '%s'"), name);
    return 0;        title = strdup(name + 1);
         if (!title)
                 return NULL;
 
         *len = 1;
         ch = title;
         end = title + strlen(title);
         while (ch < end) {
                 size_t l = strcspn(ch, escapes);
                 *len += l + 1;
                 ch += l;
                 switch (*ch) {
                 case '\'':
                         *ch = 0;
                         return title;
                 case '\\':
                         if (!ch[1] || strcspn(ch + 1, escapes)) {
                                 free(title);
                                 return NULL;
                         }
                         memmove(ch, ch + 1, strlen(ch));
                         ch++;
                         (*len)++;
                         break;
                 default:
                         free(title);
                         return NULL;
                 }
         }
 
         free(title);
         return NULL;
 }  }
   
   static long int cfg_opt_gettsecidx(cfg_opt_t *opt, const char *title)
   {
           unsigned int i, n;
   
           n = cfg_opt_size(opt);
           for (i = 0; i < n; i++) {
                   cfg_t *sec = cfg_opt_getnsec(opt, i);
   
                   if (!sec || !sec->title)
                           return -1;
   
                   if (is_set(CFGF_NOCASE, opt->flags)) {
                           if (strcasecmp(title, sec->title) == 0)
                                   return i;
                   } else {
                           if (strcmp(title, sec->title) == 0)
                                   return i;
                   }
           }
   
           return -1;
   }
   
   static cfg_opt_t *cfg_getopt_secidx(cfg_t *cfg, const char *name,
                                       long int *index)
   {
           cfg_opt_t *opt = NULL;
           cfg_t *sec = cfg;
   
           if (!cfg || !cfg->name || !name || !*name) {
                   errno = EINVAL;
                   return NULL;
           }
   
           while (name && *name) {
                   char *title = NULL;
                   long int i = -1;
                   char *secname;
                   size_t len;
   
                   len = strcspn(name, "|=");
                   if (!index && name[len] == 0 /*len == strlen(name) */ )
                           /* no more subsections */
                           break;
   
                   if (!len)
                           break;
   
                   secname = strndup(name, len);
                   if (!secname)
                           return NULL;
   
                   do {
                           char *endptr;
   
                           opt = cfg_getopt_leaf(sec, secname);
                           if (!opt || opt->type != CFGT_SEC) {
                                   opt = NULL;
                                   break;
                           }
                           if (name[len] != '=') {
                                   /* non-multi, and backwards compat */
                                   i = 0;
                                   break;
                           }
                           if (!is_set(CFGF_MULTI, opt->flags))
                                   break;
                           name += len + 1;
                           title = parse_title(name, &len);
                           if (!title)
                                   break;
                           if (is_set(CFGF_TITLE, opt->flags)) {
                                   i = cfg_opt_gettsecidx(opt, title);
                                   break;
                           }
   
                           i = strtol(title, &endptr, 0);
                           if (*endptr != '\0')
                                   i = -1;
                   } while(0);
   
                   if (index)
                           *index = i;
   
                   sec = i >= 0 ? cfg_opt_getnsec(opt, i) : NULL;
                   if (!sec && !is_set(CFGF_IGNORE_UNKNOWN, cfg->flags)) {
                           if (opt && !is_set(CFGF_MULTI, opt->flags))
                                   cfg_error(cfg, _("no such option '%s'"), secname);
                           else if (title)
                                   cfg_error(cfg, _("no sub-section '%s' in '%s'"), title, secname);
                           else
                                   cfg_error(cfg, _("no sub-section title/index for '%s'"), secname);
                   }
   
                   free(secname);
                   if (title)
                           free(title);
                   if (!sec)
                           return NULL;
   
                   name += len;
                   name += strspn(name, "|");
           }
   
           if (!index) {
                   opt = cfg_getopt_leaf(sec, name);
   
                   if (!opt && !is_set(CFGF_IGNORE_UNKNOWN, cfg->flags))
                           cfg_error(cfg, _("no such option '%s'"), name);
           }
   
           return opt;
   }
   
   DLLIMPORT cfg_opt_t *cfg_getnopt(cfg_t *cfg, unsigned int index)
   {
           unsigned int i;
   
           if (!cfg)
                   return NULL;
   
           for (i = 0; cfg->opts && cfg->opts[i].name; i++) {
                   if (i == index)
                           return &cfg->opts[i];
           }
   
           return NULL;
   }
   
   DLLIMPORT cfg_opt_t *cfg_getopt(cfg_t *cfg, const char *name)
   {
           return cfg_getopt_secidx(cfg, name, NULL);
   }
   
 DLLIMPORT const char *cfg_title(cfg_t *cfg)  DLLIMPORT const char *cfg_title(cfg_t *cfg)
 {  {
    if(cfg)        if (cfg)
        return cfg->title;                return cfg->title;
    return 0;        return NULL;
 }  }
   
 DLLIMPORT const char *cfg_name(cfg_t *cfg)  DLLIMPORT const char *cfg_name(cfg_t *cfg)
 {  {
    if(cfg)        if (cfg)
        return cfg->name;                return cfg->name;
    return 0;        return NULL;
 }  }
   
 DLLIMPORT const char *cfg_opt_name(cfg_opt_t *opt)  DLLIMPORT const char *cfg_opt_name(cfg_opt_t *opt)
 {  {
    if(opt)        if (opt)
        return opt->name;                return opt->name;
    return 0;        return NULL;
 }  }
   
   DLLIMPORT const char *cfg_opt_getstr(cfg_opt_t *opt)
   {
           return cfg_opt_getnstr(opt, 0);
   }
   
 DLLIMPORT unsigned int cfg_opt_size(cfg_opt_t *opt)  DLLIMPORT unsigned int cfg_opt_size(cfg_opt_t *opt)
 {  {
    if(opt)        if (opt)
        return opt->nvalues;                return opt->nvalues;
    return 0;        return 0;
 }  }
   
 DLLIMPORT unsigned int cfg_size(cfg_t *cfg, const char *name)  DLLIMPORT unsigned int cfg_size(cfg_t *cfg, const char *name)
 {  {
    return cfg_opt_size(cfg_getopt(cfg, name));        return cfg_opt_size(cfg_getopt(cfg, name));
 }  }
   
   DLLIMPORT char *cfg_opt_getcomment(cfg_opt_t *opt)
   {
           if (opt)
                   return opt->comment;
   
           return NULL;
   }
   
   DLLIMPORT char *cfg_getcomment(cfg_t *cfg, const char *name)
   {
           return cfg_opt_getcomment(cfg_getopt(cfg, name));
   }
   
 DLLIMPORT signed long cfg_opt_getnint(cfg_opt_t *opt, unsigned int index)  DLLIMPORT signed long cfg_opt_getnint(cfg_opt_t *opt, unsigned int index)
 {  {
    assert(opt && opt->type == CFGT_INT);        if (!opt || opt->type != CFGT_INT) {
    if(opt->values && index < opt->nvalues)                errno = EINVAL;
        return opt->values[index]->number;                return 0;
    else if(opt->simple_value)        }
        return *(signed long *)opt->simple_value;
    else        if (opt->values && index < opt->nvalues)
        return 0;                return opt->values[index]->number;
         if (opt->simple_value.number)
                 return *opt->simple_value.number;
 
         return 0;
 }  }
   
DLLIMPORT signed long cfg_getnint(cfg_t *cfg, const char *name,DLLIMPORT signed long cfg_getnint(cfg_t *cfg, const char *name, unsigned int index)
                                  unsigned int index) 
 {  {
    return cfg_opt_getnint(cfg_getopt(cfg, name), index);        return cfg_opt_getnint(cfg_getopt(cfg, name), index);
 }  }
   
 DLLIMPORT signed long cfg_getint(cfg_t *cfg, const char *name)  DLLIMPORT signed long cfg_getint(cfg_t *cfg, const char *name)
 {  {
    return cfg_getnint(cfg, name, 0);        return cfg_getnint(cfg, name, 0);
 }  }
   
 DLLIMPORT double cfg_opt_getnfloat(cfg_opt_t *opt, unsigned int index)  DLLIMPORT double cfg_opt_getnfloat(cfg_opt_t *opt, unsigned int index)
 {  {
    assert(opt && opt->type == CFGT_FLOAT);        if (!opt || opt->type != CFGT_FLOAT) {
    if(opt->values && index < opt->nvalues)                errno = EINVAL;
        return opt->values[index]->fpnumber;                return 0;
    else if(opt->simple_value)        }
        return *(double *)opt->simple_value;
    else        if (opt->values && index < opt->nvalues)
        return 0;                return opt->values[index]->fpnumber;
         if (opt->simple_value.fpnumber)
                 return *opt->simple_value.fpnumber;
 
         return 0;
 }  }
   
DLLIMPORT double cfg_getnfloat(cfg_t *cfg, const char *name,DLLIMPORT double cfg_getnfloat(cfg_t *cfg, const char *name, unsigned int index)
                               unsigned int index) 
 {  {
    return cfg_opt_getnfloat(cfg_getopt(cfg, name), index);        return cfg_opt_getnfloat(cfg_getopt(cfg, name), index);
 }  }
   
 DLLIMPORT double cfg_getfloat(cfg_t *cfg, const char *name)  DLLIMPORT double cfg_getfloat(cfg_t *cfg, const char *name)
 {  {
    return cfg_getnfloat(cfg, name, 0);        return cfg_getnfloat(cfg, name, 0);
 }  }
   
 DLLIMPORT cfg_bool_t cfg_opt_getnbool(cfg_opt_t *opt, unsigned int index)  DLLIMPORT cfg_bool_t cfg_opt_getnbool(cfg_opt_t *opt, unsigned int index)
 {  {
    assert(opt && opt->type == CFGT_BOOL);        if (!opt || opt->type != CFGT_BOOL) {
    if(opt->values && index < opt->nvalues)                errno = EINVAL;
        return opt->values[index]->boolean;                return cfg_false;
    else if(opt->simple_value)        }
        return *(cfg_bool_t *)opt->simple_value;
    else        if (opt->values && index < opt->nvalues)
        return cfg_false;                return opt->values[index]->boolean;
         if (opt->simple_value.boolean)
                 return *opt->simple_value.boolean;
 
         return cfg_false;
 }  }
   
DLLIMPORT cfg_bool_t cfg_getnbool(cfg_t *cfg, const char *name,DLLIMPORT cfg_bool_t cfg_getnbool(cfg_t *cfg, const char *name, unsigned int index)
                                  unsigned int index) 
 {  {
    return cfg_opt_getnbool(cfg_getopt(cfg, name), index);        return cfg_opt_getnbool(cfg_getopt(cfg, name), index);
 }  }
   
 DLLIMPORT cfg_bool_t cfg_getbool(cfg_t *cfg, const char *name)  DLLIMPORT cfg_bool_t cfg_getbool(cfg_t *cfg, const char *name)
 {  {
    return cfg_getnbool(cfg, name, 0);        return cfg_getnbool(cfg, name, 0);
 }  }
   
 DLLIMPORT char *cfg_opt_getnstr(cfg_opt_t *opt, unsigned int index)  DLLIMPORT char *cfg_opt_getnstr(cfg_opt_t *opt, unsigned int index)
 {  {
    assert(opt && opt->type == CFGT_STR);        if (!opt || opt->type != CFGT_STR) {
    if(opt->values && index < opt->nvalues)                errno = EINVAL;
        return opt->values[index]->string;                return NULL;
    else if(opt->simple_value)        }
        return *(char **)opt->simple_value;
    else        if (opt->values && index < opt->nvalues)
        return 0;                return opt->values[index]->string;
         if (opt->simple_value.string)
                 return *opt->simple_value.string;
 
         return NULL;
 }  }
   
 DLLIMPORT char *cfg_getnstr(cfg_t *cfg, const char *name, unsigned int index)  DLLIMPORT char *cfg_getnstr(cfg_t *cfg, const char *name, unsigned int index)
 {  {
    return cfg_opt_getnstr(cfg_getopt(cfg, name), index);        return cfg_opt_getnstr(cfg_getopt(cfg, name), index);
 }  }
   
 DLLIMPORT char *cfg_getstr(cfg_t *cfg, const char *name)  DLLIMPORT char *cfg_getstr(cfg_t *cfg, const char *name)
 {  {
    return cfg_getnstr(cfg, name, 0);        return cfg_getnstr(cfg, name, 0);
 }  }
   
 DLLIMPORT void *cfg_opt_getnptr(cfg_opt_t *opt, unsigned int index)  DLLIMPORT void *cfg_opt_getnptr(cfg_opt_t *opt, unsigned int index)
 {  {
    assert(opt && opt->type == CFGT_PTR);        if (!opt || opt->type != CFGT_PTR) {
    if(opt->values && index < opt->nvalues)                errno = EINVAL;
        return opt->values[index]->ptr;                return NULL;
    else if(opt->simple_value)        }
        return *(void **)opt->simple_value;
    else        if (opt->values && index < opt->nvalues)
        return 0;                return opt->values[index]->ptr;
         if (opt->simple_value.ptr)
                 return *opt->simple_value.ptr;
 
         return NULL;
 }  }
   
 DLLIMPORT void *cfg_getnptr(cfg_t *cfg, const char *name, unsigned int index)  DLLIMPORT void *cfg_getnptr(cfg_t *cfg, const char *name, unsigned int index)
 {  {
    return cfg_opt_getnptr(cfg_getopt(cfg, name), index);        return cfg_opt_getnptr(cfg_getopt(cfg, name), index);
 }  }
   
 DLLIMPORT void *cfg_getptr(cfg_t *cfg, const char *name)  DLLIMPORT void *cfg_getptr(cfg_t *cfg, const char *name)
 {  {
    return cfg_getnptr(cfg, name, 0);        return cfg_getnptr(cfg, name, 0);
 }  }
   
 DLLIMPORT cfg_t *cfg_opt_getnsec(cfg_opt_t *opt, unsigned int index)  DLLIMPORT cfg_t *cfg_opt_getnsec(cfg_opt_t *opt, unsigned int index)
 {  {
    assert(opt && opt->type == CFGT_SEC);        if (!opt || opt->type != CFGT_SEC) {
    if(opt->values && index < opt->nvalues)                errno = EINVAL;
        return opt->values[index]->section;                return NULL;
    return 0;        }
 
         if (opt->values && index < opt->nvalues)
                 return opt->values[index]->section;
 
         errno = ENOENT;
         return NULL;
 }  }
   
 DLLIMPORT cfg_t *cfg_getnsec(cfg_t *cfg, const char *name, unsigned int index)  DLLIMPORT cfg_t *cfg_getnsec(cfg_t *cfg, const char *name, unsigned int index)
 {  {
    return cfg_opt_getnsec(cfg_getopt(cfg, name), index);        return cfg_opt_getnsec(cfg_getopt(cfg, name), index);
 }  }
   
 DLLIMPORT cfg_t *cfg_opt_gettsec(cfg_opt_t *opt, const char *title)  DLLIMPORT cfg_t *cfg_opt_gettsec(cfg_opt_t *opt, const char *title)
 {  {
    unsigned int i, n;        long int i;
   
    assert(opt && title);        if (!opt || !title) {
    if(!is_set(CFGF_TITLE, opt->flags))                errno = EINVAL;
        return 0;                return NULL;
    n = cfg_opt_size(opt);        }
    for(i = 0; i < n; i++)
    {        if (!is_set(CFGF_TITLE, opt->flags)) {
        cfg_t *sec = cfg_opt_getnsec(opt, i);                errno = EINVAL;
        assert(sec && sec->title);                return NULL;
        if(is_set(CFGF_NOCASE, opt->flags))        }
        {
            if(strcasecmp(title, sec->title) == 0)        i = cfg_opt_gettsecidx(opt, title);
                return sec;        if (i >= 0)
        }                return cfg_opt_getnsec(opt, i);
        else
        {        errno = ENOENT;
            if(strcmp(title, sec->title) == 0)        return NULL;
                return sec; 
        } 
    } 
    return 0; 
 }  }
   
 DLLIMPORT cfg_t *cfg_gettsec(cfg_t *cfg, const char *name, const char *title)  DLLIMPORT cfg_t *cfg_gettsec(cfg_t *cfg, const char *name, const char *title)
 {  {
    return cfg_opt_gettsec(cfg_getopt(cfg, name), title);        return cfg_opt_gettsec(cfg_getopt(cfg, name), title);
 }  }
   
 DLLIMPORT cfg_t *cfg_getsec(cfg_t *cfg, const char *name)  DLLIMPORT cfg_t *cfg_getsec(cfg_t *cfg, const char *name)
 {  {
    return cfg_getnsec(cfg, name, 0);        cfg_opt_t *opt;
         long int index;
 
         opt = cfg_getopt_secidx(cfg, name, &index);
         return cfg_opt_getnsec(opt, index);
 }  }
   
 static cfg_value_t *cfg_addval(cfg_opt_t *opt)  static cfg_value_t *cfg_addval(cfg_opt_t *opt)
 {  {
    opt->values = realloc(opt->values,        void *ptr;
                                  (opt->nvalues+1) * sizeof(cfg_value_t *));
    assert(opt->values);        ptr = realloc(opt->values, (opt->nvalues + 1) * sizeof(cfg_value_t *));
    opt->values[opt->nvalues] = malloc(sizeof(cfg_value_t));        if (!ptr)
    memset(opt->values[opt->nvalues], 0, sizeof(cfg_value_t));                return NULL;
    return opt->values[opt->nvalues++];
         opt->values = ptr;
         opt->values[opt->nvalues] = calloc(1, sizeof(cfg_value_t));
         if (!opt->values[opt->nvalues])
                 return NULL;
 
         opt->flags |= CFGF_MODIFIED;
 
         return opt->values[opt->nvalues++];
 }  }
   
   static cfg_opt_t *cfg_addopt(cfg_t *cfg, char *key)
   {
           int num = cfg_num(cfg);
           cfg_opt_t *opts;
   
           opts = reallocarray(cfg->opts, num + 2, sizeof(cfg_opt_t));
           if (!opts)
                   return NULL;
   
           /* Write new opt to previous CFG_END() marker */
           cfg->opts = opts;
           cfg->opts[num].name = strdup(key);
           cfg->opts[num].type = CFGT_STR;
   
           if (!cfg->opts[num].name) {
                   free(opts);
                   return NULL;
           }
   
           /* Set new CFG_END() */
           memset(&cfg->opts[num + 1], 0, sizeof(cfg_opt_t));
   
           return &cfg->opts[num];
   }
   
 DLLIMPORT int cfg_numopts(cfg_opt_t *opts)  DLLIMPORT int cfg_numopts(cfg_opt_t *opts)
 {  {
    int n;        int n;
   
    for(n = 0; opts[n].name; n++)        for (n = 0; opts && opts[n].name; n++)
        /* do nothing */ ;                /* do nothing */ ;
    return n;        return n;
 }  }
   
   DLLIMPORT unsigned int cfg_num(cfg_t *cfg)
   {
           if (!cfg)
                   return 0;
   
           return (unsigned int)cfg_numopts(cfg->opts);
   }
   
 static cfg_opt_t *cfg_dupopt_array(cfg_opt_t *opts)  static cfg_opt_t *cfg_dupopt_array(cfg_opt_t *opts)
 {  {
    int i;        int i;
    cfg_opt_t *dupopts;        cfg_opt_t *dupopts;
    int n = cfg_numopts(opts);        int n = cfg_numopts(opts);
   
    dupopts = calloc(n+1, sizeof(cfg_opt_t));        dupopts = calloc(n + 1, sizeof(cfg_opt_t));
    memcpy(dupopts, opts, n * sizeof(cfg_opt_t));        if (!dupopts)
                 return NULL;
   
    for(i = 0; i < n; i++)        memcpy(dupopts, opts, n * sizeof(cfg_opt_t));
    { 
        dupopts[i].name = strdup(opts[i].name); 
        if(opts[i].type == CFGT_SEC && opts[i].subopts) 
            dupopts[i].subopts = cfg_dupopt_array(opts[i].subopts); 
   
        if(is_set(CFGF_LIST, opts[i].flags) || opts[i].type == CFGT_FUNC)        for (i = 0; i < n; i++) {
            dupopts[i].def.parsed = opts[i].def.parsed ? strdup(opts[i].def.parsed) : 0;                /* Clear dynamic ptrs, protecting the original on failure */
        else if(opts[i].type == CFGT_STR)                dupopts[i].name = NULL;
            dupopts[i].def.string = opts[i].def.string ? strdup(opts[i].def.string) : 0;                dupopts[i].subopts = NULL;
    }                dupopts[i].def.parsed = NULL;
                 dupopts[i].def.string = NULL;
                 dupopts[i].comment = NULL;
         }
   
    return dupopts;        for (i = 0; i < n; i++) {
                 dupopts[i].name = strdup(opts[i].name);
                 if (!dupopts[i].name)
                         goto err;
 
                 if (opts[i].subopts) {
                         dupopts[i].subopts = cfg_dupopt_array(opts[i].subopts);
                         if (!dupopts[i].subopts)
                                 goto err;
                 }
 
                 if (opts[i].def.parsed) {
                         dupopts[i].def.parsed = strdup(opts[i].def.parsed);
                         if (!dupopts[i].def.parsed)
                                 goto err;
                 }
 
                 if (opts[i].def.string) {
                         dupopts[i].def.string = strdup(opts[i].def.string);
                         if (!dupopts[i].def.string)
                                 goto err;
                 }
 
                 if (opts[i].comment) {
                         dupopts[i].comment = strdup(opts[i].comment);
                         if (!dupopts[i].comment)
                                 goto err;
                 }
         }
 
         return dupopts;
 err:
         cfg_free_opt_array(dupopts);
         return NULL;
 }  }
   
 DLLIMPORT int cfg_parse_boolean(const char *s)  DLLIMPORT int cfg_parse_boolean(const char *s)
 {  {
    if(strcasecmp(s, "true") == 0        if (!s) {
       || strcasecmp(s, "on") == 0                errno = EINVAL;
       || strcasecmp(s, "yes") == 0)                return CFG_FAIL;
        return 1;        }
    else if(strcasecmp(s, "false") == 0
            || strcasecmp(s, "off") == 0        if (strcasecmp(s, "true") == 0 || strcasecmp(s, "on") == 0 || strcasecmp(s, "yes") == 0)
            || strcasecmp(s, "no") == 0)                return 1;
        return 0;        if (strcasecmp(s, "false") == 0 || strcasecmp(s, "off") == 0 || strcasecmp(s, "no") == 0)
    return -1;                return 0;
 
         return CFG_FAIL;
 }  }
   
 static void cfg_init_defaults(cfg_t *cfg)  static void cfg_init_defaults(cfg_t *cfg)
 {  {
    int i;        int i;
   
    for(i = 0; cfg->opts[i].name; i++)        for (i = 0; cfg->opts && cfg->opts[i].name; i++) {
    {                int j;
        /* libConfuse doesn't handle default values for "simple" options */ 
        if(cfg->opts[i].simple_value || is_set(CFGF_NODEFAULT, cfg->opts[i].flags)) 
            continue; 
   
        if(cfg->opts[i].type != CFGT_SEC)                for (j = 0; j < i; ++j) {
        {                        if (is_set(CFGF_NOCASE, cfg->opts[i].flags | cfg->opts[j].flags)) {
            cfg->opts[i].flags |= CFGF_DEFINIT;                                if (strcasecmp(cfg->opts[i].name, cfg->opts[j].name))
                                         continue;
                         } else {
                                 if (strcmp(cfg->opts[i].name, cfg->opts[j].name))
                                         continue;
                         }
                         /*
                          * There are two definitions of the same option name.
                          * What to do? It's a programming error and not an end
                          * user input error. Lets print a message and abort...
                          */
                         cfg_error(cfg, _("duplicate option '%s' not allowed"),
                                 cfg->opts[i].name);
                         break;
                 }
   
            if(is_set(CFGF_LIST, cfg->opts[i].flags) ||                /* libConfuse doesn't handle default values for "simple" options */
               cfg->opts[i].def.parsed)                if (cfg->opts[i].simple_value.ptr || is_set(CFGF_NODEFAULT, cfg->opts[i].flags))
            {                        continue;
                int xstate, ret; 
   
                /* If it's a list, but no default value was given,                if (cfg->opts[i].type != CFGT_SEC) {
                 * keep the option uninitialized.                        cfg->opts[i].flags |= CFGF_DEFINIT;
                 */ 
                if(cfg->opts[i].def.parsed == 0 || 
                   cfg->opts[i].def.parsed[0] == 0) 
                    continue; 
   
                /* setup scanning from the string specified for the                        if (is_set(CFGF_LIST, cfg->opts[i].flags) || cfg->opts[i].def.parsed) {
                 * "default" value, force the correct state and option                                int xstate, ret = 0;
                 */                                char *buf;
                                 FILE *fp;
   
                if(is_set(CFGF_LIST, cfg->opts[i].flags))                                /* If it's a list, but no default value was given,
                    /* lists must be surrounded by {braces} */                                 * keep the option uninitialized.
                    xstate = 3;                                 */
                else if(cfg->opts[i].type == CFGT_FUNC)                                buf = cfg->opts[i].def.parsed;
                    xstate = 0;                                if (!buf || !buf[0])
                else                                        continue;
                    xstate = 2; 
   
                cfg_scan_string_begin(cfg->opts[i].def.parsed);                                /* setup scanning from the string specified for the
                do                                 * "default" value, force the correct state and option
                {                                 */
                    ret = cfg_parse_internal(cfg, 1, xstate, &cfg->opts[i]); 
                    xstate = -1; 
                } while(ret == STATE_CONTINUE); 
                cfg_scan_string_end(); 
                if(ret == STATE_ERROR) 
                { 
                    /* 
                     * If there was an error parsing the default string, 
                     * the initialization of the default value could be 
                     * inconsistent or empty. What to do? It's a 
                     * programming error and not an end user input 
                     * error. Lets print a message and abort... 
                     */ 
                    fprintf(stderr, "Parse error in default value '%s'" 
                            " for option '%s'\n", 
                            cfg->opts[i].def.parsed, cfg->opts[i].name); 
                    fprintf(stderr, "Check your initialization macros and the" 
                            " libConfuse documentation\n"); 
                    abort(); 
                } 
            } 
            else 
            { 
                switch(cfg->opts[i].type) 
                { 
                    case CFGT_INT: 
                        cfg_opt_setnint(&cfg->opts[i], 
                                        cfg->opts[i].def.number, 0); 
                        break; 
                    case CFGT_FLOAT: 
                        cfg_opt_setnfloat(&cfg->opts[i], 
                                          cfg->opts[i].def.fpnumber, 0); 
                        break; 
                    case CFGT_BOOL: 
                        cfg_opt_setnbool(&cfg->opts[i], 
                                         cfg->opts[i].def.boolean, 0); 
                        break; 
                    case CFGT_STR: 
                        cfg_opt_setnstr(&cfg->opts[i], 
                                        cfg->opts[i].def.string, 0); 
                        break; 
                    case CFGT_FUNC: 
                    case CFGT_PTR: 
                        break; 
                    default: 
                        cfg_error(cfg, 
                                  "internal error in cfg_init_defaults(%s)", 
                                  cfg->opts[i].name); 
                        break; 
                } 
            } 
   
            /* The default value should only be returned if no value                                if (is_set(CFGF_LIST, cfg->opts[i].flags))
             * is given in the configuration file, so we set the RESET                                        /* lists must be surrounded by {braces} */
             * flag here. When/If cfg_setopt() is called, the value(s)                                        xstate = 3;
             * will be freed and the flag unset.                                else if (cfg->opts[i].type == CFGT_FUNC)
             */                                        xstate = 0;
            cfg->opts[i].flags |= CFGF_RESET;                                else
        } /* end if cfg->opts[i].type != CFGT_SEC */                                        xstate = 2;
        else if(!is_set(CFGF_MULTI, cfg->opts[i].flags))
        {                                fp = fmemopen(buf, strlen(buf), "r");
            cfg_setopt(cfg, &cfg->opts[i], 0);                                if (!fp) {
            cfg->opts[i].flags |= CFGF_DEFINIT;                                        /*
        }                                         * fmemopen() on older GLIBC versions do not accept zero
    }                                         * length buffers for some reason.  This is a workaround.
                                          */
                                         if (strlen(buf) > 0)
                                                 ret = STATE_ERROR;
                                 } else {
                                         cfg_scan_fp_begin(fp);
 
                                         do {
                                                 ret = cfg_parse_internal(cfg, 1, xstate, &cfg->opts[i]);
                                                 xstate = -1;
                                         } while (ret == STATE_CONTINUE);
 
                                         cfg_scan_fp_end();
                                         fclose(fp);
                                 }
 
                                 if (ret == STATE_ERROR) {
                                         /*
                                          * If there was an error parsing the default string,
                                          * the initialization of the default value could be
                                          * inconsistent or empty. What to do? It's a
                                          * programming error and not an end user input
                                          * error. Lets print a message and abort...
                                          */
                                         fprintf(stderr, "Parse error in default value '%s'"
                                                 " for option '%s'\n", cfg->opts[i].def.parsed, cfg->opts[i].name);
                                         fprintf(stderr, "Check your initialization macros and the" " libConfuse documentation\n");
                                         abort();
                                 }
                         } else {
                                 switch (cfg->opts[i].type) {
                                 case CFGT_INT:
                                         cfg_opt_setnint(&cfg->opts[i], cfg->opts[i].def.number, 0);
                                         break;
 
                                 case CFGT_FLOAT:
                                         cfg_opt_setnfloat(&cfg->opts[i], cfg->opts[i].def.fpnumber, 0);
                                         break;
 
                                 case CFGT_BOOL:
                                         cfg_opt_setnbool(&cfg->opts[i], cfg->opts[i].def.boolean, 0);
                                         break;
 
                                 case CFGT_STR:
                                         cfg_opt_setnstr(&cfg->opts[i], cfg->opts[i].def.string, 0);
                                         break;
 
                                 case CFGT_FUNC:
                                 case CFGT_PTR:
                                         break;
 
                                 default:
                                         cfg_error(cfg, "internal error in cfg_init_defaults(%s)", cfg->opts[i].name);
                                         break;
                                 }
                         }
 
                         /* The default value should only be returned if no value
                          * is given in the configuration file, so we set the RESET
                          * flag here. When/If cfg_setopt() is called, the value(s)
                          * will be freed and the flag unset.
                          */
                         cfg->opts[i].flags |= CFGF_RESET;
                         cfg->opts[i].flags &= ~CFGF_MODIFIED;
                 } else if (!is_set(CFGF_MULTI, cfg->opts[i].flags)) {
                         cfg_setopt(cfg, &cfg->opts[i], 0);
                         cfg->opts[i].flags |= CFGF_DEFINIT;
                 }
         }
 }  }
   
DLLIMPORT cfg_value_t *DLLIMPORT cfg_value_t *cfg_setopt(cfg_t *cfg, cfg_opt_t *opt, const char *value)
cfg_setopt(cfg_t *cfg, cfg_opt_t *opt, char *value) 
 {  {
    cfg_value_t *val = 0;        cfg_value_t *val = NULL;
    int b;        int b;
    char *s;        const char *s;
    double f;        double f;
    long int i;        long int i;
    void *p;        void *p;
    char *endptr;        char *endptr;
   
    assert(cfg && opt);        if (!cfg || !opt) {
                 errno = EINVAL;
                 return NULL;
         }
   
    if(opt->simple_value)        if (opt->simple_value.ptr) {
    {                if (opt->type == CFGT_SEC) {
        assert(opt->type != CFGT_SEC);                        errno = EINVAL;
        val = (cfg_value_t *)opt->simple_value;                        return NULL;
    }                }
    else                val = (cfg_value_t *)opt->simple_value.ptr;
    {        } else {
        if(is_set(CFGF_RESET, opt->flags))                if (is_set(CFGF_RESET, opt->flags)) {
        {                        cfg_free_value(opt);
            cfg_free_value(opt);                        opt->flags &= ~CFGF_RESET;
            opt->flags &= ~CFGF_RESET;                }
        } 
   
        if(opt->nvalues == 0 || is_set(CFGF_MULTI, opt->flags) ||                if (opt->nvalues == 0 || is_set(CFGF_MULTI, opt->flags) || is_set(CFGF_LIST, opt->flags)) {
           is_set(CFGF_LIST, opt->flags))                        val = NULL;
        { 
            val = 0; 
            if(opt->type == CFGT_SEC && is_set(CFGF_TITLE, opt->flags)) 
            { 
                unsigned int i; 
   
                /* Check if there already is a section with the same title.                        if (opt->type == CFGT_SEC && is_set(CFGF_TITLE, opt->flags)) {
                 */                                unsigned int i;
   
                /* Assert that there are either no sections at all, or a                                /* XXX: Check if there already is a section with the same title. */
                 * non-NULL section title. */ 
                assert(opt->nvalues == 0 || value); 
   
                for(i = 0; i < opt->nvalues && val == NULL; i++)                                /*
                {                                 * Check there are either no sections at
                    cfg_t *sec = opt->values[i]->section;                                 * all, or a non-NULL section title.
                    if(is_set(CFGF_NOCASE, cfg->flags))                                 */
                    {                                if (opt->nvalues != 0 && !value) {
                        if(strcasecmp(value, sec->title) == 0)                                        errno = EINVAL;
                            val = opt->values[i];                                        return NULL;
                    }                                }
                    else 
                    { 
                        if(strcmp(value, sec->title) == 0) 
                            val = opt->values[i]; 
                    } 
                } 
                if(val && is_set(CFGF_NO_TITLE_DUPES, opt->flags)) 
                { 
                    cfg_error(cfg, _("found duplicate title '%s'"), value); 
                    return 0; 
                } 
            } 
            if(val == NULL) 
                val = cfg_addval(opt); 
        } 
        else 
            val = opt->values[0]; 
    } 
   
    switch(opt->type)                                for (i = 0; i < opt->nvalues && val == NULL; i++) {
    {                                        cfg_t *sec = opt->values[i]->section;
        case CFGT_INT: 
            if(opt->parsecb) 
            { 
                if((*opt->parsecb)(cfg, opt, value, &i) != 0) 
                    return 0; 
                val->number = i; 
            } 
            else 
            { 
                val->number = strtol(value, &endptr, 0); 
                if(*endptr != '\0') 
                { 
                    cfg_error(cfg, _("invalid integer value for option '%s'"), 
                              opt->name); 
                    return 0; 
                } 
                if(errno == ERANGE) 
                { 
                    cfg_error(cfg, 
                          _("integer value for option '%s' is out of range"), 
                              opt->name); 
                    return 0; 
                } 
            } 
            break; 
   
        case CFGT_FLOAT:                                        if (is_set(CFGF_NOCASE, cfg->flags)) {
            if(opt->parsecb)                                                if (strcasecmp(value, sec->title) == 0)
            {                                                        val = opt->values[i];
                if((*opt->parsecb)(cfg, opt, value, &f) != 0)                                        } else {
                    return 0;                                                if (strcmp(value, sec->title) == 0)
                val->fpnumber = f;                                                        val = opt->values[i];
            }                                        }
            else                                }
            { 
                val->fpnumber = strtod(value, &endptr); 
                if(*endptr != '\0') 
                { 
                    cfg_error(cfg, 
                          _("invalid floating point value for option '%s'"), 
                              opt->name); 
                    return 0; 
                } 
                if(errno == ERANGE) 
                { 
                    cfg_error(cfg, 
                  _("floating point value for option '%s' is out of range"), 
                              opt->name); 
                    return 0; 
                } 
            } 
            break; 
   
        case CFGT_STR:                                if (val && is_set(CFGF_NO_TITLE_DUPES, opt->flags)) {
            free(val->string);                                        cfg_error(cfg, _("found duplicate title '%s'"), value);
            if(opt->parsecb)                                        return NULL;
            {                                }
                s = 0;                        }
                if((*opt->parsecb)(cfg, opt, value, &s) != 0) 
                    return 0; 
                val->string = strdup(s); 
            } 
            else 
                val->string = strdup(value); 
            break; 
   
        case CFGT_SEC:                        if (!val) {
            if(is_set(CFGF_MULTI, opt->flags) || val->section == 0)                                val = cfg_addval(opt);
            {                                if (!val)
                cfg_free(val->section);                                        return NULL;
                val->section = calloc(1, sizeof(cfg_t));                        }
                assert(val->section);                } else {
                val->section->name = strdup(opt->name);                        val = opt->values[0];
                val->section->opts = cfg_dupopt_array(opt->subopts);                }
                val->section->flags = cfg->flags;        }
                val->section->filename = cfg->filename ? strdup(cfg->filename) : 0; 
                val->section->line = cfg->line; 
                val->section->errfunc = cfg->errfunc; 
                val->section->title = value; 
            } 
            if(!is_set(CFGF_DEFINIT, opt->flags)) 
                cfg_init_defaults(val->section); 
            break; 
   
        case CFGT_BOOL:        switch (opt->type) {
            if(opt->parsecb)        case CFGT_INT:
            {                if (opt->parsecb) {
                if((*opt->parsecb)(cfg, opt, value, &b) != 0)                        if ((*opt->parsecb) (cfg, opt, value, &i) != 0)
                    return 0;                                return NULL;
            }                } else {
            else                        if (!value) {
            {                                errno = EINVAL;
                b = cfg_parse_boolean(value);                                return NULL;
                if(b == -1)                        }
                {                        i = strtol(value, &endptr, 0);
                    cfg_error(cfg, _("invalid boolean value for option '%s'"),                        if (*endptr != '\0') {
                              opt->name);                                cfg_error(cfg, _("invalid integer value for option '%s'"), opt->name);
                    return 0;                                return NULL;
                }                        }
            }                        if (errno == ERANGE) {
            val->boolean = (cfg_bool_t)b;                                cfg_error(cfg, _("integer value for option '%s' is out of range"), opt->name);
            break;                                return NULL;
                         }
                 }
                 val->number = i;
                 break;
   
        case CFGT_PTR:        case CFGT_FLOAT:
            assert(opt->parsecb);                if (opt->parsecb) {
            if((*opt->parsecb)(cfg, opt, value, &p) != 0)                        if ((*opt->parsecb) (cfg, opt, value, &f) != 0)
                return 0;                                return NULL;
            val->ptr = p;                } else {
            break;                        if (!value) {
                                 errno = EINVAL;
                                 return NULL;
                         }
                         f = strtod(value, &endptr);
                         if (*endptr != '\0') {
                                 cfg_error(cfg, _("invalid floating point value for option '%s'"), opt->name);
                                 return NULL;
                         }
                         if (errno == ERANGE) {
                                 cfg_error(cfg, _("floating point value for option '%s' is out of range"), opt->name);
                                 return NULL;
                         }
                 }
                 val->fpnumber = f;
                 break;
   
        default:        case CFGT_STR:
            cfg_error(cfg, "internal error in cfg_setopt(%s, %s)",                if (opt->parsecb) {
                      opt->name, value);                        s = NULL;
            assert(0);                        if ((*opt->parsecb) (cfg, opt, value, &s) != 0)
            break;                                return NULL;
    }                } else {
    return val;                        s = value;
                 }
 
                 if (!s) {
                         errno = EINVAL;
                         return NULL;
                 }
 
                 free(val->string);
                 val->string = strdup(s);
                 if (!val->string)
                         return NULL;
                 break;
 
         case CFGT_SEC:
                 if (is_set(CFGF_MULTI, opt->flags) || val->section == 0) {
                         if (val->section) {
                                 val->section->path = NULL; /* Global search path */
                                 cfg_free(val->section);
                         }
                         val->section = calloc(1, sizeof(cfg_t));
                         if (!val->section)
                                 return NULL;
 
                         val->section->name = strdup(opt->name);
                         if (!val->section->name) {
                                 free(val->section);
                                 return NULL;
                         }
 
                         val->section->flags = cfg->flags;
                         if (is_set(CFGF_KEYSTRVAL, opt->flags))
                                 val->section->flags |= CFGF_KEYSTRVAL;
 
                         val->section->filename = cfg->filename ? strdup(cfg->filename) : NULL;
                         if (cfg->filename && !val->section->filename) {
                                 free(val->section->name);
                                 free(val->section);
                                 return NULL;
                         }
 
                         val->section->line = cfg->line;
                         val->section->errfunc = cfg->errfunc;
                         val->section->title = value ? strdup(value) : NULL;
                         if (value && !val->section->title) {
                                 free(val->section->filename);
                                 free(val->section->name);
                                 free(val->section);
                                 return NULL;
                         }
 
                         val->section->opts = cfg_dupopt_array(opt->subopts);
                         if (!val->section->opts) {
                                 if (val->section->title)
                                         free(val->section->title);
                                 if (val->section->filename)
                                         free(val->section->filename);
                                 free(val->section->name);
                                 free(val->section);
                                 return NULL;
                         }
                 }
                 if (!is_set(CFGF_DEFINIT, opt->flags))
                         cfg_init_defaults(val->section);
                 break;
 
         case CFGT_BOOL:
                 if (opt->parsecb) {
                         if ((*opt->parsecb) (cfg, opt, value, &b) != 0)
                                 return NULL;
                 } else {
                         b = cfg_parse_boolean(value);
                         if (b == -1) {
                                 cfg_error(cfg, _("invalid boolean value for option '%s'"), opt->name);
                                 return NULL;
                         }
                 }
                 val->boolean = (cfg_bool_t)b;
                 break;
 
         case CFGT_PTR:
                 if (!opt->parsecb) {
                         errno = EINVAL;
                         return NULL;
                 }
 
                 if ((*opt->parsecb) (cfg, opt, value, &p) != 0)
                         return NULL;
                 if (val->ptr && opt->freecb)
                         opt->freecb(val->ptr);
                 val->ptr = p;
                 break;
 
         default:
                 cfg_error(cfg, "internal error in cfg_setopt(%s, %s)", opt->name, (value) ? (value) : "NULL");
                 return NULL;
         }
 
         opt->flags |= CFGF_MODIFIED;
 
         return val;
 }  }
   
DLLIMPORT cfg_errfunc_t cfg_set_error_function(cfg_t *cfg,DLLIMPORT int cfg_opt_setmulti(cfg_t *cfg, cfg_opt_t *opt, unsigned int nvalues, char **values)
                                               cfg_errfunc_t errfunc) 
 {  {
    cfg_errfunc_t old;        cfg_opt_t old;
         unsigned int i;
   
    assert(cfg);        if (!opt || !nvalues) {
    old = cfg->errfunc;                errno = EINVAL;
    cfg->errfunc = errfunc;                return CFG_FAIL;
    return old;        }
 
         old = *opt;
         opt->nvalues = 0;
         opt->values = 0;
 
         for (i = 0; i < nvalues; i++) {
                 if (cfg_setopt(cfg, opt, values[i]))
                         continue;
 
                 /* ouch, revert */
                 cfg_free_value(opt);
                 opt->nvalues = old.nvalues;
                 opt->values = old.values;
                 opt->flags &= ~(CFGF_RESET | CFGF_MODIFIED);
                 opt->flags |= old.flags & (CFGF_RESET | CFGF_MODIFIED);
 
                 return CFG_FAIL;
         }
 
         cfg_free_value(&old);
         opt->flags |= CFGF_MODIFIED;
 
         return CFG_SUCCESS;
 }  }
   
   DLLIMPORT int cfg_setmulti(cfg_t *cfg, const char *name, unsigned int nvalues, char **values)
   {
           cfg_opt_t *opt;
   
           if (!cfg || !name || !values) {
                   errno = EINVAL;
                   return CFG_FAIL;
           }
   
           opt = cfg_getopt(cfg, name);
           if (!opt) {
                   errno = ENOENT;
                   return CFG_FAIL;
           }
   
           return cfg_opt_setmulti(cfg, opt, nvalues, values);
   }
   
   /* searchpath */
   
   struct cfg_searchpath_t {
           char *dir;              /**< directory to search */
           cfg_searchpath_t *next; /**< next in list */
   };
   
   /* prepend a new cfg_searchpath_t to the linked list */
   
   DLLIMPORT int cfg_add_searchpath(cfg_t *cfg, const char *dir)
   {
           cfg_searchpath_t *p;
           char *d;
   
           if (!cfg || !dir) {
                   errno = EINVAL;
                   return CFG_FAIL;
           }
   
           d = cfg_tilde_expand(dir);
           if (!d)
                   return CFG_FAIL;
   
           p = malloc(sizeof(cfg_searchpath_t));
           if (!p) {
                   free(d);
                   return CFG_FAIL;
           }
   
           p->next   = cfg->path;
           p->dir    = d;
           cfg->path = p;
   
           return CFG_SUCCESS;
   }
   
   DLLIMPORT cfg_errfunc_t cfg_set_error_function(cfg_t *cfg, cfg_errfunc_t errfunc)
   {
           cfg_errfunc_t old;
   
           if (!cfg) {
                   errno = EINVAL;
                   return NULL;
           }
   
           old = cfg->errfunc;
           cfg->errfunc = errfunc;
   
           return old;
   }
   
   DLLIMPORT cfg_print_filter_func_t cfg_set_print_filter_func(cfg_t *cfg, cfg_print_filter_func_t pff)
   {
           cfg_print_filter_func_t old;
   
           if (!cfg) {
                   errno = EINVAL;
                   return NULL;
           }
   
           old = cfg->pff;
           cfg->pff = pff;
   
           return old;
   }
   
 DLLIMPORT void cfg_error(cfg_t *cfg, const char *fmt, ...)  DLLIMPORT void cfg_error(cfg_t *cfg, const char *fmt, ...)
 {  {
    va_list ap;        va_list ap;
   
    va_start(ap, fmt);        va_start(ap, fmt);
   
    if(cfg && cfg->errfunc)        if (cfg && cfg->errfunc)
        (*cfg->errfunc)(cfg, fmt, ap);                (*cfg->errfunc) (cfg, fmt, ap);
    else        else {
    {                if (cfg && cfg->filename && cfg->line)
        if(cfg && cfg->filename && cfg->line)                        fprintf(stderr, "%s:%d: ", cfg->filename, cfg->line);
            fprintf(stderr, "%s:%d: ", cfg->filename, cfg->line);                else if (cfg && cfg->filename)
        else if(cfg && cfg->filename)                        fprintf(stderr, "%s: ", cfg->filename);
            fprintf(stderr, "%s: ", cfg->filename);                vfprintf(stderr, fmt, ap);
        vfprintf(stderr, fmt, ap);                fprintf(stderr, "\n");
        fprintf(stderr, "\n");        }
    } 
   
    va_end(ap);        va_end(ap);
 }  }
   
 static int call_function(cfg_t *cfg, cfg_opt_t *opt, cfg_opt_t *funcopt)  static int call_function(cfg_t *cfg, cfg_opt_t *opt, cfg_opt_t *funcopt)
 {  {
    int ret;        int ret;
    const char **argv;        const char **argv;
    unsigned int i;        unsigned int i;
   
    /* create a regular argv string vector and call        if (!cfg || !opt ||!funcopt) {
     * the registered function                errno = EINVAL;
     */                return CFG_FAIL;
    argv = calloc(funcopt->nvalues, sizeof(char *));        }
    for(i = 0; i < funcopt->nvalues; i++)                
        argv[i] = funcopt->values[i]->string;        /*
    ret = (*opt->func)(cfg, opt, funcopt->nvalues, argv);         * create am argv string vector and call the registered function
    cfg_free_value(funcopt);         */
    free(argv);        argv = calloc(funcopt->nvalues, sizeof(char *));
    return ret;        if (!argv)
                 return CFG_FAIL;
 
         for (i = 0; i < funcopt->nvalues; i++)
                 argv[i] = funcopt->values[i]->string;
 
         ret = (*opt->func) (cfg, opt, funcopt->nvalues, argv);
         cfg_free_value(funcopt);
         free(argv);
 
         return ret;
 }  }
   
static int cfg_parse_internal(cfg_t *cfg, int level,static void cfg_handle_deprecated(cfg_t *cfg, cfg_opt_t *opt)
                              int force_state, cfg_opt_t *force_opt) 
 {  {
    int state = 0;        if (is_set(CFGF_DROP, opt->flags)) {
    char *opttitle = 0;                cfg_error(cfg, _("dropping deprecated configuration option '%s'"), opt->name);
    cfg_opt_t *opt = 0;                cfg_free_value(opt);
    cfg_value_t *val = 0;        } else {
    cfg_opt_t funcopt = CFG_STR(0, 0, 0);                cfg_error(cfg, _("found deprecated option '%s', please update configuration file."), opt->name);
    int num_values = 0; /* number of values found for a list option */        }
    int rc;}
   
    if(force_state != -1)static int cfg_parse_internal(cfg_t *cfg, int level, int force_state, cfg_opt_t *force_opt)
        state = force_state;{
    if(force_opt)        int state = 0;
        opt = force_opt;        char *comment = NULL;
         char *opttitle = NULL;
         cfg_opt_t *opt = NULL;
         cfg_value_t *val = NULL;
         cfg_opt_t funcopt = CFG_STR(0, 0, 0);
         int ignore = 0;         /* ignore until this token, traverse parser w/o error */
         int num_values = 0;     /* number of values found for a list option */
         int rc;
   
    while(1)        if (force_state != -1)
    {                state = force_state;
        int tok = cfg_yylex(cfg);        if (force_opt)
                 opt = force_opt;
   
        if(tok == 0)        while (1) {
        {                int tok = cfg_yylex(cfg);
            /* lexer.l should have called cfg_error */ 
            return STATE_ERROR; 
        } 
   
        if(tok == EOF)                if (tok == 0) {
        {                        /* lexer.l should have called cfg_error() */
            if(state != 0)                        goto error;
            {                }
                cfg_error(cfg, _("premature end of file")); 
                return STATE_ERROR; 
            } 
            return STATE_EOF; 
        } 
   
        switch(state)                if (tok == EOF) {
        {                        if (state != 0) {
            case 0: /* expecting an option name */                                cfg_error(cfg, _("premature end of file"));
                if(tok == '}')                                goto error;
                {                        }
                    if(level == 0) 
                    { 
                        cfg_error(cfg, _("unexpected closing brace")); 
                        return STATE_ERROR; 
                    } 
                    return STATE_EOF; 
                } 
                if(tok != CFGT_STR) 
                { 
                    cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval); 
                    return STATE_ERROR; 
                } 
                opt = cfg_getopt(cfg, cfg_yylval); 
                if(opt == 0) 
                    return STATE_ERROR; 
                if(opt->type == CFGT_SEC) 
                { 
                    if(is_set(CFGF_TITLE, opt->flags)) 
                        state = 6; 
                    else 
                        state = 5; 
                } 
                else if(opt->type == CFGT_FUNC) 
                { 
                    state = 7; 
                } 
                else 
                    state = 1; 
                break; 
   
            case 1: /* expecting an equal sign or plus-equal sign */                        if (opt && is_set(CFGF_DEPRECATED, opt->flags))
                if(tok == '+')                                cfg_handle_deprecated(cfg, opt);
                { 
                    if(!is_set(CFGF_LIST, opt->flags)) 
                    { 
                        cfg_error(cfg, 
                                  _("attempt to append to non-list option '%s'"), 
                                  opt->name); 
                        return STATE_ERROR; 
                    } 
                    /* Even if the reset flag was set by 
                     * cfg_init_defaults, appending to the defaults 
                     * should be ok. 
                     */ 
                    opt->flags &= ~CFGF_RESET; 
                } 
                else if(tok == '=') 
                { 
                    /* set the (temporary) reset flag to clear the old 
                     * values, since we obviously didn't want to append */ 
                    opt->flags |= CFGF_RESET; 
                } 
                else 
                { 
                    cfg_error(cfg, _("missing equal sign after option '%s'"), 
                              opt->name); 
                    return STATE_ERROR; 
                } 
                if(is_set(CFGF_LIST, opt->flags)) 
                { 
                    state = 3; 
                    num_values = 0; 
                } else 
                    state = 2; 
                break; 
   
            case 2: /* expecting an option value */                        if (comment)
                if(tok == '}' && is_set(CFGF_LIST, opt->flags))                                free(comment);
                { 
                    state = 0; 
                    if(num_values == 0 && is_set(CFGF_RESET, opt->flags)) 
                        /* Reset flags was set, and the empty list was 
                         * specified. Free all old values. */ 
                        cfg_free_value(opt); 
                    break; 
                } 
   
                if(tok != CFGT_STR)                        return STATE_EOF;
                {                }
                    cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval); 
                    return STATE_ERROR; 
                } 
   
                if(cfg_setopt(cfg, opt, cfg_yylval) == 0)                switch (state) {
                    return STATE_ERROR;                case 0: /* expecting an option name */
                if(opt->validcb && (*opt->validcb)(cfg, opt) != 0)                        if (opt && is_set(CFGF_DEPRECATED, opt->flags))
                    return STATE_ERROR;                                cfg_handle_deprecated(cfg, opt);
                if(is_set(CFGF_LIST, opt->flags)) 
                { 
                    ++num_values; 
                    state = 4; 
                } 
                else 
                    state = 0; 
                break; 
   
            case 3: /* expecting an opening brace for a list option */                        switch (tok) {
                if(tok != '{')                        case '}':
                {                                if (level == 0) {
                    if(tok != CFGT_STR)                                        cfg_error(cfg, _("unexpected closing brace"));
                    {                                        goto error;
                        cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);                                }
                        return STATE_ERROR;                                if (comment)
                    }                                        free(comment);
   
                    if(cfg_setopt(cfg, opt, cfg_yylval) == 0)                                return STATE_EOF;
                        return STATE_ERROR; 
                    if(opt->validcb && (*opt->validcb)(cfg, opt) != 0) 
                        return STATE_ERROR; 
                    ++num_values; 
                    state = 0; 
                } 
                else 
                    state = 2; 
                break; 
   
            case 4: /* expecting a separator for a list option, or                        case CFGT_STR:
                     * closing (list) brace */                                break;
                if(tok == ',') 
                    state = 2; 
                else if(tok == '}') 
                { 
                    state = 0; 
                    if(opt->validcb && (*opt->validcb)(cfg, opt) != 0) 
                        return STATE_ERROR; 
                } 
                else 
                { 
                    cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval); 
                    return STATE_ERROR; 
                } 
                break; 
   
            case 5: /* expecting an opening brace for a section */                        case CFGT_COMMENT:
                if(tok != '{')                                if (!is_set(CFGF_COMMENTS, cfg->flags))
                {                                        continue;
                    cfg_error(cfg, _("missing opening brace for section '%s'"), 
                              opt->name); 
                    return STATE_ERROR; 
                } 
   
                val = cfg_setopt(cfg, opt, opttitle);                                if (comment)
                opttitle = 0;                                        free(comment);
                if(!val)                                comment = strdup(cfg_yylval);
                    return STATE_ERROR;                                continue;
   
                val->section->line = cfg->line;                        default:
                val->section->errfunc = cfg->errfunc;                                cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
                rc = cfg_parse_internal(val->section, level+1,-1,0);                                goto error;
                cfg->line = val->section->line;                        }
                if(rc != STATE_EOF) 
                    return STATE_ERROR; 
                if(opt->validcb && (*opt->validcb)(cfg, opt) != 0) 
                    return STATE_ERROR; 
                state = 0; 
                break; 
   
            case 6: /* expecting a title for a section */                        opt = cfg_getopt(cfg, cfg_yylval);
                if(tok != CFGT_STR)                        if (!opt) {
                {                                if (is_set(CFGF_IGNORE_UNKNOWN, cfg->flags)) {
                    cfg_error(cfg, _("missing title for section '%s'"),                                        state = 10;
                              opt->name);                                        break;
                    return STATE_ERROR;                                }
                } 
                else 
                    opttitle = strdup(cfg_yylval); 
                state = 5; 
                break; 
   
            case 7: /* expecting an opening parenthesis for a function */                                /* Not found, is it a dynamic key-value section? */
                if(tok != '(')                                if (is_set(CFGF_KEYSTRVAL, cfg->flags)) {
                {                                        opt = cfg_addopt(cfg, cfg_yylval);
                    cfg_error(cfg, _("missing parenthesis for function '%s'"),                                        if (!opt)
                              opt->name);                                                goto error;
                    return STATE_ERROR; 
                } 
                state = 8; 
                break; 
   
            case 8: /* expecting a function parameter or a closing paren */                                        state = 1;
                if(tok == ')')                                        break;
                {                                }
                    int ret = call_function(cfg, opt, &funcopt); 
                    if(ret != 0) 
                        return STATE_ERROR; 
                    state = 0; 
                } 
                else if(tok == CFGT_STR) 
                { 
                    val = cfg_addval(&funcopt); 
                    val->string = strdup(cfg_yylval); 
                    state = 9; 
                } 
                else 
                { 
                    cfg_error(cfg, _("syntax error in call of function '%s'"), 
                              opt->name); 
                    return STATE_ERROR; 
                } 
                break; 
   
            case 9: /* expecting a comma in a function or a closing paren */                                goto error;
                if(tok == ')')                        }
                { 
                    int ret = call_function(cfg, opt, &funcopt); 
                    if(ret != 0) 
                        return STATE_ERROR; 
                    state = 0; 
                } 
                else if(tok == ',') 
                    state = 8; 
                else 
                { 
                    cfg_error(cfg, _("syntax error in call of function '%s'"), 
                              opt->name); 
                    return STATE_ERROR; 
                } 
                break; 
   
            default:                        if (opt->type == CFGT_SEC) {
                /* missing state, internal error, abort */                                if (is_set(CFGF_TITLE, opt->flags))
                assert(0);                                        state = 6;
        }                                else
    }                                        state = 5;
                         } else if (opt->type == CFGT_FUNC) {
                                 state = 7;
                         } else {
                                 state = 1;
                         }
                         break;
   
    return STATE_EOF;                case 1: /* expecting an equal sign or plus-equal sign */
                         if (!opt)
                                 goto error;
 
                         if (tok == '+') {
                                 if (!is_set(CFGF_LIST, opt->flags)) {
                                         cfg_error(cfg, _("attempt to append to non-list option '%s'"), opt->name);
                                         goto error;
                                 }
                                 /* Even if the reset flag was set by
                                  * cfg_init_defaults, appending to the defaults
                                  * should be ok.
                                  */
                                 opt->flags &= ~CFGF_RESET;
                         } else if (tok == '=') {
                                 /* set the (temporary) reset flag to clear the old
                                  * values, since we obviously didn't want to append */
                                 opt->flags |= CFGF_RESET;
                         } else {
                                 cfg_error(cfg, _("missing equal sign after option '%s'"), opt->name);
                                 goto error;
                         }
 
                         opt->flags |= CFGF_MODIFIED;
 
                         if (is_set(CFGF_LIST, opt->flags)) {
                                 state = 3;
                                 num_values = 0;
                         } else {
                                 state = 2;
                         }
                         break;
 
                 case 2: /* expecting an option value */
                         if (tok == '}' && opt && is_set(CFGF_LIST, opt->flags)) {
                                 state = 0;
                                 if (num_values == 0 && is_set(CFGF_RESET, opt->flags))
                                         /* Reset flags was set, and the empty list was
                                          * specified. Free all old values. */
                                         cfg_free_value(opt);
                                 break;
                         }
 
                         if (tok != CFGT_STR) {
                                 cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
                                 goto error;
                         }
 
                         if (cfg_setopt(cfg, opt, cfg_yylval) == 0)
                                 goto error;
 
                         if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
                                 goto error;
 
                         /* Inherit last read comment */
                         cfg_opt_setcomment(opt, comment);
                         if (comment)
                                 free(comment);
                         comment = NULL;
 
                         if (opt && is_set(CFGF_LIST, opt->flags)) {
                                 ++num_values;
                                 state = 4;
                         } else {
                                 state = 0;
                         }
                         break;
 
                 case 3: /* expecting an opening brace for a list option */
                         if (tok != '{') {
                                 if (tok != CFGT_STR) {
                                         cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
                                         goto error;
                                 }
 
                                 if (cfg_setopt(cfg, opt, cfg_yylval) == 0)
                                         goto error;
                                 if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
                                         goto error;
                                 ++num_values;
                                 state = 0;
                         } else {
                                 state = 2;
                         }
                         break;
 
                 case 4: /* expecting a separator for a list option, or closing (list) brace */
                         if (tok == ',') {
                                 state = 2;
                         } else if (tok == '}') {
                                 state = 0;
                                 if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
                                         goto error;
                         } else {
                                 cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
                                 goto error;
                         }
                         break;
 
                 case 5: /* expecting an opening brace for a section */
                         if (tok != '{') {
                                 cfg_error(cfg, _("missing opening brace for section '%s'"), opt ? opt->name : "");
                                 goto error;
                         }
 
                         val = cfg_setopt(cfg, opt, opttitle);
                         if (!val)
                                 goto error;
 
                         if (opttitle)
                                 free(opttitle);
                         opttitle = NULL;
 
                         val->section->path = cfg->path; /* Remember global search path */
                         val->section->line = cfg->line;
                         val->section->errfunc = cfg->errfunc;
                         rc = cfg_parse_internal(val->section, level + 1, -1, 0);
                         if (rc != STATE_EOF)
                                 goto error;
 
                         cfg->line = val->section->line;
                         if (opt && opt->validcb && (*opt->validcb) (cfg, opt) != 0)
                                 goto error;
                         state = 0;
                         break;
 
                 case 6: /* expecting a title for a section */
                         if (tok != CFGT_STR) {
                                 cfg_error(cfg, _("missing title for section '%s'"), opt ? opt->name : "");
                                 goto error;
                         } else {
                                 opttitle = strdup(cfg_yylval);
                                 if (!opttitle)
                                         goto error;
                         }
                         state = 5;
                         break;
 
                 case 7: /* expecting an opening parenthesis for a function */
                         if (tok != '(') {
                                 cfg_error(cfg, _("missing parenthesis for function '%s'"), opt ? opt->name : "");
                                 goto error;
                         }
                         state = 8;
                         break;
 
                 case 8: /* expecting a function parameter or a closing paren */
                         if (tok == ')') {
                                 if (call_function(cfg, opt, &funcopt))
                                         goto error;
                                 state = 0;
                         } else if (tok == CFGT_STR) {
                                 val = cfg_addval(&funcopt);
                                 if (!val)
                                         goto error;
 
                                 val->string = strdup(cfg_yylval);
                                 if (!val->string)
                                         goto error;
 
                                 state = 9;
                         } else {
                                 cfg_error(cfg, _("syntax error in call of function '%s'"), opt ? opt->name : "");
                                 goto error;
                         }
                         break;
 
                 case 9: /* expecting a comma in a function or a closing paren */
                         if (tok == ')') {
                                 if (call_function(cfg, opt, &funcopt))
                                         goto error;
                                 state = 0;
                         } else if (tok == ',') {
                                 state = 8;
                         } else {
                                 cfg_error(cfg, _("syntax error in call of function '%s'"), opt ? opt->name : "");
                                 goto error;
                         }
                         break;
 
                 case 10: /* unknown option, mini-discard parser states: 10-15 */
                         if (comment) {
                                 free(comment);
                                 comment = NULL;
                         }
 
                         if (tok == '+') {
                                 ignore = '=';
                                 state = 13; /* Append to list, should be followed by '=' */
                         } else if (tok == '=') {
                                 ignore = 0;
                                 state = 14; /* Assignment, regular handling */
                         } else if (tok == '(') {
                                 ignore = ')';
                                 state = 13; /* Function, ignore until end of param list */
                         } else if (tok == '{') {
                                 state = 12; /* Section, ignore all until closing brace */
                         } else if (tok == CFGT_STR) {
                                 state = 11; /* No '=' ... must be a titled section */
                         } else if (tok == '}' && force_state == 10) {
                                 if (comment)
                                         free(comment);
 
                                 return STATE_CONTINUE;
                         }
                         break;
 
                 case 11: /* unknown option, expecting start of title section */
                         if (tok != '{') {
                                 cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
                                 goto error;
                         }
                         state = 12;
                         break;
 
                 case 12: /* unknown option, recursively ignore entire sub-section */
                         rc = cfg_parse_internal(cfg, level + 1, 10, NULL);
                         if (rc != STATE_CONTINUE)
                                 goto error;
                         ignore = '}';
                         state = 13;
                         break;
 
                 case 13: /* unknown option, consume tokens silently until end of func/list */
                         if (tok != ignore)
                                 break;
 
                         if (ignore == '=') {
                                 ignore = 0;
                                 state = 14;
                                 break;
                         }
 
                         /* Are we done with recursive ignore of sub-section? */
                         if (force_state == 10) {
                                 if (comment)
                                         free(comment);
 
                                 return STATE_CONTINUE;
                         }
 
                         ignore = 0;
                         state = 0;
                         break;
 
                 case 14: /* unknown option, assuming value or start of list */
                         if (tok == '{') {
                                 ignore = '}';
                                 state = 13;
                                 break;
                         }
 
                         if (tok != CFGT_STR) {
                                 cfg_error(cfg, _("unexpected token '%s'"), cfg_yylval);
                                 goto error;
                         }
 
                         ignore = 0;
                         if (force_state == 10)
                                 state = 15;
                         else
                                 state = 0;
                         break;
 
                 case 15: /* unknown option, dummy read of next parameter in sub-section */
                         state = 10;
                         break;
 
                 default:
                         cfg_error(cfg, _("Internal error in cfg_parse_internal(), unknown state %d"), state);
                         goto error;
                 }
         }
 
         if (comment)
                 free(comment);
 
         return STATE_EOF;
 
 error:
         if (opttitle)
                 free(opttitle);
         if (comment)
                 free(comment);
 
         return STATE_ERROR;
 }  }
   
 DLLIMPORT int cfg_parse_fp(cfg_t *cfg, FILE *fp)  DLLIMPORT int cfg_parse_fp(cfg_t *cfg, FILE *fp)
 {  {
    int ret;        int ret;
    assert(cfg && fp); 
   
    if(cfg->filename == 0)        if (!cfg || !fp) {
        cfg->filename = strdup("FILE");                errno = EINVAL;
    cfg->line = 1;                return CFG_PARSE_ERROR;
         }
   
    cfg_yyin = fp;        if (!cfg->filename)
    cfg_scan_fp_begin(cfg_yyin);                cfg->filename = strdup("FILE");
    ret = cfg_parse_internal(cfg, 0, -1, 0);        if (!cfg->filename)
    cfg_scan_fp_end();                return CFG_PARSE_ERROR;
    if(ret == STATE_ERROR)
        return CFG_PARSE_ERROR;        cfg->line = 1;
    return CFG_SUCCESS;        cfg_scan_fp_begin(fp);
         ret = cfg_parse_internal(cfg, 0, -1, NULL);
         cfg_scan_fp_end();
         if (ret == STATE_ERROR)
                 return CFG_PARSE_ERROR;
 
         return CFG_SUCCESS;
 }  }
   
   static char *cfg_make_fullpath(const char *dir, const char *file)
   {
           int np;
           char *path;
           size_t len;
   
           if (!dir || !file) {
                   errno = EINVAL;
                   return NULL;
           }
   
           len = strlen(dir) + strlen(file) + 2;
           path = malloc(len);
           if (!path)
                   return NULL;
   
           np = snprintf(path, len, "%s/%s", dir, file);
   
           /*
            * np is the number of characters that would have
            * been printed if there was enough room in path.
            * if np >= n then the snprintf() was truncated
            * (which must be a bug).
            */
           assert(np < (int)len);
   
           return path;
   }
   
   DLLIMPORT char *cfg_searchpath(cfg_searchpath_t *p, const char *file)
   {
           char *fullpath;
   #ifdef HAVE_SYS_STAT_H
           struct stat st;
           int err;
   #endif
   
           if (!p || !file) {
                   errno = EINVAL;
                   return NULL;
           }
   
           if ((fullpath = cfg_searchpath(p->next, file)) != NULL)
                   return fullpath;
   
           if ((fullpath = cfg_make_fullpath(p->dir, file)) == NULL)
                   return NULL;
   
   #ifdef HAVE_SYS_STAT_H
           err = stat((const char *)fullpath, &st);
           if ((!err) && S_ISREG(st.st_mode))
                   return fullpath;
   #else
           /* needs an alternative check here for win32 */
   #endif
   
           free(fullpath);
           return NULL;
   }
   
 DLLIMPORT int cfg_parse(cfg_t *cfg, const char *filename)  DLLIMPORT int cfg_parse(cfg_t *cfg, const char *filename)
 {  {
    int ret;        int ret;
    FILE *fp;        char *fn;
         FILE *fp;
   
    assert(cfg && filename);        if (!cfg || !filename) {
                 errno = EINVAL;
                 return CFG_FILE_ERROR;
         }
   
    free(cfg->filename);        if (cfg->path)
    cfg->filename = cfg_tilde_expand(filename);                fn = cfg_searchpath(cfg->path, filename);
    fp = fopen(cfg->filename, "r");        else
    if(fp == 0)                fn = cfg_tilde_expand(filename);
        return CFG_FILE_ERROR;        if (!fn)
    ret = cfg_parse_fp(cfg, fp);                return CFG_FILE_ERROR;
    fclose(fp);
    return ret;        free(cfg->filename);
         cfg->filename = fn;
 
         fp = fopen(cfg->filename, "r");
         if (!fp)
                 return CFG_FILE_ERROR;
 
         ret = cfg_parse_fp(cfg, fp);
         fclose(fp);
 
         return ret;
 }  }
   
 DLLIMPORT int cfg_parse_buf(cfg_t *cfg, const char *buf)  DLLIMPORT int cfg_parse_buf(cfg_t *cfg, const char *buf)
 {  {
    int ret;        int ret;
         char *fn;
         FILE *fp;
   
    assert(cfg);        if (!cfg) {
    if(buf == 0)                errno = EINVAL;
        return CFG_SUCCESS;                return CFG_PARSE_ERROR;
         }
   
    free(cfg->filename);        if (!buf)
    cfg->filename = strdup("[buf]");                return CFG_SUCCESS;
    cfg->line = 1; 
   
    cfg_scan_string_begin(buf);        fn = strdup("[buf]");
    ret = cfg_parse_internal(cfg, 0, -1, 0);        if (!fn)
    cfg_scan_string_end();                return CFG_PARSE_ERROR;
    if(ret == STATE_ERROR)
        return CFG_PARSE_ERROR;        free(cfg->filename);
    return CFG_SUCCESS;        cfg->filename = fn;
 
         fp = fmemopen((void *)buf, strlen(buf), "r");
         if (!fp) {
                 /*
                  * fmemopen() on older GLIBC versions do not accept zero
                  * length buffers for some reason.  This is a workaround.
                  */
                 if (strlen(buf) > 0)
                         return CFG_FILE_ERROR;
 
                 return CFG_SUCCESS;
         }
 
         ret = cfg_parse_fp(cfg, fp);
         fclose(fp);
 
         return ret;
 }  }
   
 DLLIMPORT cfg_t *cfg_init(cfg_opt_t *opts, cfg_flag_t flags)  DLLIMPORT cfg_t *cfg_init(cfg_opt_t *opts, cfg_flag_t flags)
 {  {
    cfg_t *cfg;        cfg_t *cfg;
   
    cfg = calloc(1, sizeof(cfg_t));        cfg = calloc(1, sizeof(cfg_t));
    assert(cfg);        if (!cfg)
                 return NULL;
   
    cfg->name = strdup("root");        cfg->name = strdup("root");
    cfg->opts = cfg_dupopt_array(opts);        if (!cfg->name) {
    cfg->flags = flags;                free(cfg);
    cfg->filename = 0;                return NULL;
    cfg->line = 0;        }
    cfg->errfunc = 0; 
   
    cfg_init_defaults(cfg);        cfg->opts = cfg_dupopt_array(opts);
         if (!cfg->opts) {
                 free(cfg->name);
                 free(cfg);
                 return NULL;
         }
   
           cfg->flags = flags;
           cfg->filename = 0;
           cfg->line = 0;
           cfg->errfunc = 0;
   
 #if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)  #if defined(ENABLE_NLS) && defined(HAVE_GETTEXT)
    setlocale(LC_MESSAGES, "");        bindtextdomain(PACKAGE, LOCALEDIR);
    setlocale(LC_CTYPE, ""); 
    bindtextdomain(PACKAGE, LOCALEDIR); 
 #endif  #endif
   
    return cfg;        cfg_init_defaults(cfg);
 
         return cfg;
 }  }
   
 DLLIMPORT char *cfg_tilde_expand(const char *filename)  DLLIMPORT char *cfg_tilde_expand(const char *filename)
 {  {
    char *expanded = 0;        char *expanded = 0;
   
 #ifndef _WIN32  #ifndef _WIN32
    /* do tilde expansion        /* Do tilde expansion */
     */        if (filename[0] == '~') {
    if(filename[0] == '~')                struct passwd *passwd = 0;
    {                const char *file = 0;
        struct passwd *passwd = 0; 
        const char *file = 0; 
   
        if(filename[1] == '/' || filename[1] == 0)                if (filename[1] == '/' || filename[1] == 0) {
        {                        /* ~ or ~/path */
            /* ~ or ~/path */                        passwd = getpwuid(geteuid());
            passwd = getpwuid(geteuid());                        file = filename + 1;
            file = filename + 1;                } else {
        }                        /* ~user or ~user/path */
        else                        char *user;
        { 
            /* ~user or ~user/path */ 
            char *user; 
   
            file = strchr(filename, '/');                        file = strchr(filename, '/');
            if(file == 0)                        if (file == 0)
                file = filename + strlen(filename);                                file = filename + strlen(filename);
            user = malloc(file - filename); 
            strncpy(user, filename + 1, file - filename - 1); 
            passwd = getpwnam(user); 
            free(user); 
        } 
   
        if(passwd)                        user = malloc(file - filename);
        {                        if (!user)
            expanded = malloc(strlen(passwd->pw_dir) + strlen(file) + 1);                                return NULL;
            strcpy(expanded, passwd->pw_dir);
            strcat(expanded, file);                        strncpy(user, filename + 1, file - filename - 1);
        }                        passwd = getpwnam(user);
    }                        free(user);
                 }
 
                 if (passwd) {
                         expanded = malloc(strlen(passwd->pw_dir) + strlen(file) + 1);
                         if (!expanded)
                                 return NULL;
 
                         strcpy(expanded, passwd->pw_dir);
                         strcat(expanded, file);
                 }
         }
 #endif  #endif
    if(!expanded)        if (!expanded)
        expanded = strdup(filename);                expanded = strdup(filename);
    return expanded;
         return expanded;
 }  }
   
DLLIMPORT void cfg_free_value(cfg_opt_t *opt)DLLIMPORT int cfg_free_value(cfg_opt_t *opt)
 {  {
    unsigned int i;        if (!opt) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
   
    if(opt == 0)        if (opt->comment && !is_set(CFGF_RESET, opt->flags)) {
        return;                free(opt->comment);
                 opt->comment = NULL;
         }
   
    if(opt->values)        if (opt->values) {
    {                unsigned int i;
        for(i = 0; i < opt->nvalues; i++)
        {                for (i = 0; i < opt->nvalues; i++) {
            if(opt->type == CFGT_STR)                        if (opt->type == CFGT_STR) {
                free(opt->values[i]->string);                                free((void *)opt->values[i]->string);
            else if(opt->type == CFGT_SEC)                        } else if (opt->type == CFGT_SEC) {
                cfg_free(opt->values[i]->section);                                opt->values[i]->section->path = NULL; /* Global search path */
            else if(opt->type == CFGT_PTR && opt->freecb && opt->values[i]->ptr)                                cfg_free(opt->values[i]->section);
                (opt->freecb)(opt->values[i]->ptr);                        } else if (opt->type == CFGT_PTR && opt->freecb && opt->values[i]->ptr) {
            free(opt->values[i]);                                (opt->freecb) (opt->values[i]->ptr);
        }                        }
        free(opt->values);                        free(opt->values[i]);
    }                }
    opt->values = 0;                free(opt->values);
    opt->nvalues = 0;        }
 
         opt->values  = NULL;
         opt->nvalues = 0;
 
         return CFG_SUCCESS;
 }  }
   
 static void cfg_free_opt_array(cfg_opt_t *opts)  static void cfg_free_opt_array(cfg_opt_t *opts)
 {  {
    int i;        int i;
   
    for(i = 0; opts[i].name; ++i)        for (i = 0; opts[i].name; ++i) {
    {                free((void *)opts[i].name);
        free(opts[i].name);                if (opts[i].comment)
        if(opts[i].type == CFGT_FUNC || is_set(CFGF_LIST, opts[i].flags))                        free(opts[i].comment);
            free(opts[i].def.parsed);                if (opts[i].def.parsed)
        else if(opts[i].type == CFGT_STR)                        free(opts[i].def.parsed);
            free(opts[i].def.string);                if (opts[i].def.string)
        else if(opts[i].type == CFGT_SEC)                        free((void *)opts[i].def.string);
            cfg_free_opt_array(opts[i].subopts);                if (opts[i].subopts)
    }                        cfg_free_opt_array(opts[i].subopts);
    free(opts);        }
         free(opts);
 }  }
   
DLLIMPORT void cfg_free(cfg_t *cfg)static int cfg_free_searchpath(cfg_searchpath_t *p)
 {  {
    int i;        if (p) {
                 cfg_free_searchpath(p->next);
                 free(p->dir);
                 free(p);
         }
   
    if(cfg == 0)        return CFG_SUCCESS;
        return;}
   
    for(i = 0; cfg->opts[i].name; ++i)DLLIMPORT int cfg_free(cfg_t *cfg)
        cfg_free_value(&cfg->opts[i]);{
         int i;
         int isroot = 0;
   
    cfg_free_opt_array(cfg->opts);        if (!cfg) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
   
    free(cfg->name);        if (cfg->comment)
    free(cfg->title);                free(cfg->comment);
    free(cfg->filename); 
   
    free(cfg);        for (i = 0; cfg->opts[i].name; ++i)
                 cfg_free_value(&cfg->opts[i]);
 
         cfg_free_opt_array(cfg->opts);
         cfg_free_searchpath(cfg->path);
 
         if (cfg->name) {
                 isroot = !strcmp(cfg->name, "root");
                 free(cfg->name);
         }
         if (cfg->title)
                 free(cfg->title);
         if (cfg->filename)
                 free(cfg->filename);
 
         free(cfg);
         if (isroot)
                 cfg_yylex_destroy();
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT int cfg_include(cfg_t *cfg, cfg_opt_t *opt, int argc,DLLIMPORT int cfg_include(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
                          const char **argv) 
 {  {
    opt = NULL;        (void)opt;              /* Unused in this predefined include FUNC */
    if(argc != 1)
    {        if (!cfg || !argv) {
        cfg_error(cfg, _("wrong number of arguments to cfg_include()"));                errno = EINVAL;
        return 1;                return CFG_FAIL;
    }        }
    return cfg_lexer_include(cfg, argv[0]);
         if (argc != 1) {
                 cfg_error(cfg, _("wrong number of arguments to cfg_include()"));
                 return 1;
         }
 
         return cfg_lexer_include(cfg, argv[0]);
 }  }
   
 static cfg_value_t *cfg_opt_getval(cfg_opt_t *opt, unsigned int index)  static cfg_value_t *cfg_opt_getval(cfg_opt_t *opt, unsigned int index)
 {  {
    cfg_value_t *val = 0;        cfg_value_t *val = 0;
   
    assert(index == 0 || is_set(CFGF_LIST, opt->flags));        if (index != 0 && !is_set(CFGF_LIST, opt->flags) && !is_set(CFGF_MULTI, opt->flags)) {
                 errno = EINVAL;
                 return NULL;
         }
   
    if(opt->simple_value)        if (opt->simple_value.ptr)
        val = (cfg_value_t *)opt->simple_value;                val = (cfg_value_t *)opt->simple_value.ptr;
    else        else {
    {                if (is_set(CFGF_RESET, opt->flags)) {
        if(is_set(CFGF_RESET, opt->flags))                        cfg_free_value(opt);
        {                        opt->flags &= ~CFGF_RESET;
            cfg_free_value(opt);                }
            opt->flags &= ~CFGF_RESET; 
        } 
   
        if(index >= opt->nvalues)                if (index >= opt->nvalues)
            val = cfg_addval(opt);                        val = cfg_addval(opt);
        else                else
            val = opt->values[index];                        val = opt->values[index];
    }        }
    return val;
         return val;
 }  }
   
DLLIMPORT void cfg_opt_setnint(cfg_opt_t *opt, long int value,DLLIMPORT int cfg_opt_setcomment(cfg_opt_t *opt, char *comment)
                               unsigned int index) 
 {  {
    cfg_value_t *val;        char *oldcomment, *newcomment;
    assert(opt && opt->type == CFGT_INT);
    val = cfg_opt_getval(opt, index);        if (!opt || !comment) {
    val->number = value;                errno = EINVAL;
                 return CFG_FAIL;
         }
 
         oldcomment = opt->comment;
         newcomment = strdup(comment);
         if (!newcomment)
                 return CFG_FAIL;
 
         if (oldcomment)
                 free(oldcomment);
         opt->comment = newcomment;
         opt->flags |= CFGF_COMMENTS;
         opt->flags |= CFGF_MODIFIED;
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT void cfg_setnint(cfg_t *cfg, const char *name,DLLIMPORT int cfg_setcomment(cfg_t *cfg, const char *name, char *comment)
                     long int value, unsigned int index) 
 {  {
    cfg_opt_setnint(cfg_getopt(cfg, name), value, index);        return cfg_opt_setcomment(cfg_getopt(cfg, name), comment);
 }  }
   
DLLIMPORT void cfg_setint(cfg_t *cfg, const char *name, long int value)DLLIMPORT int cfg_opt_setnint(cfg_opt_t *opt, long int value, unsigned int index)
 {  {
    cfg_setnint(cfg, name, value, 0);        cfg_value_t *val;
 
         if (!opt || opt->type != CFGT_INT) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
 
         val = cfg_opt_getval(opt, index);
         if (!val)
                 return CFG_FAIL;
 
         val->number = value;
         opt->flags |= CFGF_MODIFIED;
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT void cfg_opt_setnfloat(cfg_opt_t *opt, double value,DLLIMPORT int cfg_setnint(cfg_t *cfg, const char *name, long int value, unsigned int index)
                                 unsigned int index) 
 {  {
    cfg_value_t *val;        cfg_opt_t *opt;
    assert(opt && opt->type == CFGT_FLOAT);
    val = cfg_opt_getval(opt, index);        opt = cfg_getopt(cfg, name);
    val->fpnumber = value;        if (opt && opt->validcb2 && (*opt->validcb2)(cfg, opt, (void *)&value) != 0)
                 return CFG_FAIL;
 
         return cfg_opt_setnint(opt, value, index);
 }  }
   
DLLIMPORT void cfg_setnfloat(cfg_t *cfg, const char *name,DLLIMPORT int cfg_setint(cfg_t *cfg, const char *name, long int value)
                   double value, unsigned int index) 
 {  {
    cfg_opt_setnfloat(cfg_getopt(cfg, name), value, index);        return cfg_setnint(cfg, name, value, 0);
 }  }
   
DLLIMPORT void cfg_setfloat(cfg_t *cfg, const char *name, double value)DLLIMPORT int cfg_opt_setnfloat(cfg_opt_t *opt, double value, unsigned int index)
 {  {
    cfg_setnfloat(cfg, name, value, 0);        cfg_value_t *val;
 
         if (!opt || opt->type != CFGT_FLOAT) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
 
         val = cfg_opt_getval(opt, index);
         if (!val)
                 return CFG_FAIL;
 
         val->fpnumber = value;
         opt->flags |= CFGF_MODIFIED;
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT void cfg_opt_setnbool(cfg_opt_t *opt, cfg_bool_t value,DLLIMPORT int cfg_setnfloat(cfg_t *cfg, const char *name, double value, unsigned int index)
                                unsigned int index) 
 {  {
    cfg_value_t *val;        cfg_opt_t *opt;
    assert(opt && opt->type == CFGT_BOOL);
    val = cfg_opt_getval(opt, index);        opt = cfg_getopt(cfg, name);
    val->boolean = value;        if (opt && opt->validcb2 && (*opt->validcb2)(cfg, opt, (void *)&value) != 0)
                 return CFG_FAIL;
 
         return cfg_opt_setnfloat(opt, value, index);
 }  }
   
DLLIMPORT void cfg_setnbool(cfg_t *cfg, const char *name,DLLIMPORT int cfg_setfloat(cfg_t *cfg, const char *name, double value)
                            cfg_bool_t value, unsigned int index) 
 {  {
    cfg_opt_setnbool(cfg_getopt(cfg, name), value, index);        return cfg_setnfloat(cfg, name, value, 0);
 }  }
   
DLLIMPORT void cfg_setbool(cfg_t *cfg, const char *name, cfg_bool_t value)DLLIMPORT int cfg_opt_setnbool(cfg_opt_t *opt, cfg_bool_t value, unsigned int index)
 {  {
    cfg_setnbool(cfg, name, value, 0);        cfg_value_t *val;
 
         if (!opt || opt->type != CFGT_BOOL) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
 
         val = cfg_opt_getval(opt, index);
         if (!val)
                 return CFG_FAIL;
 
         val->boolean = value;
         opt->flags |= CFGF_MODIFIED;
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT void cfg_opt_setnstr(cfg_opt_t *opt, const char *value,DLLIMPORT int cfg_setnbool(cfg_t *cfg, const char *name, cfg_bool_t value, unsigned int index)
                               unsigned int index) 
 {  {
    cfg_value_t *val;        return cfg_opt_setnbool(cfg_getopt(cfg, name), value, index);
    assert(opt && opt->type == CFGT_STR); 
    val = cfg_opt_getval(opt, index); 
    free(val->string); 
    val->string = value ? strdup(value) : 0; 
 }  }
   
DLLIMPORT void cfg_setnstr(cfg_t *cfg, const char *name,DLLIMPORT int cfg_setbool(cfg_t *cfg, const char *name, cfg_bool_t value)
                           const char *value, unsigned int index) 
 {  {
    cfg_opt_setnstr(cfg_getopt(cfg, name), value, index);        return cfg_setnbool(cfg, name, value, 0);
 }  }
   
DLLIMPORT void cfg_setstr(cfg_t *cfg, const char *name, const char *value)DLLIMPORT int cfg_opt_setnstr(cfg_opt_t *opt, const char *value, unsigned int index)
 {  {
    cfg_setnstr(cfg, name, value, 0);        char *newstr, *oldstr = NULL;
         cfg_value_t *val;
 
         if (!opt || opt->type != CFGT_STR) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
 
         val = cfg_opt_getval(opt, index);
         if (!val)
                 return CFG_FAIL;
 
         if (val->string)
                 oldstr = val->string;
 
         if (value) {
                 newstr = strdup(value);
                 if (!newstr)
                         return CFG_FAIL;
                 val->string = newstr;
         } else {
                 val->string = NULL;
         }
 
         if (oldstr)
                 free(oldstr);
         opt->flags |= CFGF_MODIFIED;
 
         return CFG_SUCCESS;
 }  }
   
static void cfg_addlist_internal(cfg_opt_t *opt,DLLIMPORT int cfg_setnstr(cfg_t *cfg, const char *name, const char *value, unsigned int index)
                                 unsigned int nvalues, va_list ap) 
 {  {
    unsigned int i;        cfg_opt_t *opt;
   
    for(i = 0; i < nvalues; i++)        opt = cfg_getopt(cfg, name);
    {        if (opt && opt->validcb2 && (*opt->validcb2)(cfg, opt, (void *)value) != 0)
        switch(opt->type)                return CFG_FAIL;
        {
            case CFGT_INT:        return cfg_opt_setnstr(opt, value, index);
                cfg_opt_setnint(opt, va_arg(ap, int), opt->nvalues); 
                break; 
            case CFGT_FLOAT: 
                cfg_opt_setnfloat(opt, va_arg(ap, double), 
                                  opt->nvalues); 
                break; 
            case CFGT_BOOL: 
                cfg_opt_setnbool(opt, va_arg(ap, cfg_bool_t), 
                                 opt->nvalues); 
                break; 
            case CFGT_STR: 
                cfg_opt_setnstr(opt, va_arg(ap, char*), opt->nvalues); 
                break; 
            case CFGT_FUNC: 
            case CFGT_SEC: 
            default: 
                break; 
        } 
    } 
 }  }
   
DLLIMPORT void cfg_setlist(cfg_t *cfg, const char *name,DLLIMPORT int cfg_setstr(cfg_t *cfg, const char *name, const char *value)
                           unsigned int nvalues, ...) 
 {  {
    va_list ap;        return cfg_setnstr(cfg, name, value, 0);
    cfg_opt_t *opt = cfg_getopt(cfg, name);}
   
    assert(opt && is_set(CFGF_LIST, opt->flags));static int cfg_addlist_internal(cfg_opt_t *opt, unsigned int nvalues, va_list ap)
 {
         int result = CFG_FAIL;
         unsigned int i;
   
    cfg_free_value(opt);        for (i = 0; i < nvalues; i++) {
    va_start(ap, nvalues);                switch (opt->type) {
    cfg_addlist_internal(opt, nvalues, ap);                case CFGT_INT:
    va_end(ap);                        result = cfg_opt_setnint(opt, va_arg(ap, int), opt->nvalues);
                         break;
 
                 case CFGT_FLOAT:
                         result = cfg_opt_setnfloat(opt, va_arg(ap, double), opt->nvalues);
                         break;
 
                 case CFGT_BOOL:
                         result = cfg_opt_setnbool(opt, va_arg(ap, cfg_bool_t), opt->nvalues);
                         break;
 
                 case CFGT_STR:
                         result = cfg_opt_setnstr(opt, va_arg(ap, char *), opt->nvalues);
                         break;
 
                 case CFGT_FUNC:
                 case CFGT_SEC:
                 default:
                         result = CFG_SUCCESS;
                         break;
                 }
         }
 
         return result;
 }  }
   
DLLIMPORT void cfg_addlist(cfg_t *cfg, const char *name,DLLIMPORT int cfg_setlist(cfg_t *cfg, const char *name, unsigned int nvalues, ...)
                           unsigned int nvalues, ...) 
 {  {
    va_list ap;        va_list ap;
    cfg_opt_t *opt = cfg_getopt(cfg, name);        cfg_opt_t *opt = cfg_getopt(cfg, name);
   
    assert(opt && is_set(CFGF_LIST, opt->flags));        if (!opt || !is_set(CFGF_LIST, opt->flags)) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
   
    va_start(ap, nvalues);        cfg_free_value(opt);
    cfg_addlist_internal(opt, nvalues, ap);        va_start(ap, nvalues);
    va_end(ap);        cfg_addlist_internal(opt, nvalues, ap);
         va_end(ap);
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT void cfg_opt_nprint_var(cfg_opt_t *opt, unsigned int index, FILE *fp)DLLIMPORT int cfg_addlist(cfg_t *cfg, const char *name, unsigned int nvalues, ...)
 {  {
    const char *str;        va_list ap;
         cfg_opt_t *opt = cfg_getopt(cfg, name);
   
    assert(opt && fp);        if (!opt || !is_set(CFGF_LIST, opt->flags)) {
    switch(opt->type)                errno = EINVAL;
    {                return CFG_FAIL;
        case CFGT_INT:        }
            fprintf(fp, "%ld", cfg_opt_getnint(opt, index));
            break;        va_start(ap, nvalues);
        case CFGT_FLOAT:        cfg_addlist_internal(opt, nvalues, ap);
            fprintf(fp, "%lf", cfg_opt_getnfloat(opt, index));        va_end(ap);
            break;
        case CFGT_STR:        return CFG_SUCCESS;
            str = cfg_opt_getnstr(opt, index); 
            fprintf(fp, "\""); 
            while (str && *str) 
            { 
                if(*str == '"') 
                    fprintf(fp, "\\\""); 
                else if(*str == '\\') 
                    fprintf(fp, "\\\\"); 
                else 
                    fprintf(fp, "%c", *str); 
                str++; 
            } 
            fprintf(fp, "\""); 
            break; 
        case CFGT_BOOL: 
            fprintf(fp, "%s", cfg_opt_getnbool(opt, index) ? "true" : "false"); 
            break; 
        case CFGT_NONE: 
        case CFGT_SEC: 
        case CFGT_FUNC: 
        case CFGT_PTR: 
            break; 
    } 
 }  }
   
   DLLIMPORT cfg_t *cfg_addtsec(cfg_t *cfg, const char *name, const char *title)
   {
           cfg_opt_t *opt;
           cfg_value_t *val;
   
           if (cfg_gettsec(cfg, name, title))
                   return NULL;
   
           opt = cfg_getopt(cfg, name);
           if (!opt) {
                   cfg_error(cfg, _("no such option '%s'"), name);
                   return NULL;
           }
           val = cfg_setopt(cfg, opt, title);
           if (!val)
                   return NULL;
   
           val->section->path = cfg->path; /* Remember global search path. */
           val->section->line = 1;
           val->section->errfunc = cfg->errfunc;
   
           return val->section;
   }
   
   DLLIMPORT int cfg_opt_rmnsec(cfg_opt_t *opt, unsigned int index)
   {
           unsigned int n;
           cfg_value_t *val;
   
           if (!opt || opt->type != CFGT_SEC) {
                   errno = EINVAL;
                   return CFG_FAIL;
           }
   
           n = cfg_opt_size(opt);
           if (index >= n)
                   return CFG_FAIL;
   
           val = cfg_opt_getval(opt, index);
           if (!val)
                   return CFG_FAIL;
   
           if (index + 1 != n) {
                   /* not removing last, move the tail */
                   memmove(&opt->values[index], &opt->values[index + 1], sizeof(opt->values[index]) * (n - index - 1));
           }
           --opt->nvalues;
   
           cfg_free(val->section);
           free(val);
   
           return CFG_SUCCESS;
   }
   
   DLLIMPORT int cfg_rmnsec(cfg_t *cfg, const char *name, unsigned int index)
   {
           return cfg_opt_rmnsec(cfg_getopt(cfg, name), index);
   }
   
   DLLIMPORT int cfg_rmsec(cfg_t *cfg, const char *name)
   {
           cfg_opt_t *opt;
           long int index;
   
           opt = cfg_getopt_secidx(cfg, name, &index);
           return cfg_opt_rmnsec(opt, index);
   }
   
   DLLIMPORT int cfg_opt_rmtsec(cfg_opt_t *opt, const char *title)
   {
           unsigned int i, n;
   
           if (!opt || !title) {
                   errno = EINVAL;
                   return CFG_FAIL;
           }
   
           if (!is_set(CFGF_TITLE, opt->flags))
                   return CFG_FAIL;
   
           n = cfg_opt_size(opt);
           for (i = 0; i < n; i++) {
                   cfg_t *sec = cfg_opt_getnsec(opt, i);
   
                   if (!sec || !sec->title)
                           return CFG_FAIL;
   
                   if (is_set(CFGF_NOCASE, opt->flags)) {
                           if (strcasecmp(title, sec->title) == 0)
                                   break;
                   } else {
                           if (strcmp(title, sec->title) == 0)
                                   break;
                   }
           }
           if (i == n)
                   return CFG_FAIL;
   
           return cfg_opt_rmnsec(opt, i);
   }
   
   DLLIMPORT int cfg_rmtsec(cfg_t *cfg, const char *name, const char *title)
   {
           return cfg_opt_rmtsec(cfg_getopt(cfg, name), title);
   }
   
   DLLIMPORT int cfg_opt_nprint_var(cfg_opt_t *opt, unsigned int index, FILE *fp)
   {
           const char *str;
   
           if (!opt || !fp) {
                   errno = EINVAL;
                   return CFG_FAIL;
           }
   
           switch (opt->type) {
           case CFGT_INT:
                   fprintf(fp, "%ld", cfg_opt_getnint(opt, index));
                   break;
   
           case CFGT_FLOAT:
                   fprintf(fp, "%f", cfg_opt_getnfloat(opt, index));
                   break;
   
           case CFGT_STR:
                   str = cfg_opt_getnstr(opt, index);
                   fprintf(fp, "\"");
                   while (str && *str) {
                           if (*str == '"')
                                   fprintf(fp, "\\\"");
                           else if (*str == '\\')
                                   fprintf(fp, "\\\\");
                           else
                                   fprintf(fp, "%c", *str);
                           str++;
                   }
                   fprintf(fp, "\"");
                   break;
   
           case CFGT_BOOL:
                   fprintf(fp, "%s", cfg_opt_getnbool(opt, index) ? "true" : "false");
                   break;
   
           case CFGT_NONE:
           case CFGT_SEC:
           case CFGT_FUNC:
           case CFGT_PTR:
           case CFGT_COMMENT:
                   break;
           }
   
           return CFG_SUCCESS;
   }
   
 static void cfg_indent(FILE *fp, int indent)  static void cfg_indent(FILE *fp, int indent)
 {  {
    while(indent--)        while (indent--)
        fprintf(fp, "  ");                fprintf(fp, "  ");
 }  }
   
DLLIMPORT void cfg_opt_print_indent(cfg_opt_t *opt, FILE *fp, int indent)static int cfg_opt_print_pff_indent(cfg_opt_t *opt, FILE *fp,
                                     cfg_print_filter_func_t pff, int indent)
 {  {
    assert(opt && fp);        if (!opt || !fp) {
                 errno = EINVAL;
                 return CFG_FAIL;
         }
   
    if(opt->type == CFGT_SEC)        if (is_set(CFGF_COMMENTS, opt->flags) && opt->comment) {
    {                cfg_indent(fp, indent);
        cfg_t *sec;                fprintf(fp, "/* %s */\n", opt->comment);
        unsigned int i;        }
   
        for(i = 0; i < cfg_opt_size(opt); i++)        if (opt->type == CFGT_SEC) {
        {                cfg_t *sec;
            sec = cfg_opt_getnsec(opt, i);                unsigned int i;
            cfg_indent(fp, indent); 
            if(is_set(CFGF_TITLE, opt->flags)) 
                fprintf(fp, "%s \"%s\" {\n", opt->name, cfg_title(sec)); 
            else 
                fprintf(fp, "%s {\n", opt->name); 
            cfg_print_indent(sec, fp, indent + 1); 
            cfg_indent(fp, indent); 
            fprintf(fp, "}\n"); 
        } 
    } 
    else if(opt->type != CFGT_FUNC && opt->type != CFGT_NONE) 
    { 
        if(is_set(CFGF_LIST, opt->flags)) 
        { 
            unsigned int i; 
   
            cfg_indent(fp, indent);                for (i = 0; i < cfg_opt_size(opt); i++) {
            fprintf(fp, "%s = {", opt->name);                        sec = cfg_opt_getnsec(opt, i);
                         cfg_indent(fp, indent);
                         if (is_set(CFGF_TITLE, opt->flags))
                                 fprintf(fp, "%s \"%s\" {\n", opt->name, cfg_title(sec));
                         else
                                 fprintf(fp, "%s {\n", opt->name);
                         cfg_print_pff_indent(sec, fp, pff, indent + 1);
                         cfg_indent(fp, indent);
                         fprintf(fp, "}\n");
                 }
         } else if (opt->type != CFGT_FUNC && opt->type != CFGT_NONE) {
                 if (is_set(CFGF_LIST, opt->flags)) {
                         cfg_indent(fp, indent);
                         fprintf(fp, "%s = {", opt->name);
   
            if(opt->nvalues)                        if (opt->nvalues) {
            {                                unsigned int i;
                if(opt->pf) 
                    opt->pf(opt, 0, fp); 
                else 
                    cfg_opt_nprint_var(opt, 0, fp); 
                for(i = 1; i < opt->nvalues; i++) 
                { 
                    fprintf(fp, ", "); 
                    if(opt->pf) 
                        opt->pf(opt, i, fp); 
                    else 
                        cfg_opt_nprint_var(opt, i, fp); 
                } 
            } 
   
            fprintf(fp, "}");                                if (opt->pf)
        }                                        opt->pf(opt, 0, fp);
        else                                else
        {                                        cfg_opt_nprint_var(opt, 0, fp);
            cfg_indent(fp, indent);                                for (i = 1; i < opt->nvalues; i++) {
            /* comment out the option if is not set */                                        fprintf(fp, ", ");
            if(opt->simple_value)                                        if (opt->pf)
            {                                                opt->pf(opt, i, fp);
                if(opt->type == CFGT_STR && *((char **)opt->simple_value) == 0)                                        else
                    fprintf(fp, "# ");                                                cfg_opt_nprint_var(opt, i, fp);
            }                                }
            else                        }
            {
                if(cfg_opt_size(opt) == 0 || (                        fprintf(fp, "}");
                       opt->type == CFGT_STR && (opt->values[0]->string == 0 ||                } else {
                                             opt->values[0]->string[0] == 0)))                        cfg_indent(fp, indent);
                    fprintf(fp, "# ");                        /* comment out the option if is not set */
            }                        if (cfg_opt_size(opt) == 0 ||
            fprintf(fp, "%s = ", opt->name);                            (opt->type == CFGT_STR && !cfg_opt_getnstr(opt, 0)))
            if(opt->pf)                                fprintf(fp, "# ");
                opt->pf(opt, 0, fp);                        fprintf(fp, "%s=", opt->name);
            else                        if (opt->pf)
                cfg_opt_nprint_var(opt, 0, fp);                                opt->pf(opt, 0, fp);
        }                        else
                                    cfg_opt_nprint_var(opt, 0, fp);
        fprintf(fp, "\n");                }
    }
    else if(opt->pf)                fprintf(fp, "\n");
    {        } else if (opt->pf) {
        cfg_indent(fp, indent);                cfg_indent(fp, indent);
        opt->pf(opt, 0, fp);                opt->pf(opt, 0, fp);
        fprintf(fp, "\n");                fprintf(fp, "\n");
    }        }
 
         return CFG_SUCCESS;
 }  }
   
DLLIMPORT void cfg_opt_print(cfg_opt_t *opt, FILE *fp)DLLIMPORT int cfg_opt_print_indent(cfg_opt_t *opt, FILE *fp, int indent)
 {  {
    cfg_opt_print_indent(opt, fp, 0);        return cfg_opt_print_pff_indent(opt, fp, 0, indent);
 }  }
   
DLLIMPORT void cfg_print_indent(cfg_t *cfg, FILE *fp, int indent)DLLIMPORT int cfg_opt_print(cfg_opt_t *opt, FILE *fp)
 {  {
    int i;        return cfg_opt_print_pff_indent(opt, fp, 0, 0);
 }
   
    for(i = 0; cfg->opts[i].name; i++)static int cfg_print_pff_indent(cfg_t *cfg, FILE *fp,
        cfg_opt_print_indent(&cfg->opts[i], fp, indent);                                cfg_print_filter_func_t fb_pff, int indent)
 {
         int i, result = CFG_SUCCESS;
 
         for (i = 0; cfg->opts[i].name; i++) {
                 cfg_print_filter_func_t pff = cfg->pff ? cfg->pff : fb_pff;
                 if (pff && pff(cfg, &cfg->opts[i]))
                         continue;
                 result += cfg_opt_print_pff_indent(&cfg->opts[i], fp, pff, indent);
         }
 
         return result;
 }  }
   
DLLIMPORT void cfg_print(cfg_t *cfg, FILE *fp)DLLIMPORT int cfg_print_indent(cfg_t *cfg, FILE *fp, int indent)
 {  {
    cfg_print_indent(cfg, fp, 0);        return cfg_print_pff_indent(cfg, fp, 0, indent);
 }  }
   
DLLIMPORT cfg_print_func_t cfg_opt_set_print_func(cfg_opt_t *opt,DLLIMPORT int cfg_print(cfg_t *cfg, FILE *fp)
                                                  cfg_print_func_t pf) 
 {  {
    cfg_print_func_t oldpf;        return cfg_print_pff_indent(cfg, fp, 0, 0);
 }
   
    assert(opt);DLLIMPORT cfg_print_func_t cfg_opt_set_print_func(cfg_opt_t *opt, cfg_print_func_t pf)
    oldpf = opt->pf;{
    opt->pf = pf;        cfg_print_func_t oldpf;
   
    return oldpf;        if (!opt) {
                 errno = EINVAL;
                 return NULL;
         }
 
         oldpf = opt->pf;
         opt->pf = pf;
 
         return oldpf;
 }  }
   
DLLIMPORT cfg_print_func_t cfg_set_print_func(cfg_t *cfg, const char *name,DLLIMPORT cfg_print_func_t cfg_set_print_func(cfg_t *cfg, const char *name, cfg_print_func_t pf)
                                              cfg_print_func_t pf) 
 {  {
    return cfg_opt_set_print_func(cfg_getopt(cfg, name), pf);        return cfg_opt_set_print_func(cfg_getopt(cfg, name), pf);
 }  }
   
 static cfg_opt_t *cfg_getopt_array(cfg_opt_t *rootopts, int cfg_flags, const char *name)  static cfg_opt_t *cfg_getopt_array(cfg_opt_t *rootopts, int cfg_flags, const char *name)
 {  {
    unsigned int i;        unsigned int i;
    cfg_opt_t *opts = rootopts;        cfg_opt_t *opts = rootopts;
   
    assert(rootopts && name);        if (!rootopts || !name) {
                 errno = EINVAL;
                 return NULL;
         }
   
    while(name && *name)        while (name && *name) {
    {                cfg_t *seccfg;
        cfg_t *seccfg;                char *secname;
        char *secname;                size_t len = strcspn(name, "|");
        size_t len = strcspn(name, "|"); 
        if(name[len] == 0 /*len == strlen(name)*/) 
            /* no more subsections */ 
            break; 
        if(len) 
        { 
            cfg_opt_t *secopt; 
            secname = strndup(name, len); 
            secopt = cfg_getopt_array(opts, cfg_flags, secname); 
            free(secname); 
            if(secopt == 0) 
            { 
                /*fprintf(stderr, "section not found\n");*/ 
                return 0; 
            } 
            if(secopt->type != CFGT_SEC) 
            { 
                /*fprintf(stderr, "not a section!\n");*/ 
                return 0; 
            } 
   
            if(!is_set(CFGF_MULTI, secopt->flags) &&                if (name[len] == 0 /*len == strlen(name) */ )
               (seccfg = cfg_opt_getnsec(secopt, 0)) != 0)                        /* no more subsections */
            {                        break;
                opts = seccfg->opts; 
            } 
            else 
                opts = secopt->subopts; 
            if(opts == 0) 
            { 
                /*fprintf(stderr, "section have no subopts!?\n");*/ 
                return 0; 
            } 
        } 
        name += len; 
        name += strspn(name, "|"); 
    } 
   
    for(i = 0; opts[i].name; i++)                if (len) {
    {                        cfg_opt_t *secopt;
        if(is_set(CFGF_NOCASE, cfg_flags))
        {                        secname = strndup(name, len);
            if(strcasecmp(opts[i].name, name) == 0)                        if (!secname)
                return &opts[i];                                return NULL;
        }
        else                        secopt = cfg_getopt_array(opts, cfg_flags, secname);
        {                        free(secname);
            if(strcmp(opts[i].name, name) == 0)                        if (!secopt) {
                return &opts[i];                                /*fprintf(stderr, "section not found\n"); */
        }                                return NULL;
    }                        }
    return 0;                        if (secopt->type != CFGT_SEC) {
                                 /*fprintf(stderr, "not a section!\n"); */
                                 return NULL;
                         }
 
                         if (!is_set(CFGF_MULTI, secopt->flags) && (seccfg = cfg_opt_getnsec(secopt, 0)) != 0)
                                 opts = seccfg->opts;
                         else
                                 opts = secopt->subopts;
 
                         if (!opts) {
                                 /*fprintf(stderr, "section have no subopts!?\n"); */
                                 return NULL;
                         }
                 }
                 name += len;
                 name += strspn(name, "|");
         }
 
         for (i = 0; opts[i].name; i++) {
                 if (is_set(CFGF_NOCASE, cfg_flags)) {
                         if (strcasecmp(opts[i].name, name) == 0)
                                 return &opts[i];
                 } else {
                         if (strcmp(opts[i].name, name) == 0)
                                 return &opts[i];
                 }
         }
 
         return NULL;
 }  }
   
DLLIMPORT cfg_validate_callback_t cfg_set_validate_func(cfg_t *cfg,DLLIMPORT cfg_validate_callback_t cfg_set_validate_func(cfg_t *cfg, const char *name, cfg_validate_callback_t vf)
                                                        const char *name, 
                                                        cfg_validate_callback_t vf) 
 {  {
    cfg_opt_t *opt = cfg_getopt_array(cfg->opts, cfg->flags, name);        cfg_opt_t *opt;
    cfg_validate_callback_t oldvf;        cfg_validate_callback_t oldvf;
    assert(opt);
    oldvf = opt->validcb;        opt = cfg_getopt_array(cfg->opts, cfg->flags, name);
    opt->validcb = vf;        if (!opt)
    return oldvf;                return NULL;
 
         oldvf = opt->validcb;
         opt->validcb = vf;
 
         return oldvf;
 }  }
   
   DLLIMPORT cfg_validate_callback2_t cfg_set_validate_func2(cfg_t *cfg, const char *name, cfg_validate_callback2_t vf)
   {
           cfg_opt_t *opt;
           cfg_validate_callback2_t oldvf;
   
           opt = cfg_getopt_array(cfg->opts, cfg->flags, name);
           if (!opt)
                   return NULL;
   
           oldvf = opt->validcb2;
           opt->validcb2 = vf;
   
           return oldvf;
   }
   
   /**
    * Local Variables:
    *  indent-tabs-mode: t
    *  c-file-style: "linux"
    * End:
    */

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.2


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