1235537Sgber/*
2235537Sgber * Copyright (c) 1999, Boris Popov
3235537Sgber * All rights reserved.
4235537Sgber *
5235537Sgber * Redistribution and use in source and binary forms, with or without
6235537Sgber * modification, are permitted provided that the following conditions
7235537Sgber * are met:
8235537Sgber * 1. Redistributions of source code must retain the above copyright
9235537Sgber *    notice, this list of conditions and the following disclaimer.
10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright
11235537Sgber *    notice, this list of conditions and the following disclaimer in the
12235537Sgber *    documentation and/or other materials provided with the distribution.
13235537Sgber * 3. Neither the name of the author nor the names of any co-contributors
14235537Sgber *    may be used to endorse or promote products derived from this software
15235537Sgber *    without specific prior written permission.
16235537Sgber *
17235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20235537Sgber * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27235537Sgber * SUCH DAMAGE.
28235537Sgber *
29235537Sgber * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp
30235537Sgber */
31235537Sgber
32235537Sgber#include <sys/cdefs.h>
33235537Sgber__FBSDID("$FreeBSD$");
34235537Sgber#include <sys/types.h>
35235537Sgber#include <sys/queue.h>
36235537Sgber#include <ctype.h>
37235537Sgber#include <errno.h>
38235537Sgber#include <stdio.h>
39235537Sgber#include <string.h>
40235537Sgber#include <stdlib.h>
41235537Sgber#include <pwd.h>
42235537Sgber#include <unistd.h>
43235537Sgber
44235537Sgber#include "nandsim_rcfile.h"
45235537Sgber
46235537SgberSLIST_HEAD(rcfile_head, rcfile);
47235537Sgberstatic struct rcfile_head pf_head = {NULL};
48235537Sgberstatic struct rcsection *rc_findsect(struct rcfile *rcp,
49235537Sgber    const char *sectname, int sect_id);
50235537Sgberstatic struct rcsection *rc_addsect(struct rcfile *rcp,
51235537Sgber    const char *sectname);
52235537Sgberstatic int rc_sect_free(struct rcsection *rsp);
53235537Sgberstatic struct rckey *rc_sect_findkey(struct rcsection *rsp,
54235537Sgber    const char *keyname);
55235537Sgberstatic struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
56235537Sgber    char *value);
57235537Sgberstatic void rc_key_free(struct rckey *p);
58235537Sgberstatic void rc_parse(struct rcfile *rcp);
59235537Sgber
60235537Sgberstatic struct rcfile* rc_find(const char *filename);
61235537Sgber
62235537Sgber/*
63235537Sgber * open rcfile and load its content, if already open - return previous handle
64235537Sgber */
65235537Sgberint
66235537Sgberrc_open(const char *filename, const char *mode,struct rcfile **rcfile)
67235537Sgber{
68235537Sgber	struct rcfile *rcp;
69235537Sgber	FILE *f;
70235537Sgber	rcp = rc_find(filename);
71235537Sgber	if (rcp) {
72235537Sgber		*rcfile = rcp;
73235537Sgber		return (0);
74235537Sgber	}
75235537Sgber	f = fopen (filename, mode);
76235537Sgber	if (f == NULL)
77235537Sgber		return errno;
78235537Sgber	rcp = malloc(sizeof(struct rcfile));
79235537Sgber	if (rcp == NULL) {
80235537Sgber		fclose(f);
81235537Sgber		return ENOMEM;
82235537Sgber	}
83235537Sgber	bzero(rcp, sizeof(struct rcfile));
84235537Sgber	rcp->rf_name = strdup(filename);
85235537Sgber	rcp->rf_f = f;
86235537Sgber	SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
87235537Sgber	rc_parse(rcp);
88235537Sgber	*rcfile = rcp;
89235537Sgber	return (0);
90235537Sgber}
91235537Sgber
92235537Sgberint
93235537Sgberrc_close(struct rcfile *rcp)
94235537Sgber{
95235537Sgber	struct rcsection *p,*n;
96235537Sgber
97235537Sgber	fclose(rcp->rf_f);
98235537Sgber	for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
99235537Sgber		n = p;
100235537Sgber		p = SLIST_NEXT(p,rs_next);
101235537Sgber		rc_sect_free(n);
102235537Sgber	}
103235537Sgber	free(rcp->rf_name);
104235537Sgber	SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
105235537Sgber	free(rcp);
106235537Sgber	return (0);
107235537Sgber}
108235537Sgber
109235537Sgberstatic struct rcfile*
110235537Sgberrc_find(const char *filename)
111235537Sgber{
112235537Sgber	struct rcfile *p;
113235537Sgber
114235537Sgber	SLIST_FOREACH(p, &pf_head, rf_next)
115235537Sgber		if (strcmp (filename, p->rf_name) == 0)
116235537Sgber			return (p);
117235537Sgber	return (0);
118235537Sgber}
119235537Sgber
120235537Sgber/* Find section with given name and id */
121235537Sgberstatic struct rcsection *
122235537Sgberrc_findsect(struct rcfile *rcp, const char *sectname, int sect_id)
123235537Sgber{
124235537Sgber	struct rcsection *p;
125235537Sgber
126235537Sgber	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
127235537Sgber		if (strcmp(p->rs_name, sectname) == 0 && p->rs_id == sect_id)
128235537Sgber			return (p);
129235537Sgber	return (NULL);
130235537Sgber}
131235537Sgber
132235537Sgberstatic struct rcsection *
133235537Sgberrc_addsect(struct rcfile *rcp, const char *sectname)
134235537Sgber{
135235537Sgber	struct rcsection *p;
136235537Sgber	int id = 0;
137235537Sgber	p = rc_findsect(rcp, sectname, 0);
138235537Sgber	if (p) {
139235537Sgber		/*
140235537Sgber		 * If section with that name already exists -- add one more,
141235537Sgber		 * same named, but with different id (higher by one)
142235537Sgber		 */
143235537Sgber		while (p != NULL) {
144235537Sgber			id = p->rs_id + 1;
145235537Sgber			p = rc_findsect(rcp, sectname, id);
146235537Sgber		}
147235537Sgber	}
148235537Sgber	p = malloc(sizeof(*p));
149235537Sgber	if (!p)
150235537Sgber		return (NULL);
151235537Sgber	p->rs_name = strdup(sectname);
152235537Sgber	p->rs_id = id;
153235537Sgber	SLIST_INIT(&p->rs_keys);
154235537Sgber	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
155235537Sgber	return (p);
156235537Sgber}
157235537Sgber
158235537Sgberstatic int
159235537Sgberrc_sect_free(struct rcsection *rsp)
160235537Sgber{
161235537Sgber	struct rckey *p,*n;
162235537Sgber
163235537Sgber	for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
164235537Sgber		n = p;
165235537Sgber		p = SLIST_NEXT(p,rk_next);
166235537Sgber		rc_key_free(n);
167235537Sgber	}
168235537Sgber	free(rsp->rs_name);
169235537Sgber	free(rsp);
170235537Sgber	return (0);
171235537Sgber}
172235537Sgber
173235537Sgberstatic struct rckey *
174235537Sgberrc_sect_findkey(struct rcsection *rsp, const char *keyname)
175235537Sgber{
176235537Sgber	struct rckey *p;
177235537Sgber
178235537Sgber	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
179235537Sgber		if (strcmp(p->rk_name, keyname)==0)
180235537Sgber			return (p);
181235537Sgber	return (NULL);
182235537Sgber}
183235537Sgber
184235537Sgberstatic struct rckey *
185235537Sgberrc_sect_addkey(struct rcsection *rsp, const char *name, char *value)
186235537Sgber{
187235537Sgber	struct rckey *p;
188235537Sgber	p = rc_sect_findkey(rsp, name);
189235537Sgber	if (p) {
190235537Sgber		free(p->rk_value);
191235537Sgber	} else {
192235537Sgber		p = malloc(sizeof(*p));
193235537Sgber		if (!p)
194235537Sgber			return (NULL);
195235537Sgber		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
196235537Sgber		p->rk_name = strdup(name);
197235537Sgber	}
198235537Sgber	p->rk_value = value ? strdup(value) : strdup("");
199235537Sgber	return (p);
200235537Sgber}
201235537Sgber
202235537Sgberstatic void
203235537Sgberrc_key_free(struct rckey *p)
204235537Sgber{
205235537Sgber	free(p->rk_value);
206235537Sgber	free(p->rk_name);
207235537Sgber	free(p);
208235537Sgber}
209235537Sgber
210235537Sgberenum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
211235537Sgber
212235537Sgberstatic void
213235537Sgberrc_parse(struct rcfile *rcp)
214235537Sgber{
215235537Sgber	FILE *f = rcp->rf_f;
216235537Sgber	int state = stNewLine, c;
217235537Sgber	struct rcsection *rsp = NULL;
218235537Sgber	struct rckey *rkp = NULL;
219235537Sgber	char buf[2048];
220235537Sgber	char *next = buf, *last = &buf[sizeof(buf)-1];
221235537Sgber
222235537Sgber	while ((c = getc (f)) != EOF) {
223235537Sgber		if (c == '\r')
224235537Sgber			continue;
225235537Sgber		if (state == stNewLine) {
226235537Sgber			next = buf;
227235537Sgber			if (isspace(c))
228235537Sgber				continue;	/* skip leading junk */
229235537Sgber			if (c == '[') {
230235537Sgber				state = stHeader;
231235537Sgber				rsp = NULL;
232235537Sgber				continue;
233235537Sgber			}
234235537Sgber			if (c == '#' || c == ';') {
235235537Sgber				state = stSkipToEOL;
236235537Sgber			} else {		/* something meaningful */
237235537Sgber				state = stGetKey;
238235537Sgber			}
239235537Sgber		}
240235537Sgber		if (state == stSkipToEOL || next == last) {/* ignore long lines */
241235537Sgber			if (c == '\n') {
242235537Sgber				state = stNewLine;
243235537Sgber				next = buf;
244235537Sgber			}
245235537Sgber			continue;
246235537Sgber		}
247235537Sgber		if (state == stHeader) {
248235537Sgber			if (c == ']') {
249235537Sgber				*next = 0;
250235537Sgber				next = buf;
251235537Sgber				rsp = rc_addsect(rcp, buf);
252235537Sgber				state = stSkipToEOL;
253235537Sgber			} else
254235537Sgber				*next++ = c;
255235537Sgber			continue;
256235537Sgber		}
257235537Sgber		if (state == stGetKey) {
258235537Sgber			if (c == ' ' || c == '\t')/* side effect: 'key name='*/
259235537Sgber				continue;	  /* become 'keyname='	     */
260235537Sgber			if (c == '\n') {	  /* silently ignore ... */
261235537Sgber				state = stNewLine;
262235537Sgber				continue;
263235537Sgber			}
264235537Sgber			if (c != '=') {
265235537Sgber				*next++ = c;
266235537Sgber				continue;
267235537Sgber			}
268235537Sgber			*next = 0;
269235537Sgber			if (rsp == NULL) {
270235537Sgber				fprintf(stderr, "Key '%s' defined before "
271235537Sgber				    "section\n", buf);
272235537Sgber				state = stSkipToEOL;
273235537Sgber				continue;
274235537Sgber			}
275235537Sgber			rkp = rc_sect_addkey(rsp, buf, NULL);
276235537Sgber			next = buf;
277235537Sgber			state = stGetValue;
278235537Sgber			continue;
279235537Sgber		}
280235537Sgber		/* only stGetValue left */
281235537Sgber		if (state != stGetValue) {
282235537Sgber			fprintf(stderr, "Well, I can't parse file "
283235537Sgber			    "'%s'\n",rcp->rf_name);
284235537Sgber			state = stSkipToEOL;
285235537Sgber		}
286235537Sgber		if (c != '\n') {
287235537Sgber			*next++ = c;
288235537Sgber			continue;
289235537Sgber		}
290235537Sgber		*next = 0;
291235537Sgber		rkp->rk_value = strdup(buf);
292235537Sgber		state = stNewLine;
293235537Sgber		rkp = NULL;
294235537Sgber	}	/* while */
295235537Sgber	if (c == EOF && state == stGetValue) {
296235537Sgber		*next = 0;
297235537Sgber		rkp->rk_value = strdup(buf);
298235537Sgber	}
299235537Sgber}
300235537Sgber
301235537Sgberint
302235537Sgberrc_getstringptr(struct rcfile *rcp, const char *section, int sect_id,
303235537Sgber    const char *key, char **dest)
304235537Sgber{
305235537Sgber	struct rcsection *rsp;
306235537Sgber	struct rckey *rkp;
307235537Sgber
308235537Sgber	*dest = NULL;
309235537Sgber	rsp = rc_findsect(rcp, section, sect_id);
310235537Sgber	if (!rsp)
311235537Sgber		return (ENOENT);
312235537Sgber	rkp = rc_sect_findkey(rsp,key);
313235537Sgber	if (!rkp)
314235537Sgber		return (ENOENT);
315235537Sgber	*dest = rkp->rk_value;
316235537Sgber	return (0);
317235537Sgber}
318235537Sgber
319235537Sgberint
320235537Sgberrc_getstring(struct rcfile *rcp, const char *section, int sect_id,
321235537Sgber    const char *key, unsigned int maxlen, char *dest)
322235537Sgber{
323235537Sgber	char *value;
324235537Sgber	int error;
325235537Sgber
326235537Sgber	error = rc_getstringptr(rcp, section, sect_id, key, &value);
327235537Sgber	if (error)
328235537Sgber		return (error);
329235537Sgber	if (strlen(value) >= maxlen) {
330235537Sgber		fprintf(stderr, "line too long for key '%s' in section '%s',"
331235537Sgber		    "max = %d\n",key, section, maxlen);
332235537Sgber		return (EINVAL);
333235537Sgber	}
334235537Sgber	strcpy(dest,value);
335235537Sgber	return (0);
336235537Sgber}
337235537Sgber
338235537Sgberint
339235537Sgberrc_getint(struct rcfile *rcp, const char *section, int sect_id,
340235537Sgber    const char *key, int *value)
341235537Sgber{
342235537Sgber	struct rcsection *rsp;
343235537Sgber	struct rckey *rkp;
344235537Sgber
345235537Sgber	rsp = rc_findsect(rcp, section, sect_id);
346235537Sgber	if (!rsp)
347235537Sgber		return (ENOENT);
348235537Sgber	rkp = rc_sect_findkey(rsp,key);
349235537Sgber	if (!rkp)
350235537Sgber		return (ENOENT);
351235537Sgber	errno = 0;
352235537Sgber	*value = strtol(rkp->rk_value,NULL,0);
353235537Sgber	if (errno) {
354235537Sgber		fprintf(stderr, "invalid int value '%s' for key '%s' in "
355235537Sgber		    "section '%s'\n",rkp->rk_value,key,section);
356235537Sgber		return (errno);
357235537Sgber	}
358235537Sgber	return (0);
359235537Sgber}
360235537Sgber
361235537Sgber/*
362235537Sgber * 1,yes,true
363235537Sgber * 0,no,false
364235537Sgber */
365235537Sgberint
366235537Sgberrc_getbool(struct rcfile *rcp, const char *section, int sect_id,
367235537Sgber    const char *key, int *value)
368235537Sgber{
369235537Sgber	struct rcsection *rsp;
370235537Sgber	struct rckey *rkp;
371235537Sgber	char *p;
372235537Sgber
373235537Sgber	rsp = rc_findsect(rcp, section, sect_id);
374235537Sgber	if (!rsp)
375235537Sgber		return (ENOENT);
376235537Sgber	rkp = rc_sect_findkey(rsp,key);
377235537Sgber	if (!rkp)
378235537Sgber		return (ENOENT);
379235537Sgber	p = rkp->rk_value;
380235537Sgber	while (*p && isspace(*p)) p++;
381235537Sgber	if (*p == '0' || strcasecmp(p,"no") == 0 ||
382235537Sgber	    strcasecmp(p, "false") == 0 ||
383235537Sgber	    strcasecmp(p, "off") == 0) {
384235537Sgber		*value = 0;
385235537Sgber		return (0);
386235537Sgber	}
387235537Sgber	if (*p == '1' || strcasecmp(p,"yes") == 0 ||
388235537Sgber	    strcasecmp(p, "true") == 0 ||
389235537Sgber	    strcasecmp(p, "on") == 0) {
390235537Sgber		*value = 1;
391235537Sgber		return (0);
392235537Sgber	}
393235537Sgber	fprintf(stderr, "invalid boolean value '%s' for key '%s' in section "
394235537Sgber	    "'%s' \n",p, key, section);
395235537Sgber	return (EINVAL);
396235537Sgber}
397235537Sgber
398235537Sgber/* Count how many sections with given name exists in configuration. */
399235537Sgberint rc_getsectionscount(struct rcfile *f, const char *sectname)
400235537Sgber{
401235537Sgber	struct rcsection *p;
402235537Sgber	int count = 0;
403235537Sgber
404235537Sgber	p = rc_findsect(f, sectname, 0);
405235537Sgber	if (p) {
406235537Sgber		while (p != NULL) {
407235537Sgber			count = p->rs_id + 1;
408235537Sgber			p = rc_findsect(f, sectname, count);
409235537Sgber		}
410235537Sgber		return (count);
411235537Sgber	} else
412235537Sgber		return (0);
413235537Sgber}
414235537Sgber
415235537Sgberchar **
416235537Sgberrc_getkeys(struct rcfile *rcp, const char *sectname, int sect_id)
417235537Sgber{
418235537Sgber	struct rcsection *rsp;
419235537Sgber	struct rckey *p;
420235537Sgber	char **names_tbl;
421235537Sgber	int i = 0, count = 0;
422235537Sgber
423235537Sgber	rsp = rc_findsect(rcp, sectname, sect_id);
424235537Sgber	if (rsp == NULL)
425235537Sgber		return (NULL);
426235537Sgber
427235537Sgber	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
428235537Sgber		count++;
429235537Sgber
430235537Sgber	names_tbl = malloc(sizeof(char *) * (count + 1));
431235537Sgber	if (names_tbl == NULL)
432235537Sgber		return (NULL);
433235537Sgber
434235537Sgber	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
435235537Sgber		names_tbl[i++] = p->rk_name;
436235537Sgber
437235537Sgber	names_tbl[i] = NULL;
438235537Sgber	return (names_tbl);
439235537Sgber}
440235537Sgber
441