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