14 /* have a care with this one. It can't be used when (c) has side-effects */
16 #define ISSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r')
18 /* read a file and return it in a buffer. Size is stored in *len.
19 * If there are errors, return NULL and set *len to -errno */
20 static char *cfg_read_file(const char *path
, unsigned *len
)
22 int fd
, rd
= 0, total
= 0;
26 /* open, stat, malloc, read. caller handles errors (errno will be set) */
27 fd
= open(path
, O_RDONLY
);
30 fprintf(stderr
, "Failed to open '%s': %s\n", path
, strerror(errno
));
34 if (fstat(fd
, &st
) < 0) {
36 fprintf(stderr
, "Failed to stat '%s': %s\n", path
, strerror(errno
));
41 /* make room for a forced newline and null-termination */
42 buf
= malloc(st
.st_size
+ 3);
45 fprintf(stderr
, "Failed to allocate %lld bytes of memory for '%s'\n",
46 (long long)st
.st_size
, path
);
52 rd
= read(fd
, buf
+ rd
, st
.st_size
- rd
);
54 } while (total
< st
.st_size
&& rd
> 0);
56 /* preserve errno, so close() doesn't alter it */
60 if (rd
< 0 || total
!= st
.st_size
) {
61 fprintf(stderr
, "Reading from '%s' failed: %s\n", path
, strerror(*len
));
66 /* force newline+nul at EOF */
67 buf
[st
.st_size
] = '\n';
68 buf
[st
.st_size
+ 1] = '\0';
74 static struct cfg_comp
*start_compound(const char *name
, struct cfg_comp
*cur
, unsigned line
)
76 struct cfg_comp
*comp
= calloc(1, sizeof(struct cfg_comp
));
79 int namelen
= strlen(name
);
81 comp
->name
= strdup(name
);
82 while (ISSPACE(comp
->name
[namelen
- 1])) {
83 comp
->name
[--namelen
] = 0;
90 cur
->nest
= realloc(cur
->nest
, sizeof(struct cfg_comp
*) * cur
->nested
);
91 cur
->nest
[cur
->nested
- 1] = comp
;
97 static struct cfg_comp
*close_compound(struct cfg_comp
*comp
, unsigned line
)
101 cfg_error(comp
, NULL
, "Compound closed on line %d was never opened\n", line
);
109 static void add_var(struct cfg_comp
*comp
, struct cfg_var
*v
)
112 cfg_error(NULL
, v
, "Adding variable to NULL compound. Weird that...\n");
113 if (comp
->vars
>= comp
->vlist_len
) {
114 comp
->vlist_len
+= 5;
115 comp
->vlist
= realloc(comp
->vlist
, sizeof(struct cfg_var
*) * comp
->vlist_len
);
118 int vlen
= strlen(v
->value
) - 1;
119 while (ISSPACE(v
->value
[vlen
]))
120 v
->value
[vlen
--] = 0;
123 comp
->vlist
[comp
->vars
] = malloc(sizeof(struct cfg_var
));
124 memcpy(comp
->vlist
[comp
->vars
++], v
, sizeof(struct cfg_var
));
128 static inline char *strchrnul(const char *s
, int c
)
133 if (s
[i
] == 0 || s
[i
] == c
)
140 static struct cfg_comp
*parse_file(const char *path
, struct cfg_comp
*parent
, unsigned line
)
142 unsigned compound_depth
= 0, buflen
, i
, lnum
= 0;
145 struct cfg_comp
*comp
;
147 if (!(comp
= start_compound(path
, parent
, 0)))
150 if (!(buf
= cfg_read_file(path
, &buflen
))) {
155 comp
->buf
= buf
; /* save a pointer to free() later */
158 memset(&v
, 0, sizeof(v
));
159 for (i
= 0; i
< buflen
; i
++) {
160 char *next
, *lstart
, *lend
;
163 /* skip whitespace */
164 while (ISSPACE(buf
[i
]))
167 /* skip empty lines */
168 if (buf
[i
] == '\n') {
169 v
.key
= v
.value
= NULL
;
176 while(buf
[i
] != '\n')
182 /* check for compound closure */
184 v
.key
= v
.value
= NULL
;
186 comp
= close_compound(comp
, lnum
);
190 /* we have a real line, so set the starting point */
193 /* locate next newline */
194 next
= lend
= strchrnul(&buf
[i
], '\n');
195 while ((ISSPACE(*lend
) || *lend
== '\n') && lend
> lstart
)
198 /* check for start of compound */
202 /* nul-terminate and strip space from end of line */
203 while(ISSPACE(*lend
) && lend
> lstart
)
206 v
.key
= v
.value
= NULL
;
208 comp
= start_compound(lstart
, comp
, lnum
);
211 } else if (*lend
== ';' && lend
[-1] != '\\') {
213 while (ISSPACE(*lend
) && lend
> lstart
)
218 char *p
= lstart
+ 1;
224 while (p
< lend
&& !ISSPACE(*p
) && *p
!= '=')
229 if (ISSPACE(*p
) || *p
== '=') {
230 v
.key_len
= p
- &buf
[i
];
231 while(p
<= lend
&& (ISSPACE(*p
) || *p
== '='))
234 if (*p
&& p
<= lend
&& p
> split
)
239 if (v
.key
&& *v
.key
) {
241 v
.value_len
= 1 + lend
- v
.value
;
243 memset(&v
, 0, sizeof(v
));
252 static void cfg_print_error(struct cfg_comp
*comp
, struct cfg_var
*v
,
253 const char *fmt
, va_list ap
)
257 fprintf(stderr
, "*** Configuration error\n");
259 fprintf(stderr
, " on line %d, near '%s' = '%s'\n",
260 v
->line
, v
->key
, v
->value
);
263 fprintf(stderr
, " in compound '%s' starting on line %d\n", comp
->name
, comp
->start
);
265 for (c
= comp
; c
; c
= c
->parent
) {
268 fprintf(stderr
, " in file '%s'\n", c
->name
);
271 fprintf(stderr
, "----\n");
272 vfprintf(stderr
, fmt
, ap
);
273 if (fmt
[strlen(fmt
) - 1] != '\n')
275 fprintf(stderr
, "----\n");
278 /** public functions **/
279 void cfg_warn(struct cfg_comp
*comp
, struct cfg_var
*v
, const char *fmt
, ...)
284 cfg_print_error(comp
, v
, fmt
, ap
);
288 void cfg_error(struct cfg_comp
*comp
, struct cfg_var
*v
, const char *fmt
, ...)
293 cfg_print_error(comp
, v
, fmt
, ap
);
299 /* releases a compound and all its nested compounds recursively
300 * Note that comp->name is never free()'d since it either
301 * points to somewhere in comp->buf or is obtained from the caller
302 * and may point to the stack of some other function */
303 void cfg_destroy_compound(struct cfg_comp
*comp
)
310 /* free() children so this can be entered anywhere in the middle */
311 for (i
= 0; i
< comp
->nested
; i
++) {
312 cfg_destroy_compound(comp
->nest
[i
]);
315 for (i
= 0; i
< comp
->vars
; i
++)
316 free(comp
->vlist
[i
]);
333 struct cfg_comp
*cfg_parse_file(const char *path
)
335 struct cfg_comp
*comp
;
339 comp
= parse_file(path
, NULL
, 0);
341 /* this is the public API, so make sure all compounds are closed */
342 if (comp
&& comp
->parent
) {
343 cfg_error(comp
, NULL
, "Unclosed compound (there may be more)\n");