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