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
rc_open(const char * filename,const char * mode,struct rcfile ** rcfile)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
rc_merge(const char * filename,struct rcfile ** rcfile)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
rc_close(struct rcfile * rcp)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*
rc_cachelookup(const char * filename)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 *
rc_findsect(struct rcfile * rcp,const char * sectname)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 *
rc_addsect(struct rcfile * rcp,const char * sectname)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
rc_freesect(struct rcfile * rcp,struct rcsection * rsp)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 *
rc_sect_findkey(struct rcsection * rsp,const char * keyname)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 *
rc_sect_addkey(struct rcsection * rsp,const char * name,const char * value)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
rc_key_free(struct rckey * p)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
rc_parse(struct rcfile * rcp)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
rc_getstringptr(struct rcfile * rcp,const char * section,const char * key,char ** dest)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
rc_getstring(struct rcfile * rcp,const char * section,const char * key,size_t maxlen,char * dest)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
rc_getint(struct rcfile * rcp,const char * section,const char * key,int * value)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
rc_getbool(struct rcfile * rcp,const char * section,const char * key,int * value)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
opt_args_parse(struct rcfile * rcp,struct opt_args * ap,const char * sect,opt_callback_t * callback)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
opt_args_parseopt(struct opt_args * ap,int opt,char * arg,opt_callback_t * callback)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