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 comp
->vlist
[comp
->vars
] = malloc(sizeof(struct cfg_var
));
114 memcpy(comp
->vlist
[comp
->vars
++], v
, sizeof(struct cfg_var
));
117 static inline char *end_of_line(char *s
)
129 if (*s
== '{' || *s
== '}')
139 static struct cfg_comp
*parse_file(const char *path
, struct cfg_comp
*parent
, unsigned line
)
141 unsigned compound_depth
= 0, buflen
, i
, lnum
= 0;
144 struct cfg_comp
*comp
;
145 char end
= '\n'; /* count like cat -n */
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 */
156 comp
->start
= comp
->end
= line
;
158 memset(&v
, 0, sizeof(v
));
159 for (i
= 0; i
< buflen
; i
++) {
160 char *next
, *lstart
, *lend
;
165 /* skipe whitespace */
166 while(ISSPACE(buf
[i
]))
171 while(buf
[i
] != '\n')
178 /* hop empty lines */
179 if (buf
[i
] == '\n') {
180 v
.key
= v
.value
= NULL
;
185 next
= lend
= end_of_line(&buf
[i
]);
189 /* nul-terminate and strip space from end of line */
191 while(ISSPACE(*lend
))
194 /* check for start of compound */
196 v
.key
= v
.value
= NULL
;
198 comp
= start_compound(lstart
, comp
, lnum
);
204 char *p
= lstart
+ 1;
209 while (p
< lend
&& !ISSPACE(*p
) && *p
!= '=')
212 if (ISSPACE(*p
) || *p
== '=') {
213 v
.key_len
= p
- &buf
[i
];
214 while(p
< lend
&& (ISSPACE(*p
) || *p
== '='))
222 if (v
.key
&& *v
.key
) {
224 v
.value_len
= 1 + lend
- v
.value
;
226 memset(&v
, 0, sizeof(v
));
230 comp
= close_compound(comp
, lnum
);
240 static void cfg_print_error(struct cfg_comp
*comp
, struct cfg_var
*v
,
241 const char *fmt
, va_list ap
)
245 fprintf(stderr
, "*** Configuration error\n");
247 fprintf(stderr
, " on line %d, near '%s' = '%s'\n",
248 v
->line
, v
->key
, v
->value
);
251 fprintf(stderr
, " in compound '%s' starting on line %d\n", comp
->name
, comp
->start
);
253 fprintf(stderr
, " in file ");
254 for (c
= comp
; c
; c
= c
->parent
) {
256 fprintf(stderr
, "'%s'", c
->name
);
259 fprintf(stderr
, "----\n");
260 vfprintf(stderr
, fmt
, ap
);
261 if (fmt
[strlen(fmt
) - 1] != '\n')
263 fprintf(stderr
, "----\n");
266 /** public functions **/
268 /* this is significantly faster than doing strdup(), since
269 * it can copy word-wise rather than byte-wise */
270 char *cfg_copy_value(struct cfg_var
*v
)
274 if ((ptr
= calloc(v
->value_len
+ 1, 1)))
275 return memcpy(ptr
, v
->value
, v
->value_len
);
280 void cfg_warn(struct cfg_comp
*comp
, struct cfg_var
*v
, const char *fmt
, ...)
285 cfg_print_error(comp
, v
, fmt
, ap
);
289 void cfg_error(struct cfg_comp
*comp
, struct cfg_var
*v
, const char *fmt
, ...)
294 cfg_print_error(comp
, v
, fmt
, ap
);
300 /* releases a compound and all its nested compounds recursively
301 * Note that comp->name is never free()'d since it either
302 * points to somewhere in comp->buf or is obtained from the caller
303 * and may point to the stack of some other function */
304 void cfg_destroy_compound(struct cfg_comp
*comp
)
311 /* free() children so this can be entered anywhere in the middle */
312 for (i
= 0; i
< comp
->nested
; i
++) {
313 cfg_destroy_compound(comp
->nest
[i
]);
317 for (i
= 0; i
< comp
->vars
; i
++)
318 free(comp
->vlist
[i
]);
332 /* If there is a parent we'll likely enter this compound again.
333 * If so, it mustn't try to free anything or read from any list,
334 * so zero the entire compound, but preserve the parent pointer. */
335 struct cfg_comp
*parent
= comp
->parent
;
336 memset(comp
, 0, sizeof(struct cfg_comp
));
337 comp
->parent
= parent
;
341 struct cfg_comp
*cfg_parse_file(const char *path
)
343 struct cfg_comp
*comp
= parse_file(path
, NULL
, 0);
345 /* this is the public API, so make sure all compounds are closed */
346 if (comp
&& comp
->parent
) {
347 cfg_error(comp
, NULL
, "Unclosed compound (there may be more)\n");