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