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