2 * Copyright (c) 2000, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
32 * from: Id: rcfile.c,v 1.5 2001/04/16 12:46:46 bp Exp
35 #include <sys/cdefs.h>
36 __RCSID("$NetBSD: rcfile.c,v 1.4 2003/04/04 08:05:36 jdolecek Exp $");
38 #include <sys/types.h>
39 #include <sys/queue.h>
50 #include "rcfile_priv.h"
52 SLIST_HEAD(rcfile_head
, rcfile
);
53 static struct rcfile_head pf_head
= {NULL
};
55 static struct rcfile
* rc_cachelookup(const char *filename
);
56 static struct rcsection
*rc_findsect(struct rcfile
*rcp
, const char *sectname
);
57 static struct rcsection
*rc_addsect(struct rcfile
*rcp
, const char *sectname
);
58 static int rc_freesect(struct rcfile
*rcp
, struct rcsection
*rsp
);
59 static struct rckey
*rc_sect_findkey(struct rcsection
*rsp
, const char *keyname
);
60 static struct rckey
*rc_sect_addkey(struct rcsection
*rsp
, const char *name
, const char *value
);
61 static void rc_key_free(struct rckey
*p
);
62 static void rc_parse(struct rcfile
*rcp
);
66 * open rcfile and load its content, if already open - return previous handle
69 rc_open(const char *filename
, const char *mode
, struct rcfile
**rcfile
)
74 rcp
= rc_cachelookup(filename
);
79 f
= fopen(filename
, mode
);
82 rcp
= malloc(sizeof(struct rcfile
));
87 bzero(rcp
, sizeof(struct rcfile
));
88 rcp
->rf_name
= strdup(filename
);
90 SLIST_INSERT_HEAD(&pf_head
, rcp
, rf_next
);
97 rc_merge(const char *filename
, struct rcfile
**rcfile
)
99 struct rcfile
*rcp
= *rcfile
;
103 return rc_open(filename
, "r", rcfile
);
105 f
= fopen (filename
, "r");
117 rc_close(struct rcfile
*rcp
)
119 struct rcsection
*p
, *n
;
122 for(p
= SLIST_FIRST(&rcp
->rf_sect
); p
;) {
124 p
= SLIST_NEXT(p
,rs_next
);
128 SLIST_REMOVE(&pf_head
, rcp
, rcfile
, rf_next
);
133 static struct rcfile
*
134 rc_cachelookup(const char *filename
)
138 SLIST_FOREACH(p
, &pf_head
, rf_next
)
139 if (strcmp (filename
, p
->rf_name
) == 0)
144 static struct rcsection
*
145 rc_findsect(struct rcfile
*rcp
, const char *sectname
)
149 SLIST_FOREACH(p
, &rcp
->rf_sect
, rs_next
)
150 if (strcmp(p
->rs_name
, sectname
)==0)
155 static struct rcsection
*
156 rc_addsect(struct rcfile
*rcp
, const char *sectname
)
160 p
= rc_findsect(rcp
, sectname
);
162 p
= malloc(sizeof(*p
));
164 p
->rs_name
= strdup(sectname
);
165 SLIST_INIT(&p
->rs_keys
);
166 SLIST_INSERT_HEAD(&rcp
->rf_sect
, p
, rs_next
);
171 rc_freesect(struct rcfile
*rcp
, struct rcsection
*rsp
)
175 SLIST_REMOVE(&rcp
->rf_sect
, rsp
, rcsection
, rs_next
);
176 for(p
= SLIST_FIRST(&rsp
->rs_keys
);p
;) {
178 p
= SLIST_NEXT(p
,rk_next
);
186 static struct rckey
*
187 rc_sect_findkey(struct rcsection
*rsp
, const char *keyname
)
191 SLIST_FOREACH(p
, &rsp
->rs_keys
, rk_next
)
192 if (strcmp(p
->rk_name
, keyname
)==0)
197 static struct rckey
*
198 rc_sect_addkey(struct rcsection
*rsp
, const char *name
, const char *value
)
202 p
= rc_sect_findkey(rsp
, name
);
206 p
= malloc(sizeof(*p
));
208 SLIST_INSERT_HEAD(&rsp
->rs_keys
, p
, rk_next
);
209 p
->rk_name
= strdup(name
);
211 p
->rk_value
= value
? strdup(value
) : strdup("");
217 rc_sect_delkey(struct rcsection
*rsp
, struct rckey
*p
)
220 SLIST_REMOVE(&rsp
->rs_keys
, p
, rckey
, rk_next
);
227 rc_key_free(struct rckey
*p
)
234 enum { stNewLine
, stHeader
, stSkipToEOL
, stGetKey
, stGetValue
};
237 rc_parse(struct rcfile
*rcp
)
240 int state
= stNewLine
, c
;
241 struct rcsection
*rsp
= NULL
;
242 struct rckey
*rkp
= NULL
;
244 char *next
= buf
, *last
= &buf
[sizeof(buf
)-1];
246 while ((c
= getc (f
)) != EOF
) {
249 if (state
== stNewLine
) {
252 continue; /* skip leading junk */
258 if (c
== '#' || c
== ';') {
260 } else { /* something meaningfull */
264 if (state
== stSkipToEOL
|| next
== last
) {/* ignore long lines */
271 if (state
== stHeader
) {
275 rsp
= rc_addsect(rcp
, buf
);
281 if (state
== stGetKey
) {
282 if (c
== ' ' || c
== '\t')/* side effect: 'key name='*/
283 continue; /* become 'keyname=' */
284 if (c
== '\n') { /* silently ignore ... */
294 fprintf(stderr
, "Key '%s' defined before section\n", buf
);
298 rkp
= rc_sect_addkey(rsp
, buf
, NULL
);
303 /* only stGetValue left */
304 if (state
!= stGetValue
) {
305 fprintf(stderr
, "Well, I can't parse file '%s'\n",rcp
->rf_name
);
313 rkp
->rk_value
= strdup(buf
);
317 if (c
== EOF
&& state
== stGetValue
) {
319 rkp
->rk_value
= strdup(buf
);
325 rc_getstringptr(struct rcfile
*rcp
, const char *section
, const char *key
,
328 struct rcsection
*rsp
;
332 rsp
= rc_findsect(rcp
, section
);
333 if (!rsp
) return ENOENT
;
334 rkp
= rc_sect_findkey(rsp
,key
);
335 if (!rkp
) return ENOENT
;
336 *dest
= rkp
->rk_value
;
341 rc_getstring(struct rcfile
*rcp
, const char *section
, const char *key
,
342 size_t maxlen
, char *dest
)
347 error
= rc_getstringptr(rcp
, section
, key
, &value
);
350 if (strlen(value
) >= maxlen
) {
351 warnx("line too long for key '%s' in section '%s', max = %lu\n", key
, section
, (unsigned long)maxlen
);
359 rc_getint(struct rcfile
*rcp
, const char *section
, const char *key
, int *value
)
361 struct rcsection
*rsp
;
364 rsp
= rc_findsect(rcp
, section
);
367 rkp
= rc_sect_findkey(rsp
, key
);
371 *value
= strtol(rkp
->rk_value
, NULL
, 0);
373 warnx("invalid int value '%s' for key '%s' in section '%s'\n", rkp
->rk_value
, key
, section
);
384 rc_getbool(struct rcfile
*rcp
, const char *section
, const char *key
, int *value
)
386 struct rcsection
*rsp
;
390 rsp
= rc_findsect(rcp
, section
);
391 if (!rsp
) return ENOENT
;
392 rkp
= rc_sect_findkey(rsp
,key
);
393 if (!rkp
) return ENOENT
;
395 while (*p
&& isspace((unsigned char)*p
)) p
++;
396 if (*p
== '0' || strcasecmp(p
,"no") == 0 || strcasecmp(p
,"false") == 0) {
400 if (*p
== '1' || strcasecmp(p
,"yes") == 0 || strcasecmp(p
,"true") == 0) {
404 fprintf(stderr
, "invalid boolean value '%s' for key '%s' in section '%s' \n",p
, key
, section
);
409 * Unified command line/rc file parser
412 opt_args_parse(struct rcfile
*rcp
, struct opt_args
*ap
, const char *sect
,
413 opt_callback_t
*callback
)
417 for (; ap
->opt
; ap
++) {
420 if (rc_getstringptr(rcp
, sect
, ap
->name
, &ap
->str
) != 0)
422 len
= strlen(ap
->str
);
423 if (len
> ap
->ival
) {
424 warnx("rc: argument for option '%c' (%s) too long\n", ap
->opt
, ap
->name
);
430 error
= rc_getbool(rcp
, sect
, ap
->name
, &ap
->ival
);
438 if (rc_getint(rcp
, sect
, ap
->name
, &ap
->ival
) != 0)
440 if (((ap
->flag
& OPTFL_HAVEMIN
) && ap
->ival
< ap
->min
) ||
441 ((ap
->flag
& OPTFL_HAVEMAX
) && ap
->ival
> ap
->max
)) {
442 warnx("rc: argument for option '%c' (%s) should be in [%d-%d] range\n",
443 ap
->opt
, ap
->name
, ap
->min
, ap
->max
);
456 opt_args_parseopt(struct opt_args
*ap
, int opt
, char *arg
,
457 opt_callback_t
*callback
)
461 for (; ap
->opt
; ap
++) {
468 len
= strlen(ap
->str
);
469 if (len
> ap
->ival
) {
470 warnx("opt: Argument for option '%c' (%s) too long\n", ap
->opt
, ap
->name
);
482 ap
->ival
= strtol(arg
, NULL
, 0);
484 warnx("opt: Invalid integer value for option '%c' (%s).\n",ap
->opt
,ap
->name
);
487 if (((ap
->flag
& OPTFL_HAVEMIN
) &&
488 (ap
->ival
< ap
->min
)) ||
489 ((ap
->flag
& OPTFL_HAVEMAX
) &&
490 (ap
->ival
> ap
->max
))) {
491 warnx("opt: Argument for option '%c' (%s) should be in [%d-%d] range\n",ap
->opt
,ap
->name
,ap
->min
,ap
->max
);