File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / confuse / src / confuse.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:49:17 2021 UTC (3 years, 3 months ago) by misho
Branches: confuse, MAIN
CVS tags: v3_3, HEAD
confuse 3.3

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

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