xref: /freebsd/contrib/smbfs/lib/smb/rcfile.c (revision 10ff414c)
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