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