13 static struct cfg_comp
*parse_file(const char *path
, struct cfg_comp
*parent
, unsigned line
);
15 /* read a file and return it in a buffer. Size is stored in *len.
16 * If there are errors, return NULL and set *len to -errno */
17 static char *cfg_read_file(const char *path
, unsigned *len
)
19 int fd
, rd
= 0, total
= 0;
23 if (access(path
, R_OK
) < 0) {
25 fprintf(stderr
, "Failed to access '%s': %s\n", path
, strerror(errno
));
29 /* open, stat, malloc, read. caller handles errors (errno will be set) */
30 fd
= open(path
, O_RDONLY
);
33 fprintf(stderr
, "Failed to open '%s': %s\n", path
, strerror(errno
));
37 if (fstat(fd
, &st
) < 0) {
39 fprintf(stderr
, "Failed to stat '%s': %s\n", path
, strerror(errno
));
44 /* make room for a forced newline and null-termination */
45 buf
= malloc(st
.st_size
+ 3);
48 fprintf(stderr
, "Failed to allocate %lu bytes of memory for '%s'\n",
55 rd
= read(fd
, buf
+ rd
, st
.st_size
- rd
);
57 } while (total
< st
.st_size
&& rd
> 0);
59 /* preserve errno, so close() doesn't alter it */
63 if (rd
< 0 || total
!= st
.st_size
) {
64 fprintf(stderr
, "Reading from '%s' failed: %s\n", path
, strerror(*len
));
69 /* force newline+nul at EOF */
70 buf
[st
.st_size
] = '\n';
71 buf
[st
.st_size
+ 1] = '\0';
77 static struct cfg_comp
*start_compound(const char *name
, struct cfg_comp
*cur
, unsigned line
)
79 struct cfg_comp
*comp
= calloc(1, sizeof(struct cfg_comp
));
89 cur
->nest
= realloc(cur
->nest
, sizeof(struct cfg_comp
*) * cur
->nested
);
90 cur
->nest
[cur
->nested
- 1] = comp
;
96 static struct cfg_comp
*close_compound(struct cfg_comp
*comp
, unsigned line
)
106 static void add_var(struct cfg_comp
*comp
, struct cfg_var
*v
)
108 if (comp
->vars
>= comp
->vlist_len
) {
109 comp
->vlist_len
+= 5;
110 comp
->vlist
= realloc(comp
->vlist
, sizeof(struct cfg_var
*) * comp
->vlist_len
);
113 int vlen
= strlen(v
->value
) - 1;
114 while (ISSPACE(v
->value
[vlen
]))
115 v
->value
[vlen
--] = 0;
118 comp
->vlist
[comp
->vars
] = malloc(sizeof(struct cfg_var
));
119 memcpy(comp
->vlist
[comp
->vars
++], v
, sizeof(struct cfg_var
));
122 static inline char *end_of_line(char *s
)
134 if (*s
== '{' || *s
== '}')
144 static struct cfg_comp
*parse_file(const char *path
, struct cfg_comp
*parent
, unsigned line
)
146 unsigned compound_depth
= 0, buflen
, i
, lnum
= 0;
149 struct cfg_comp
*comp
;
150 char end
= '\n'; /* count like cat -n */
152 if (!(comp
= start_compound(path
, parent
, 0)))
155 if (!(buf
= cfg_read_file(path
, &buflen
))) {
160 comp
->buf
= buf
; /* save a pointer to free() later */
161 comp
->start
= comp
->end
= line
;
163 memset(&v
, 0, sizeof(v
));
164 for (i
= 0; i
< buflen
; i
++) {
165 char *next
, *lstart
, *lend
;
170 /* skipe whitespace */
171 while(ISSPACE(buf
[i
]))
176 while(buf
[i
] != '\n')
183 /* hop empty lines */
184 if (buf
[i
] == '\n') {
185 v
.key
= v
.value
= NULL
;
190 next
= lend
= end_of_line(&buf
[i
]);
194 /* nul-terminate and strip space from end of line */
196 while(ISSPACE(*lend
))
199 /* check for start of compound */
201 v
.key
= v
.value
= NULL
;
203 comp
= start_compound(lstart
, comp
, lnum
);
209 char *p
= lstart
+ 1;
214 while (p
< lend
&& !ISSPACE(*p
) && *p
!= '=')
217 if (ISSPACE(*p
) || *p
== '=') {
218 v
.key_len
= p
- &buf
[i
];
219 while(p
< lend
&& (ISSPACE(*p
) || *p
== '='))
227 if (v
.key
&& *v
.key
) {
229 v
.value_len
= 1 + lend
- v
.value
;
231 memset(&v
, 0, sizeof(v
));
235 comp
= close_compound(comp
, lnum
);
245 static void cfg_print_error(struct cfg_comp
*comp
, struct cfg_var
*v
,
246 const char *fmt
, va_list ap
)
250 fprintf(stderr
, "*** Configuration error\n");
252 fprintf(stderr
, " on line %d, near '%s' = '%s'\n",
253 v
->line
, v
->key
, v
->value
);
256 fprintf(stderr
, " in compound '%s' starting on line %d\n", comp
->name
, comp
->start
);
258 fprintf(stderr
, " in file ");
259 for (c
= comp
; c
; c
= c
->parent
) {
261 fprintf(stderr
, "'%s'", c
->name
);
264 fprintf(stderr
, "----\n");
265 vfprintf(stderr
, fmt
, ap
);
266 if (fmt
[strlen(fmt
) - 1] != '\n')
268 fprintf(stderr
, "----\n");
271 /** public functions **/
273 /* this is significantly faster than doing strdup(), since
274 * it can copy word-wise rather than byte-wise */
275 char *cfg_copy_value(struct cfg_var
*v
)
279 if ((ptr
= calloc(v
->value_len
+ 1, 1)))
280 return memcpy(ptr
, v
->value
, v
->value_len
);
285 void cfg_warn(struct cfg_comp
*comp
, struct cfg_var
*v
, const char *fmt
, ...)
290 cfg_print_error(comp
, v
, fmt
, ap
);
294 void cfg_error(struct cfg_comp
*comp
, struct cfg_var
*v
, const char *fmt
, ...)
299 cfg_print_error(comp
, v
, fmt
, ap
);
305 /* releases a compound and all its nested compounds recursively
306 * Note that comp->name is never free()'d since it either
307 * points to somewhere in comp->buf or is obtained from the caller
308 * and may point to the stack of some other function */
309 void cfg_destroy_compound(struct cfg_comp
*comp
)
316 /* free() children so this can be entered anywhere in the middle */
317 for (i
= 0; i
< comp
->nested
; i
++) {
318 cfg_destroy_compound(comp
->nest
[i
]);
322 for (i
= 0; i
< comp
->vars
; i
++)
323 free(comp
->vlist
[i
]);
337 /* If there is a parent we'll likely enter this compound again.
338 * If so, it mustn't try to free anything or read from any list,
339 * so zero the entire compound, but preserve the parent pointer. */
340 struct cfg_comp
*parent
= comp
->parent
;
341 memset(comp
, 0, sizeof(struct cfg_comp
));
342 comp
->parent
= parent
;
346 struct cfg_comp
*cfg_parse_file(const char *path
)
348 struct cfg_comp
*comp
= parse_file(path
, NULL
, 0);
350 /* this is the public API, so make sure all compounds are closed */
351 if (comp
&& comp
->parent
) {
352 cfg_error(comp
, NULL
, "Unclosed compound (there may be more)\n");