chiark / gitweb /
first try at implementing dependency loader
[elogind.git] / conf-parser.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <stdlib.h>
8
9 #include "conf-parser.h"
10 #include "util.h"
11 #include "macro.h"
12 #include "strv.h"
13
14 #define WHITESPACE " \t\n"
15 #define COMMENTS "#;\n"
16 #define LINE_MAX 4096
17
18 /* Run the user supplied parser for an assignment */
19 static int next_assignment(
20                 const char *filename,
21                 unsigned line,
22                 const char *section,
23                 const ConfigItem *t,
24                 const char *lvalue,
25                 const char *rvalue,
26                 void *userdata) {
27
28         assert(filename);
29         assert(t);
30         assert(lvalue);
31         assert(rvalue);
32
33         for (; t->parse; t++) {
34
35                 if (t->lvalue && !streq(lvalue, t->lvalue))
36                         continue;
37
38                 if (t->section && !section)
39                         continue;
40
41                 if (t->section && !streq(section, t->section))
42                         continue;
43
44                 return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
45         }
46
47         fprintf(stderr, "[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, strna(section));
48         return -EBADMSG;
49 }
50
51 /* Returns non-zero when c is contained in s */
52 static int in_string(char c, const char *s) {
53         assert(s);
54
55         for (; *s; s++)
56                 if (*s == c)
57                         return 1;
58
59         return 0;
60 }
61
62 /* Remove all whitepsapce from the beginning and the end of *s. *s may
63  * be modified. */
64 static char *strip(char *s) {
65         char *b = s+strspn(s, WHITESPACE);
66         char *e, *l = NULL;
67
68         for (e = b; *e; e++)
69                 if (!in_string(*e, WHITESPACE))
70                         l = e;
71
72         if (l)
73                 *(l+1) = 0;
74
75         return b;
76 }
77
78 /* Parse a variable assignment line */
79 static int parse_line(const char *filename, unsigned line, char **section, const ConfigItem *t, char *l, void *userdata) {
80         char *e, *c, *b;
81
82         b = l+strspn(l, WHITESPACE);
83
84         if ((c = strpbrk(b, COMMENTS)))
85                 *c = 0;
86
87         if (!*b)
88                 return 0;
89
90         if (startswith(b, ".include ")) {
91                 char *path = NULL, *fn;
92                 int r;
93
94                 fn = strip(b+9);
95                 if (!is_path_absolute(fn)) {
96                         const char *k;
97
98                         if ((k = strrchr(filename, '/'))) {
99                                 char *dir;
100
101                                 if (!(dir = strndup(filename, k-filename)))
102                                         return -ENOMEM;
103
104                                 if (asprintf(&path, "%s/%s", dir, fn) < 0)
105                                         return -errno;
106
107                                 fn = path;
108                                 free(dir);
109                         }
110                 }
111
112                 r = config_parse(fn, t, userdata);
113                 free(path);
114                 return r;
115         }
116
117         if (*b == '[') {
118                 size_t k;
119                 char *n;
120
121                 k = strlen(b);
122                 assert(k > 0);
123
124                 if (b[k-1] != ']') {
125                         fprintf(stderr, "[%s:%u] Invalid section header.", filename, line);
126                         return -EBADMSG;
127                 }
128
129                 if (!(n = strndup(b+1, k-2)))
130                         return -ENOMEM;
131
132                 free(*section);
133                 *section = n;
134
135                 return 0;
136         }
137
138         if (!(e = strchr(b, '='))) {
139                 fprintf(stderr, "[%s:%u] Missing '='.", filename, line);
140                 return -EBADMSG;
141         }
142
143         *e = 0;
144         e++;
145
146         return next_assignment(filename, line, *section, t, strip(b), strip(e), userdata);
147 }
148
149 /* Go through the file and parse each line */
150 int config_parse(const char *filename, const ConfigItem *t, void *userdata) {
151         unsigned line = 0;
152         char *section = NULL;
153         FILE *f;
154         int r;
155
156         assert(filename);
157         assert(t);
158
159         if (!(f = fopen(filename, "re"))) {
160                 r = -errno;
161                 fprintf(stderr, "Failed to open configuration file '%s': %s", filename, strerror(-r));
162                 goto finish;
163         }
164
165         while (!feof(f)) {
166                 char l[LINE_MAX];
167
168                 if (!fgets(l, sizeof(l), f)) {
169                         if (feof(f))
170                                 break;
171
172                         r = -errno;
173                         fprintf(stderr, "Failed to read configuration file '%s': %s", filename, strerror(-r));
174                         goto finish;
175                 }
176
177                 if ((r = parse_line(filename, ++line, &section, t, l, userdata)) < 0)
178                         goto finish;
179         }
180
181         r = 0;
182
183 finish:
184         free(section);
185
186         if (f)
187                 fclose(f);
188
189         return r;
190 }
191
192 int config_parse_int(
193                 const char *filename,
194                 unsigned line,
195                 const char *section,
196                 const char *lvalue,
197                 const char *rvalue,
198                 void *data,
199                 void *userdata) {
200
201         int *i = data;
202         int r;
203
204         assert(filename);
205         assert(lvalue);
206         assert(rvalue);
207         assert(data);
208
209         if ((r = safe_atoi(rvalue, i)) < 0) {
210                 fprintf(stderr, "[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
211                 return r;
212         }
213
214         return 0;
215 }
216
217 int config_parse_unsigned(
218                 const char *filename,
219                 unsigned line,
220                 const char *section,
221                 const char *lvalue,
222                 const char *rvalue,
223                 void *data,
224                 void *userdata) {
225
226         unsigned *u = data;
227         int r;
228
229         assert(filename);
230         assert(lvalue);
231         assert(rvalue);
232         assert(data);
233
234         if ((r = safe_atou(rvalue, u)) < 0) {
235                 fprintf(stderr, "[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
236                 return r;
237         }
238
239         return 0;
240 }
241
242 int config_parse_size(
243                 const char *filename,
244                 unsigned line,
245                 const char *section,
246                 const char *lvalue,
247                 const char *rvalue,
248                 void *data,
249                 void *userdata) {
250
251         size_t *sz = data;
252         unsigned u;
253         int r;
254
255         assert(filename);
256         assert(lvalue);
257         assert(rvalue);
258         assert(data);
259
260         if ((r = safe_atou(rvalue, &u)) < 0) {
261                 fprintf(stderr, "[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
262                 return r;
263         }
264
265         *sz = (size_t) u;
266         return 0;
267 }
268
269 int config_parse_bool(
270                 const char *filename,
271                 unsigned line,
272                 const char *section,
273                 const char *lvalue,
274                 const char *rvalue,
275                 void *data,
276                 void *userdata) {
277
278         int k;
279         bool *b = data;
280
281         assert(filename);
282         assert(lvalue);
283         assert(rvalue);
284         assert(data);
285
286         if ((k = parse_boolean(rvalue)) < 0) {
287                 fprintf(stderr, "[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
288                 return k;
289         }
290
291         *b = !!k;
292         return 0;
293 }
294
295 int config_parse_string(
296                 const char *filename,
297                 unsigned line,
298                 const char *section,
299                 const char *lvalue,
300                 const char *rvalue,
301                 void *data,
302                 void *userdata) {
303
304         char **s = data;
305         char *n;
306
307         assert(filename);
308         assert(lvalue);
309         assert(rvalue);
310         assert(data);
311
312         if (*rvalue) {
313                 if (!(n = strdup(rvalue)))
314                         return -ENOMEM;
315         } else
316                 n = NULL;
317
318         free(*s);
319         *s = n;
320
321         return 0;
322 }
323
324
325 int config_parse_strv(
326                 const char *filename,
327                 unsigned line,
328                 const char *section,
329                 const char *lvalue,
330                 const char *rvalue,
331                 void *data,
332                 void *userdata) {
333
334         char*** sv = data;
335         char **n;
336         char *w;
337         unsigned k;
338         size_t l;
339         char *state;
340
341         assert(filename);
342         assert(lvalue);
343         assert(rvalue);
344         assert(data);
345
346         k = strv_length(*sv);
347         FOREACH_WORD(w, &l, rvalue, state)
348                 k++;
349
350         if (!(n = new(char*, k+1)))
351                 return -ENOMEM;
352
353         for (k = 0; (*sv)[k]; k++)
354                 n[k] = (*sv)[k];
355         FOREACH_WORD(w, &l, rvalue, state)
356                 if (!(n[k++] = strndup(w, l)))
357                         goto fail;
358
359         n[k] = NULL;
360         free(*sv);
361         *sv = n;
362
363         return 0;
364
365 fail:
366         for (; k > 0; k--)
367                 free(n[k-1]);
368
369         return -ENOMEM;
370 }