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/types.h> 36 #include <sys/queue.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <stdlib.h> 42 #include <pwd.h> 43 #include <unistd.h> 44 #include <err.h> 45 46 #include <cflib.h> 47 #include "rcfile_priv.h" 48 49 SLIST_HEAD(rcfile_head, rcfile); 50 static struct rcfile_head pf_head = {NULL}; 51 52 static struct rcfile* rc_cachelookup(const char *filename); 53 static struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname); 54 static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname); 55 static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp); 56 static struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname); 57 static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value); 58 static void rc_key_free(struct rckey *p); 59 static void rc_parse(struct rcfile *rcp); 60 61 62 /* 63 * open rcfile and load its content, if already open - return previous handle 64 */ 65 int 66 rc_open(const char *filename, const char *mode, struct rcfile **rcfile) 67 { 68 struct rcfile *rcp; 69 FILE *f; 70 71 rcp = rc_cachelookup(filename); 72 if (rcp) { 73 *rcfile = rcp; 74 return 0; 75 } 76 f = fopen(filename, mode); 77 if (f == NULL) 78 return errno; 79 rcp = malloc(sizeof(struct rcfile)); 80 if (rcp == NULL) { 81 fclose(f); 82 return ENOMEM; 83 } 84 bzero(rcp, sizeof(struct rcfile)); 85 rcp->rf_name = strdup(filename); 86 rcp->rf_f = f; 87 SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); 88 rc_parse(rcp); 89 *rcfile = rcp; 90 return 0; 91 } 92 93 int 94 rc_merge(const char *filename, struct rcfile **rcfile) 95 { 96 struct rcfile *rcp = *rcfile; 97 FILE *f, *t; 98 99 if (rcp == NULL) { 100 return rc_open(filename, "r", rcfile); 101 } 102 f = fopen (filename, "r"); 103 if (f == NULL) 104 return errno; 105 t = rcp->rf_f; 106 rcp->rf_f = f; 107 rc_parse(rcp); 108 rcp->rf_f = t; 109 fclose(f); 110 return 0; 111 } 112 113 int 114 rc_close(struct rcfile *rcp) 115 { 116 struct rcsection *p, *n; 117 118 fclose(rcp->rf_f); 119 for(p = SLIST_FIRST(&rcp->rf_sect); p;) { 120 n = p; 121 p = SLIST_NEXT(p,rs_next); 122 rc_freesect(rcp, n); 123 } 124 free(rcp->rf_name); 125 SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next); 126 free(rcp); 127 return 0; 128 } 129 130 static struct rcfile* 131 rc_cachelookup(const char *filename) 132 { 133 struct rcfile *p; 134 135 SLIST_FOREACH(p, &pf_head, rf_next) 136 if (strcmp (filename, p->rf_name) == 0) 137 return p; 138 return 0; 139 } 140 141 static struct rcsection * 142 rc_findsect(struct rcfile *rcp, const char *sectname) 143 { 144 struct rcsection *p; 145 146 SLIST_FOREACH(p, &rcp->rf_sect, rs_next) 147 if (strcmp(p->rs_name, sectname)==0) 148 return p; 149 return NULL; 150 } 151 152 static struct rcsection * 153 rc_addsect(struct rcfile *rcp, const char *sectname) 154 { 155 struct rcsection *p; 156 const char* sectletter = sectname; 157 158 p = rc_findsect(rcp, sectname); 159 if (p) return p; 160 p = malloc(sizeof(*p)); 161 if (!p) return NULL; 162 for(sectletter = sectname; *sectletter; sectletter++) { 163 if (islower(*sectletter)) { 164 if (strcmp(sectname, "default")) 165 dprintf(STDERR_FILENO, "warning: section name [%s] contains lower-case letters\n", sectname); 166 break; 167 } 168 } 169 p->rs_name = strdup(sectname); 170 SLIST_INIT(&p->rs_keys); 171 SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next); 172 return p; 173 } 174 175 static int 176 rc_freesect(struct rcfile *rcp, struct rcsection *rsp) 177 { 178 struct rckey *p,*n; 179 180 SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next); 181 for(p = SLIST_FIRST(&rsp->rs_keys);p;) { 182 n = p; 183 p = SLIST_NEXT(p,rk_next); 184 rc_key_free(n); 185 } 186 free(rsp->rs_name); 187 free(rsp); 188 return 0; 189 } 190 191 static struct rckey * 192 rc_sect_findkey(struct rcsection *rsp, const char *keyname) 193 { 194 struct rckey *p; 195 196 SLIST_FOREACH(p, &rsp->rs_keys, rk_next) 197 if (strcmp(p->rk_name, keyname)==0) 198 return p; 199 return NULL; 200 } 201 202 static struct rckey * 203 rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value) 204 { 205 struct rckey *p; 206 207 p = rc_sect_findkey(rsp, name); 208 if (p) { 209 free(p->rk_value); 210 } else { 211 p = malloc(sizeof(*p)); 212 if (!p) return NULL; 213 SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next); 214 p->rk_name = strdup(name); 215 } 216 p->rk_value = value ? strdup(value) : strdup(""); 217 return p; 218 } 219 220 #if 0 221 void 222 rc_sect_delkey(struct rcsection *rsp, struct rckey *p) 223 { 224 225 SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next); 226 rc_key_free(p); 227 return; 228 } 229 #endif 230 231 static void 232 rc_key_free(struct rckey *p) 233 { 234 free(p->rk_value); 235 free(p->rk_name); 236 free(p); 237 } 238 239 enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue}; 240 241 static void 242 rc_parse(struct rcfile *rcp) 243 { 244 FILE *f = rcp->rf_f; 245 int state = stNewLine, c; 246 struct rcsection *rsp = NULL; 247 struct rckey *rkp = NULL; 248 char buf[2048]; 249 char *next = buf, *last = &buf[sizeof(buf)-1]; 250 251 while ((c = getc (f)) != EOF) { 252 if (c == '\r') 253 continue; 254 if (state == stNewLine) { 255 next = buf; 256 if (isspace(c)) 257 continue; /* skip leading junk */ 258 if (c == '[') { 259 state = stHeader; 260 rsp = NULL; 261 continue; 262 } 263 if (c == '#' || c == ';') { 264 state = stSkipToEOL; 265 } else { /* something meaningfull */ 266 state = stGetKey; 267 } 268 } 269 if (state == stSkipToEOL || next == last) {/* ignore long lines */ 270 if (c == '\n'){ 271 state = stNewLine; 272 next = buf; 273 } 274 continue; 275 } 276 if (state == stHeader) { 277 if (c == ']') { 278 *next = 0; 279 next = buf; 280 rsp = rc_addsect(rcp, buf); 281 state = stSkipToEOL; 282 } else 283 *next++ = c; 284 continue; 285 } 286 if (state == stGetKey) { 287 if (c == ' ' || c == '\t')/* side effect: 'key name='*/ 288 continue; /* become 'keyname=' */ 289 if (c == '\n') { /* silently ignore ... */ 290 state = stNewLine; 291 continue; 292 } 293 if (c != '=') { 294 *next++ = c; 295 continue; 296 } 297 *next = 0; 298 if (rsp == NULL) { 299 fprintf(stderr, "Key '%s' defined before section\n", buf); 300 state = stSkipToEOL; 301 continue; 302 } 303 rkp = rc_sect_addkey(rsp, buf, NULL); 304 next = buf; 305 state = stGetValue; 306 continue; 307 } 308 /* only stGetValue left */ 309 if (state != stGetValue) { 310 fprintf(stderr, "Well, I can't parse file '%s'\n",rcp->rf_name); 311 state = stSkipToEOL; 312 } 313 if (c != '\n') { 314 *next++ = c; 315 continue; 316 } 317 *next = 0; 318 rkp->rk_value = strdup(buf); 319 state = stNewLine; 320 rkp = NULL; 321 } /* while */ 322 if (c == EOF && state == stGetValue) { 323 *next = 0; 324 rkp->rk_value = strdup(buf); 325 } 326 return; 327 } 328 329 int 330 rc_getstringptr(struct rcfile *rcp, const char *section, const char *key, 331 char **dest) 332 { 333 struct rcsection *rsp; 334 struct rckey *rkp; 335 336 *dest = NULL; 337 rsp = rc_findsect(rcp, section); 338 if (!rsp) return ENOENT; 339 rkp = rc_sect_findkey(rsp,key); 340 if (!rkp) return ENOENT; 341 *dest = rkp->rk_value; 342 return 0; 343 } 344 345 int 346 rc_getstring(struct rcfile *rcp, const char *section, const char *key, 347 size_t maxlen, char *dest) 348 { 349 char *value; 350 int error; 351 352 error = rc_getstringptr(rcp, section, key, &value); 353 if (error) 354 return error; 355 if (strlen(value) >= maxlen) { 356 warnx("line too long for key '%s' in section '%s', max = %zd\n", key, section, maxlen); 357 return EINVAL; 358 } 359 strcpy(dest, value); 360 return 0; 361 } 362 363 int 364 rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value) 365 { 366 struct rcsection *rsp; 367 struct rckey *rkp; 368 369 rsp = rc_findsect(rcp, section); 370 if (!rsp) 371 return ENOENT; 372 rkp = rc_sect_findkey(rsp, key); 373 if (!rkp) 374 return ENOENT; 375 errno = 0; 376 *value = strtol(rkp->rk_value, NULL, 0); 377 if (errno) { 378 warnx("invalid int value '%s' for key '%s' in section '%s'\n", rkp->rk_value, key, section); 379 return errno; 380 } 381 return 0; 382 } 383 384 /* 385 * 1,yes,true 386 * 0,no,false 387 */ 388 int 389 rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value) 390 { 391 struct rcsection *rsp; 392 struct rckey *rkp; 393 char *p; 394 395 rsp = rc_findsect(rcp, section); 396 if (!rsp) return ENOENT; 397 rkp = rc_sect_findkey(rsp,key); 398 if (!rkp) return ENOENT; 399 p = rkp->rk_value; 400 while (*p && isspace(*p)) p++; 401 if (*p == '0' || strcasecmp(p,"no") == 0 || strcasecmp(p,"false") == 0) { 402 *value = 0; 403 return 0; 404 } 405 if (*p == '1' || strcasecmp(p,"yes") == 0 || strcasecmp(p,"true") == 0) { 406 *value = 1; 407 return 0; 408 } 409 fprintf(stderr, "invalid boolean value '%s' for key '%s' in section '%s' \n",p, key, section); 410 return EINVAL; 411 } 412 413 /* 414 * Unified command line/rc file parser 415 */ 416 int 417 opt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect, 418 opt_callback_t *callback) 419 { 420 int len, error; 421 422 for (; ap->opt; ap++) { 423 switch (ap->type) { 424 case OPTARG_STR: 425 if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0) 426 break; 427 len = strlen(ap->str); 428 if (len > ap->ival) { 429 warnx("rc: argument for option '%c' (%s) too long\n", ap->opt, ap->name); 430 return EINVAL; 431 } 432 callback(ap); 433 break; 434 case OPTARG_BOOL: 435 error = rc_getbool(rcp, sect, ap->name, &ap->ival); 436 if (error == ENOENT) 437 break; 438 if (error) 439 return EINVAL; 440 callback(ap); 441 break; 442 case OPTARG_INT: 443 if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0) 444 break; 445 if (((ap->flag & OPTFL_HAVEMIN) && ap->ival < ap->min) || 446 ((ap->flag & OPTFL_HAVEMAX) && ap->ival > ap->max)) { 447 warnx("rc: argument for option '%c' (%s) should be in [%d-%d] range\n", 448 ap->opt, ap->name, ap->min, ap->max); 449 return EINVAL; 450 } 451 callback(ap); 452 break; 453 default: 454 break; 455 } 456 } 457 return 0; 458 } 459 460 int 461 opt_args_parseopt(struct opt_args *ap, int opt, char *arg, 462 opt_callback_t *callback) 463 { 464 int len; 465 466 for (; ap->opt; ap++) { 467 if (ap->opt != opt) 468 continue; 469 switch (ap->type) { 470 case OPTARG_STR: 471 ap->str = arg; 472 if (arg) { 473 len = strlen(ap->str); 474 if (len > ap->ival) { 475 warnx("opt: Argument for option '%c' (%s) too long\n", ap->opt, ap->name); 476 return EINVAL; 477 } 478 callback(ap); 479 } 480 break; 481 case OPTARG_BOOL: 482 ap->ival = 0; 483 callback(ap); 484 break; 485 case OPTARG_INT: 486 errno = 0; 487 ap->ival = strtol(arg, NULL, 0); 488 if (errno) { 489 warnx("opt: Invalid integer value for option '%c' (%s).\n",ap->opt,ap->name); 490 return EINVAL; 491 } 492 if (((ap->flag & OPTFL_HAVEMIN) && 493 (ap->ival < ap->min)) || 494 ((ap->flag & OPTFL_HAVEMAX) && 495 (ap->ival > ap->max))) { 496 warnx("opt: Argument for option '%c' (%s) should be in [%d-%d] range\n",ap->opt,ap->name,ap->min,ap->max); 497 return EINVAL; 498 } 499 callback(ap); 500 break; 501 default: 502 break; 503 } 504 break; 505 } 506 return 0; 507 } 508 509