1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1999, Boris Popov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the author nor the names of any co-contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: stable/12/usr.sbin/nandsim/nandsim_rcfile.c 326025 2017-11-20 19:49:47Z pfg $");
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <pwd.h>
44 #include <unistd.h>
45 
46 #include "nandsim_rcfile.h"
47 
48 SLIST_HEAD(rcfile_head, rcfile);
49 static struct rcfile_head pf_head = {NULL};
50 static struct rcsection *rc_findsect(struct rcfile *rcp,
51     const char *sectname, int sect_id);
52 static struct rcsection *rc_addsect(struct rcfile *rcp,
53     const char *sectname);
54 static int rc_sect_free(struct rcsection *rsp);
55 static struct rckey *rc_sect_findkey(struct rcsection *rsp,
56     const char *keyname);
57 static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
58     char *value);
59 static void rc_key_free(struct rckey *p);
60 static void rc_parse(struct rcfile *rcp);
61 
62 static struct rcfile* rc_find(const char *filename);
63 
64 /*
65  * open rcfile and load its content, if already open - return previous handle
66  */
67 int
rc_open(const char * filename,const char * mode,struct rcfile ** rcfile)68 rc_open(const char *filename, const char *mode,struct rcfile **rcfile)
69 {
70 	struct rcfile *rcp;
71 	FILE *f;
72 	rcp = rc_find(filename);
73 	if (rcp) {
74 		*rcfile = rcp;
75 		return (0);
76 	}
77 	f = fopen (filename, mode);
78 	if (f == NULL)
79 		return errno;
80 	rcp = malloc(sizeof(struct rcfile));
81 	if (rcp == NULL) {
82 		fclose(f);
83 		return ENOMEM;
84 	}
85 	bzero(rcp, sizeof(struct rcfile));
86 	rcp->rf_name = strdup(filename);
87 	rcp->rf_f = f;
88 	SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
89 	rc_parse(rcp);
90 	*rcfile = rcp;
91 	return (0);
92 }
93 
94 int
rc_close(struct rcfile * rcp)95 rc_close(struct rcfile *rcp)
96 {
97 	struct rcsection *p,*n;
98 
99 	fclose(rcp->rf_f);
100 	for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
101 		n = p;
102 		p = SLIST_NEXT(p,rs_next);
103 		rc_sect_free(n);
104 	}
105 	free(rcp->rf_name);
106 	SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
107 	free(rcp);
108 	return (0);
109 }
110 
111 static struct rcfile*
rc_find(const char * filename)112 rc_find(const char *filename)
113 {
114 	struct rcfile *p;
115 
116 	SLIST_FOREACH(p, &pf_head, rf_next)
117 		if (strcmp (filename, p->rf_name) == 0)
118 			return (p);
119 	return (0);
120 }
121 
122 /* Find section with given name and id */
123 static struct rcsection *
rc_findsect(struct rcfile * rcp,const char * sectname,int sect_id)124 rc_findsect(struct rcfile *rcp, const char *sectname, int sect_id)
125 {
126 	struct rcsection *p;
127 
128 	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
129 		if (strcmp(p->rs_name, sectname) == 0 && p->rs_id == sect_id)
130 			return (p);
131 	return (NULL);
132 }
133 
134 static struct rcsection *
rc_addsect(struct rcfile * rcp,const char * sectname)135 rc_addsect(struct rcfile *rcp, const char *sectname)
136 {
137 	struct rcsection *p;
138 	int id = 0;
139 	p = rc_findsect(rcp, sectname, 0);
140 	if (p) {
141 		/*
142 		 * If section with that name already exists -- add one more,
143 		 * same named, but with different id (higher by one)
144 		 */
145 		while (p != NULL) {
146 			id = p->rs_id + 1;
147 			p = rc_findsect(rcp, sectname, id);
148 		}
149 	}
150 	p = malloc(sizeof(*p));
151 	if (!p)
152 		return (NULL);
153 	p->rs_name = strdup(sectname);
154 	p->rs_id = id;
155 	SLIST_INIT(&p->rs_keys);
156 	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
157 	return (p);
158 }
159 
160 static int
rc_sect_free(struct rcsection * rsp)161 rc_sect_free(struct rcsection *rsp)
162 {
163 	struct rckey *p,*n;
164 
165 	for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
166 		n = p;
167 		p = SLIST_NEXT(p,rk_next);
168 		rc_key_free(n);
169 	}
170 	free(rsp->rs_name);
171 	free(rsp);
172 	return (0);
173 }
174 
175 static struct rckey *
rc_sect_findkey(struct rcsection * rsp,const char * keyname)176 rc_sect_findkey(struct rcsection *rsp, const char *keyname)
177 {
178 	struct rckey *p;
179 
180 	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
181 		if (strcmp(p->rk_name, keyname)==0)
182 			return (p);
183 	return (NULL);
184 }
185 
186 static struct rckey *
rc_sect_addkey(struct rcsection * rsp,const char * name,char * value)187 rc_sect_addkey(struct rcsection *rsp, const char *name, char *value)
188 {
189 	struct rckey *p;
190 	p = rc_sect_findkey(rsp, name);
191 	if (p) {
192 		free(p->rk_value);
193 	} else {
194 		p = malloc(sizeof(*p));
195 		if (!p)
196 			return (NULL);
197 		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
198 		p->rk_name = strdup(name);
199 	}
200 	p->rk_value = value ? strdup(value) : strdup("");
201 	return (p);
202 }
203 
204 static void
rc_key_free(struct rckey * p)205 rc_key_free(struct rckey *p)
206 {
207 	free(p->rk_value);
208 	free(p->rk_name);
209 	free(p);
210 }
211 
212 enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
213 
214 static void
rc_parse(struct rcfile * rcp)215 rc_parse(struct rcfile *rcp)
216 {
217 	FILE *f = rcp->rf_f;
218 	int state = stNewLine, c;
219 	struct rcsection *rsp = NULL;
220 	struct rckey *rkp = NULL;
221 	char buf[2048];
222 	char *next = buf, *last = &buf[sizeof(buf)-1];
223 
224 	while ((c = getc (f)) != EOF) {
225 		if (c == '\r')
226 			continue;
227 		if (state == stNewLine) {
228 			next = buf;
229 			if (isspace(c))
230 				continue;	/* skip leading junk */
231 			if (c == '[') {
232 				state = stHeader;
233 				rsp = NULL;
234 				continue;
235 			}
236 			if (c == '#' || c == ';') {
237 				state = stSkipToEOL;
238 			} else {		/* something meaningful */
239 				state = stGetKey;
240 			}
241 		}
242 		if (state == stSkipToEOL || next == last) {/* ignore long lines */
243 			if (c == '\n') {
244 				state = stNewLine;
245 				next = buf;
246 			}
247 			continue;
248 		}
249 		if (state == stHeader) {
250 			if (c == ']') {
251 				*next = 0;
252 				next = buf;
253 				rsp = rc_addsect(rcp, buf);
254 				state = stSkipToEOL;
255 			} else
256 				*next++ = c;
257 			continue;
258 		}
259 		if (state == stGetKey) {
260 			if (c == ' ' || c == '\t')/* side effect: 'key name='*/
261 				continue;	  /* become 'keyname='	     */
262 			if (c == '\n') {	  /* silently ignore ... */
263 				state = stNewLine;
264 				continue;
265 			}
266 			if (c != '=') {
267 				*next++ = c;
268 				continue;
269 			}
270 			*next = 0;
271 			if (rsp == NULL) {
272 				fprintf(stderr, "Key '%s' defined before "
273 				    "section\n", buf);
274 				state = stSkipToEOL;
275 				continue;
276 			}
277 			rkp = rc_sect_addkey(rsp, buf, NULL);
278 			next = buf;
279 			state = stGetValue;
280 			continue;
281 		}
282 		/* only stGetValue left */
283 		if (state != stGetValue) {
284 			fprintf(stderr, "Well, I can't parse file "
285 			    "'%s'\n",rcp->rf_name);
286 			state = stSkipToEOL;
287 		}
288 		if (c != '\n') {
289 			*next++ = c;
290 			continue;
291 		}
292 		*next = 0;
293 		rkp->rk_value = strdup(buf);
294 		state = stNewLine;
295 		rkp = NULL;
296 	}	/* while */
297 	if (c == EOF && state == stGetValue) {
298 		*next = 0;
299 		rkp->rk_value = strdup(buf);
300 	}
301 }
302 
303 int
rc_getstringptr(struct rcfile * rcp,const char * section,int sect_id,const char * key,char ** dest)304 rc_getstringptr(struct rcfile *rcp, const char *section, int sect_id,
305     const char *key, char **dest)
306 {
307 	struct rcsection *rsp;
308 	struct rckey *rkp;
309 
310 	*dest = NULL;
311 	rsp = rc_findsect(rcp, section, sect_id);
312 	if (!rsp)
313 		return (ENOENT);
314 	rkp = rc_sect_findkey(rsp,key);
315 	if (!rkp)
316 		return (ENOENT);
317 	*dest = rkp->rk_value;
318 	return (0);
319 }
320 
321 int
rc_getstring(struct rcfile * rcp,const char * section,int sect_id,const char * key,unsigned int maxlen,char * dest)322 rc_getstring(struct rcfile *rcp, const char *section, int sect_id,
323     const char *key, unsigned int maxlen, char *dest)
324 {
325 	char *value;
326 	int error;
327 
328 	error = rc_getstringptr(rcp, section, sect_id, key, &value);
329 	if (error)
330 		return (error);
331 	if (strlen(value) >= maxlen) {
332 		fprintf(stderr, "line too long for key '%s' in section '%s',"
333 		    "max = %d\n",key, section, maxlen);
334 		return (EINVAL);
335 	}
336 	strcpy(dest,value);
337 	return (0);
338 }
339 
340 int
rc_getint(struct rcfile * rcp,const char * section,int sect_id,const char * key,int * value)341 rc_getint(struct rcfile *rcp, const char *section, int sect_id,
342     const char *key, int *value)
343 {
344 	struct rcsection *rsp;
345 	struct rckey *rkp;
346 
347 	rsp = rc_findsect(rcp, section, sect_id);
348 	if (!rsp)
349 		return (ENOENT);
350 	rkp = rc_sect_findkey(rsp,key);
351 	if (!rkp)
352 		return (ENOENT);
353 	errno = 0;
354 	*value = strtol(rkp->rk_value,NULL,0);
355 	if (errno) {
356 		fprintf(stderr, "invalid int value '%s' for key '%s' in "
357 		    "section '%s'\n",rkp->rk_value,key,section);
358 		return (errno);
359 	}
360 	return (0);
361 }
362 
363 /*
364  * 1,yes,true
365  * 0,no,false
366  */
367 int
rc_getbool(struct rcfile * rcp,const char * section,int sect_id,const char * key,int * value)368 rc_getbool(struct rcfile *rcp, const char *section, int sect_id,
369     const char *key, int *value)
370 {
371 	struct rcsection *rsp;
372 	struct rckey *rkp;
373 	char *p;
374 
375 	rsp = rc_findsect(rcp, section, sect_id);
376 	if (!rsp)
377 		return (ENOENT);
378 	rkp = rc_sect_findkey(rsp,key);
379 	if (!rkp)
380 		return (ENOENT);
381 	p = rkp->rk_value;
382 	while (*p && isspace(*p)) p++;
383 	if (*p == '0' || strcasecmp(p,"no") == 0 ||
384 	    strcasecmp(p, "false") == 0 ||
385 	    strcasecmp(p, "off") == 0) {
386 		*value = 0;
387 		return (0);
388 	}
389 	if (*p == '1' || strcasecmp(p,"yes") == 0 ||
390 	    strcasecmp(p, "true") == 0 ||
391 	    strcasecmp(p, "on") == 0) {
392 		*value = 1;
393 		return (0);
394 	}
395 	fprintf(stderr, "invalid boolean value '%s' for key '%s' in section "
396 	    "'%s' \n",p, key, section);
397 	return (EINVAL);
398 }
399 
400 /* Count how many sections with given name exists in configuration. */
rc_getsectionscount(struct rcfile * f,const char * sectname)401 int rc_getsectionscount(struct rcfile *f, const char *sectname)
402 {
403 	struct rcsection *p;
404 	int count = 0;
405 
406 	p = rc_findsect(f, sectname, 0);
407 	if (p) {
408 		while (p != NULL) {
409 			count = p->rs_id + 1;
410 			p = rc_findsect(f, sectname, count);
411 		}
412 		return (count);
413 	} else
414 		return (0);
415 }
416 
417 char **
rc_getkeys(struct rcfile * rcp,const char * sectname,int sect_id)418 rc_getkeys(struct rcfile *rcp, const char *sectname, int sect_id)
419 {
420 	struct rcsection *rsp;
421 	struct rckey *p;
422 	char **names_tbl;
423 	int i = 0, count = 0;
424 
425 	rsp = rc_findsect(rcp, sectname, sect_id);
426 	if (rsp == NULL)
427 		return (NULL);
428 
429 	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
430 		count++;
431 
432 	names_tbl = malloc(sizeof(char *) * (count + 1));
433 	if (names_tbl == NULL)
434 		return (NULL);
435 
436 	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
437 		names_tbl[i++] = p->rk_name;
438 
439 	names_tbl[i] = NULL;
440 	return (names_tbl);
441 }
442 
443