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