ncpl_rcfile.c revision 52153
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. 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 * $FreeBSD: head/lib/libncp/ncpl_rcfile.c 52153 1999-10-12 11:56:41Z bp $
33 */
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 <netncp/ncp_lib.h>
45#include <netncp/ncp_rcfile.h>
46#include <netncp/ncp_cfg.h>
47
48#define NWFS_CFG_FILE	NCP_PREFIX"/etc/nwfs.conf"
49
50struct rcfile *ncp_rc = NULL;
51
52SLIST_HEAD(rcfile_head, rcfile);
53static struct rcfile_head pf_head = {NULL};
54
55int rc_merge(char *filename,struct rcfile **rcfile);
56static struct rcfile* rc_find(char *filename);
57static struct rcsection *rc_findsect(struct rcfile *rcp, char *sectname);
58static struct rcsection *rc_addsect(struct rcfile *rcp, char *sectname);
59static int rc_sect_free(struct rcsection *rsp);
60static struct rckey *rc_sect_findkey(struct rcsection *rsp, char *keyname);
61static struct rckey *rc_sect_addkey(struct rcsection *rsp, char *name, char *value);
62static void rc_key_free(struct rckey *p);
63static void rc_parse(struct rcfile *rcp);
64
65
66/*
67 * open rcfile and load its content, if already open - return previous handle
68 */
69int
70rc_open(char *filename,char *mode,struct rcfile **rcfile) {
71	struct rcfile *rcp;
72	FILE *f;
73
74	rcp = rc_find(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
96int
97rc_merge(char *filename,struct rcfile **rcfile) {
98	struct rcfile *rcp = *rcfile;
99	FILE *f, *t;
100
101	if (rcp == NULL) {
102		return rc_open(filename,"r",rcfile);
103	}
104	f = fopen (filename, "r");
105	if (f==NULL)
106		return errno;
107	t = rcp->rf_f;
108	rcp->rf_f = f;
109	rc_parse(rcp);
110	rcp->rf_f = t;
111	fclose(f);
112	return 0;
113}
114
115int
116rc_close(struct rcfile *rcp) {
117	struct rcsection *p,*n;
118
119	fclose(rcp->rf_f);
120	for(p = SLIST_FIRST(&rcp->rf_sect);p;) {
121		n = p;
122		p = SLIST_NEXT(p,rs_next);
123		rc_sect_free(n);
124	}
125	free(rcp->rf_name);
126	SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
127	free(rcp);
128	return 0;
129}
130
131static struct rcfile*
132rc_find(char *filename) {
133	struct rcfile *p;
134
135	SLIST_FOREACH(p, &pf_head, rf_next)
136		if (strcmp (filename, p->rf_name)==0)
137			return p;
138	return 0;
139}
140
141static struct rcsection *
142rc_findsect(struct rcfile *rcp, char *sectname) {
143	struct rcsection *p;
144
145	SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
146		if (strcmp(p->rs_name, sectname)==0)
147			return p;
148	return NULL;
149}
150
151static struct rcsection *
152rc_addsect(struct rcfile *rcp, char *sectname) {
153	struct rcsection *p;
154
155	p = rc_findsect(rcp, sectname);
156	if (p) return p;
157	p = malloc(sizeof(*p));
158	if (!p) return NULL;
159	p->rs_name = strdup(sectname);
160	SLIST_INIT(&p->rs_keys);
161	SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next);
162	return p;
163}
164
165static int
166rc_sect_free(struct rcsection *rsp) {
167	struct rckey *p,*n;
168
169	for(p = SLIST_FIRST(&rsp->rs_keys);p;) {
170		n = p;
171		p = SLIST_NEXT(p,rk_next);
172		rc_key_free(n);
173	}
174	free(rsp->rs_name);
175	free(rsp);
176	return 0;
177}
178
179static struct rckey *
180rc_sect_findkey(struct rcsection *rsp, char *keyname) {
181	struct rckey *p;
182
183	SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
184		if (strcmp(p->rk_name, keyname)==0)
185			return p;
186	return NULL;
187}
188
189static struct rckey *
190rc_sect_addkey(struct rcsection *rsp, char *name, char *value) {
191	struct rckey *p;
192
193	p = rc_sect_findkey(rsp, name);
194	if (p) {
195		free(p->rk_value);
196	} else {
197		p = malloc(sizeof(*p));
198		if (!p) return NULL;
199		SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next);
200		p->rk_name = strdup(name);
201	}
202	p->rk_value = value ? strdup(value) : strdup("");
203	return p;
204}
205
206void
207rc_sect_delkey(struct rcsection *rsp, struct rckey *p) {
208
209	SLIST_REMOVE(&rsp->rs_keys,p,rckey,rk_next);
210	rc_key_free(p);
211	return;
212}
213
214static void
215rc_key_free(struct rckey *p){
216	free(p->rk_value);
217	free(p->rk_name);
218	free(p);
219}
220
221enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
222
223static void
224rc_parse(struct rcfile *rcp) {
225	FILE *f = rcp->rf_f;
226	int state = stNewLine, c;
227	struct rcsection *rsp = NULL;
228	struct rckey *rkp = NULL;
229	char buf[2048];
230	char *next = buf, *last = &buf[sizeof(buf)-1];
231
232	while ((c = getc (f)) != EOF) {
233		if (c == '\r')
234			continue;
235		if (state == stNewLine) {
236			next = buf;
237			if (isspace(c))
238				continue;	/* skip leading junk */
239			if (c == '[') {
240				state = stHeader;
241				rsp = NULL;
242				continue;
243			}
244			if (c == '#' || c == ';') {
245				state = stSkipToEOL;
246			} else {		/* something meaningfull */
247				state = stGetKey;
248			}
249		}
250		if (state == stSkipToEOL || next == last) {/* ignore long lines */
251			if (c == '\n'){
252				state = stNewLine;
253				next = buf;
254			}
255			continue;
256		}
257		if (state == stHeader) {
258			if (c == ']') {
259				*next = 0;
260				next = buf;
261				rsp = rc_addsect(rcp, buf);
262				state = stSkipToEOL;
263			} else
264				*next++ = c;
265			continue;
266		}
267		if (state == stGetKey) {
268			if (c == ' ' || c == '\t')/* side effect: 'key name='*/
269				continue;	  /* become 'keyname=' 	     */
270			if (c == '\n') {		/* silently ignore ... */
271				state = stNewLine;
272				continue;
273			}
274			if (c != '=') {
275				*next++ = c;
276				continue;
277			}
278			*next = 0;
279			if (rsp == NULL) {
280				fprintf(stderr, "Key '%s' defined before section\n", buf);
281				state = stSkipToEOL;
282				continue;
283			}
284			rkp = rc_sect_addkey(rsp, buf, NULL);
285			next = buf;
286			state = stGetValue;
287			continue;
288		}
289		/* only stGetValue left */
290		if (state != stGetValue) {
291			fprintf(stderr, "Well, I can't parse file '%s'\n",rcp->rf_name);
292			state = stSkipToEOL;
293		}
294		if (c != '\n') {
295			*next++ = c;
296			continue;
297		}
298		*next = 0;
299		rkp->rk_value = strdup(buf);
300		state = stNewLine;
301		rkp = NULL;
302	} 	/* while */
303	if (c == EOF && state == stGetValue) {
304		*next = 0;
305		rkp->rk_value = strdup(buf);
306	}
307	return;
308}
309
310int
311rc_getstringptr(struct rcfile *rcp,char *section, char *key,char **dest) {
312	struct rcsection *rsp;
313	struct rckey *rkp;
314
315	*dest = NULL;
316	rsp = rc_findsect(rcp, section);
317	if (!rsp) return ENOENT;
318	rkp = rc_sect_findkey(rsp,key);
319	if (!rkp) return ENOENT;
320	*dest = rkp->rk_value;
321	return 0;
322}
323
324int
325rc_getstring(struct rcfile *rcp,char *section, char *key,int maxlen,char *dest) {
326	char *value;
327	int error;
328
329	error = rc_getstringptr(rcp, section, key, &value);
330	if (error) return error;
331	if (strlen(value) >= maxlen) {
332		fprintf(stderr, "line too long for key '%s' in section '%s', max = %d\n",key, section, maxlen);
333		return EINVAL;
334	}
335	strcpy(dest,value);
336	return 0;
337}
338
339int
340rc_getint(struct rcfile *rcp,char *section, char *key,int *value) {
341	struct rcsection *rsp;
342	struct rckey *rkp;
343
344	rsp = rc_findsect(rcp, section);
345	if (!rsp) return ENOENT;
346	rkp = rc_sect_findkey(rsp,key);
347	if (!rkp) return ENOENT;
348	errno = 0;
349	*value = strtol(rkp->rk_value,NULL,0);
350	if (errno) {
351		fprintf(stderr, "invalid int value '%s' for key '%s' in section '%s'\n",rkp->rk_value,key,section);
352		return errno;
353	}
354	return 0;
355}
356
357/*
358 * 1,yes,true
359 * 0,no,false
360 */
361int
362rc_getbool(struct rcfile *rcp,char *section, char *key,int *value) {
363	struct rcsection *rsp;
364	struct rckey *rkp;
365	char *p;
366
367	rsp = rc_findsect(rcp, section);
368	if (!rsp) return ENOENT;
369	rkp = rc_sect_findkey(rsp,key);
370	if (!rkp) return ENOENT;
371	p = rkp->rk_value;
372	while (*p && isspace(*p)) p++;
373	if (*p == '0' || strcasecmp(p,"no") == 0 || strcasecmp(p,"false") == 0) {
374		*value = 0;
375		return 0;
376	}
377	if (*p == '1' || strcasecmp(p,"yes") == 0 || strcasecmp(p,"true") == 0) {
378		*value = 1;
379		return 0;
380	}
381	fprintf(stderr, "invalid boolean value '%s' for key '%s' in section '%s' \n",p, key, section);
382	return EINVAL;
383}
384
385/*
386 * first read ~/.nwfsrc, next try to merge NWFS_CFG_FILE
387 */
388int
389ncp_open_rcfile(void) {
390	char *home, *fn;
391	int error;
392
393	home = getenv("HOME");
394	if (home) {
395		fn = malloc(strlen(home) + 20);
396		sprintf(fn, "%s/.nwfsrc", home);
397		error = rc_open(fn,"r",&ncp_rc);
398		free (fn);
399	}
400	error = rc_merge(NWFS_CFG_FILE, &ncp_rc);
401	if( ncp_rc == NULL ) {
402		printf("Warning: no cfg files found.\n");
403		return 1;
404	}
405	return 0;
406}
407
408