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