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