1 /* 2 * Copyright (c) 2000, Boris Popov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: rcfile.c,v 1.5 2001/04/16 12:46:46 bp Exp $ 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/types.h> 39 #include <sys/queue.h> 40 #include <ctype.h> 41 #include <errno.h> 42 #include <stdio.h> 43 #include <string.h> 44 #include <stdlib.h> 45 #include <pwd.h> 46 #include <unistd.h> 47 #include <err.h> 48 49 #include <cflib.h> 50 #include "rcfile_priv.h" 51 52 SLIST_HEAD(rcfile_head, rcfile); 53 static struct rcfile_head pf_head = {NULL}; 54 55 static struct rcfile* rc_cachelookup(const char *filename); 56 static struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname); 57 static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname); 58 static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp); 59 static struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname); 60 static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value); 61 static void rc_key_free(struct rckey *p); 62 static void rc_parse(struct rcfile *rcp); 63 64 65 /* 66 * open rcfile and load its content, if already open - return previous handle 67 */ 68 int 69 rc_open(const char *filename, const char *mode, struct rcfile **rcfile) 70 { 71 struct rcfile *rcp; 72 FILE *f; 73 74 rcp = rc_cachelookup(filename); 75 if (rcp) { 76 *rcfile = rcp; 77 return 0; 78 } 79 f = fopen(filename, mode); 80 if (f == NULL) 81 return errno; 82 rcp = malloc(sizeof(struct rcfile)); 83 if (rcp == NULL) { 84 fclose(f); 85 return ENOMEM; 86 } 87 bzero(rcp, sizeof(struct rcfile)); 88 rcp->rf_name = strdup(filename); 89 rcp->rf_f = f; 90 SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); 91 rc_parse(rcp); 92 *rcfile = rcp; 93 return 0; 94 } 95 96 int 97 rc_merge(const char *filename, struct rcfile **rcfile) 98 { 99 struct rcfile *rcp = *rcfile; 100 FILE *f, *t; 101 102 if (rcp == NULL) { 103 return rc_open(filename, "r", rcfile); 104 } 105 f = fopen (filename, "r"); 106 if (f == NULL) 107 return errno; 108 t = rcp->rf_f; 109 rcp->rf_f = f; 110 rc_parse(rcp); 111 rcp->rf_f = t; 112 fclose(f); 113 return 0; 114 } 115 116 int 117 rc_close(struct rcfile *rcp) 118 { 119 struct rcsection *p, *n; 120 121 fclose(rcp->rf_f); 122 for(p = SLIST_FIRST(&rcp->rf_sect); p;) { 123 n = p; 124 p = SLIST_NEXT(p,rs_next); 125 rc_freesect(rcp, n); 126 } 127 free(rcp->rf_name); 128 SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next); 129 free(rcp); 130 return 0; 131 } 132 133 static struct rcfile* 134 rc_cachelookup(const char *filename) 135 { 136 struct rcfile *p; 137 138 SLIST_FOREACH(p, &pf_head, rf_next) 139 if (strcmp (filename, p->rf_name) == 0) 140 return p; 141 return 0; 142 } 143 144 static struct rcsection * 145 rc_findsect(struct rcfile *rcp, const char *sectname) 146 { 147 struct rcsection *p; 148 149 SLIST_FOREACH(p, &rcp->rf_sect, rs_next) 150 if (strcmp(p->rs_name, sectname)==0) 151 return p; 152 return NULL; 153 } 154 155 static struct rcsection * 156 rc_addsect(struct rcfile *rcp, const char *sectname) 157 { 158 struct rcsection *p; 159 const char* sectletter = sectname; 160 161 p = rc_findsect(rcp, sectname); 162 if (p) return p; 163 p = malloc(sizeof(*p)); 164 if (!p) return NULL; 165 for(sectletter = sectname; *sectletter; sectletter++) { 166 if (islower(*sectletter)) { 167 if (strcmp(sectname, "default")) 168 dprintf(STDERR_FILENO, "warning: section name [%s] contains lower-case letters\n", sectname); 169 break; 170 } 171 } 172 p->rs_name = strdup(sectname); 173 SLIST_INIT(&p->rs_keys); 174 SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next); 175 return p; 176 } 177 178 static int 179 rc_freesect(struct rcfile *rcp, struct rcsection *rsp) 180 { 181 struct rckey *p,*n; 182 183 SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next); 184 for(p = SLIST_FIRST(&rsp->rs_keys);p;) { 185 n = p; 186 p = SLIST_NEXT(p,rk_next); 187 rc_key_free(n); 188 } 189 free(rsp->rs_name); 190 free(rsp); 191 return 0; 192 } 193 194 static struct rckey * 195 rc_sect_findkey(struct rcsection *rsp, const char *keyname) 196 { 197 struct rckey *p; 198 199 SLIST_FOREACH(p, &rsp->rs_keys, rk_next) 200 if (strcmp(p->rk_name, keyname)==0) 201 return p; 202 return NULL; 203 } 204 205 static struct rckey * 206 rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value) 207 { 208 struct rckey *p; 209 210 p = rc_sect_findkey(rsp, name); 211 if (p) { 212 free(p->rk_value); 213 } else { 214 p = malloc(sizeof(*p)); 215 if (!p) return NULL; 216 SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next); 217 p->rk_name = strdup(name); 218 } 219 p->rk_value = value ? strdup(value) : strdup(""); 220 return p; 221 } 222 223 #if 0 224 void 225 rc_sect_delkey(struct rcsection *rsp, struct rckey *p) 226 { 227 228 SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next); 229 rc_key_free(p); 230 return; 231 } 232 #endif 233 234 static void 235 rc_key_free(struct rckey *p) 236 { 237 free(p->rk_value); 238 free(p->rk_name); 239 free(p); 240 } 241 242 enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue}; 243 244 static void 245 rc_parse(struct rcfile *rcp) 246 { 247 FILE *f = rcp->rf_f; 248 int state = stNewLine, c; 249 struct rcsection *rsp = NULL; 250 struct rckey *rkp = NULL; 251 char buf[2048]; 252 char *next = buf, *last = &buf[sizeof(buf)-1]; 253 254 while ((c = getc (f)) != EOF) { 255 if (c == '\r') 256 continue; 257 if (state == stNewLine) { 258 next = buf; 259 if (isspace(c)) 260 continue; /* skip leading junk */ 261 if (c == '[') { 262 state = stHeader; 263 rsp = NULL; 264 continue; 265 } 266 if (c == '#' || c == ';') { 267 state = stSkipToEOL; 268 } else { /* something meaningfull */ 269 state = stGetKey; 270 } 271 } 272 if (state == stSkipToEOL || next == last) {/* ignore long lines */ 273 if (c == '\n'){ 274 state = stNewLine; 275 next = buf; 276 } 277 continue; 278 } 279 if (state == stHeader) { 280 if (c == ']') { 281 *next = 0; 282 next = buf; 283 rsp = rc_addsect(rcp, buf); 284 state = stSkipToEOL; 285 } else 286 *next++ = c; 287 continue; 288 } 289 if (state == stGetKey) { 290 if (c == ' ' || c == '\t')/* side effect: 'key name='*/ 291 continue; /* become 'keyname=' */ 292 if (c == '\n') { /* silently ignore ... */ 293 state = stNewLine; 294 continue; 295 } 296 if (c != '=') { 297 *next++ = c; 298 continue; 299 } 300 *next = 0; 301 if (rsp == NULL) { 302 fprintf(stderr, "Key '%s' defined before section\n", buf); 303 state = stSkipToEOL; 304 continue; 305 } 306 rkp = rc_sect_addkey(rsp, buf, NULL); 307 next = buf; 308 state = stGetValue; 309 continue; 310 } 311 /* only stGetValue left */ 312 if (state != stGetValue) { 313 fprintf(stderr, "Well, I can't parse file '%s'\n",rcp->rf_name); 314 state = stSkipToEOL; 315 } 316 if (c != '\n') { 317 *next++ = c; 318 continue; 319 } 320 *next = 0; 321 rkp->rk_value = strdup(buf); 322 state = stNewLine; 323 rkp = NULL; 324 } /* while */ 325 if (c == EOF && state == stGetValue) { 326 *next = 0; 327 rkp->rk_value = strdup(buf); 328 } 329 return; 330 } 331 332 int 333 rc_getstringptr(struct rcfile *rcp, const char *section, const char *key, 334 char **dest) 335 { 336 struct rcsection *rsp; 337 struct rckey *rkp; 338 339 *dest = NULL; 340 rsp = rc_findsect(rcp, section); 341 if (!rsp) return ENOENT; 342 rkp = rc_sect_findkey(rsp,key); 343 if (!rkp) return ENOENT; 344 *dest = rkp->rk_value; 345 return 0; 346 } 347 348 int 349 rc_getstring(struct rcfile *rcp, const char *section, const char *key, 350 size_t maxlen, char *dest) 351 { 352 char *value; 353 int error; 354 355 error = rc_getstringptr(rcp, section, key, &value); 356 if (error) 357 return error; 358 if (strlen(value) >= maxlen) { 359 warnx("line too long for key '%s' in section '%s', max = %zd\n", key, section, maxlen); 360 return EINVAL; 361 } 362 strcpy(dest, value); 363 return 0; 364 } 365 366 int 367 rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value) 368 { 369 struct rcsection *rsp; 370 struct rckey *rkp; 371 372 rsp = rc_findsect(rcp, section); 373 if (!rsp) 374 return ENOENT; 375 rkp = rc_sect_findkey(rsp, key); 376 if (!rkp) 377 return ENOENT; 378 errno = 0; 379 *value = strtol(rkp->rk_value, NULL, 0); 380 if (errno) { 381 warnx("invalid int value '%s' for key '%s' in section '%s'\n", rkp->rk_value, key, section); 382 return errno; 383 } 384 return 0; 385 } 386 387 /* 388 * 1,yes,true 389 * 0,no,false 390 */ 391 int 392 rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value) 393 { 394 struct rcsection *rsp; 395 struct rckey *rkp; 396 char *p; 397 398 rsp = rc_findsect(rcp, section); 399 if (!rsp) return ENOENT; 400 rkp = rc_sect_findkey(rsp,key); 401 if (!rkp) return ENOENT; 402 p = rkp->rk_value; 403 while (*p && isspace(*p)) p++; 404 if (*p == '0' || strcasecmp(p,"no") == 0 || strcasecmp(p,"false") == 0) { 405 *value = 0; 406 return 0; 407 } 408 if (*p == '1' || strcasecmp(p,"yes") == 0 || strcasecmp(p,"true") == 0) { 409 *value = 1; 410 return 0; 411 } 412 fprintf(stderr, "invalid boolean value '%s' for key '%s' in section '%s' \n",p, key, section); 413 return EINVAL; 414 } 415 416 /* 417 * Unified command line/rc file parser 418 */ 419 int 420 opt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect, 421 opt_callback_t *callback) 422 { 423 int len, error; 424 425 for (; ap->opt; ap++) { 426 switch (ap->type) { 427 case OPTARG_STR: 428 if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0) 429 break; 430 len = strlen(ap->str); 431 if (len > ap->ival) { 432 warnx("rc: argument for option '%c' (%s) too long\n", ap->opt, ap->name); 433 return EINVAL; 434 } 435 callback(ap); 436 break; 437 case OPTARG_BOOL: 438 error = rc_getbool(rcp, sect, ap->name, &ap->ival); 439 if (error == ENOENT) 440 break; 441 if (error) 442 return EINVAL; 443 callback(ap); 444 break; 445 case OPTARG_INT: 446 if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0) 447 break; 448 if (((ap->flag & OPTFL_HAVEMIN) && ap->ival < ap->min) || 449 ((ap->flag & OPTFL_HAVEMAX) && ap->ival > ap->max)) { 450 warnx("rc: argument for option '%c' (%s) should be in [%d-%d] range\n", 451 ap->opt, ap->name, ap->min, ap->max); 452 return EINVAL; 453 } 454 callback(ap); 455 break; 456 default: 457 break; 458 } 459 } 460 return 0; 461 } 462 463 int 464 opt_args_parseopt(struct opt_args *ap, int opt, char *arg, 465 opt_callback_t *callback) 466 { 467 int len; 468 469 for (; ap->opt; ap++) { 470 if (ap->opt != opt) 471 continue; 472 switch (ap->type) { 473 case OPTARG_STR: 474 ap->str = arg; 475 if (arg) { 476 len = strlen(ap->str); 477 if (len > ap->ival) { 478 warnx("opt: Argument for option '%c' (%s) too long\n", ap->opt, ap->name); 479 return EINVAL; 480 } 481 callback(ap); 482 } 483 break; 484 case OPTARG_BOOL: 485 ap->ival = 0; 486 callback(ap); 487 break; 488 case OPTARG_INT: 489 errno = 0; 490 ap->ival = strtol(arg, NULL, 0); 491 if (errno) { 492 warnx("opt: Invalid integer value for option '%c' (%s).\n",ap->opt,ap->name); 493 return EINVAL; 494 } 495 if (((ap->flag & OPTFL_HAVEMIN) && 496 (ap->ival < ap->min)) || 497 ((ap->flag & OPTFL_HAVEMAX) && 498 (ap->ival > ap->max))) { 499 warnx("opt: Argument for option '%c' (%s) should be in [%d-%d] range\n",ap->opt,ap->name,ap->min,ap->max); 500 return EINVAL; 501 } 502 callback(ap); 503 break; 504 default: 505 break; 506 } 507 break; 508 } 509 return 0; 510 } 511 512