nandsim_rcfile.c revision 330897
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/11/usr.sbin/nandsim/nandsim_rcfile.c 330897 2018-03-14 03:19:51Z eadler $");
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
48SLIST_HEAD(rcfile_head, rcfile);
49static struct rcfile_head pf_head = {NULL};
50static struct rcsection *rc_findsect(struct rcfile *rcp,
51    const char *sectname, int sect_id);
52static struct rcsection *rc_addsect(struct rcfile *rcp,
53    const char *sectname);
54static int rc_sect_free(struct rcsection *rsp);
55static struct rckey *rc_sect_findkey(struct rcsection *rsp,
56    const char *keyname);
57static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
58    char *value);
59static void rc_key_free(struct rckey *p);
60static void rc_parse(struct rcfile *rcp);
61
62static struct rcfile* rc_find(const char *filename);
63
64/*
65 * open rcfile and load its content, if already open - return previous handle
66 */
67int
68rc_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
94int
95rc_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
111static struct rcfile*
112rc_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 */
123static struct rcsection *
124rc_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
134static struct rcsection *
135rc_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
160static int
161rc_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
175static struct rckey *
176rc_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
186static struct rckey *
187rc_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
204static void
205rc_key_free(struct rckey *p)
206{
207	free(p->rk_value);
208	free(p->rk_name);
209	free(p);
210}
211
212enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
213
214static void
215rc_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
303int
304rc_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
321int
322rc_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
340int
341rc_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 */
367int
368rc_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. */
401int 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
417char **
418rc_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