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