]> git.ipfire.org Git - thirdparty/systemd.git/blame - conf-parser.c
conf-parse: add generic parser for strv
[thirdparty/systemd.git] / conf-parser.c
CommitLineData
ed5bcfbe
LP
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"
57d42a5f 12#include "strv.h"
ed5bcfbe
LP
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 */
19static 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 */
52static 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. */
64static 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 */
79static 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 */
150int 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
183finish:
184 free(section);
185
186 if (f)
187 fclose(f);
188
189 return r;
190}
191
192int 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
217int 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
242int 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
269int 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
295int 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}
57d42a5f
LP
323
324
325int 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
365fail:
366 for (; k > 0; k--)
367 free(n[k-1]);
368
369 return -ENOMEM;
370}