]>
Commit | Line | Data |
---|---|---|
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" |
16354eff | 13 | #include "log.h" |
ed5bcfbe | 14 | |
ed5bcfbe LP |
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 | ||
16354eff | 47 | log_error("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, strna(section)); |
ed5bcfbe LP |
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 */ | |
42f4e3c4 | 79 | static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) { |
ed5bcfbe LP |
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 | ||
42f4e3c4 | 112 | r = config_parse(fn, sections, t, userdata); |
ed5bcfbe LP |
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] != ']') { | |
16354eff | 125 | log_error("[%s:%u] Invalid section header.", filename, line); |
ed5bcfbe LP |
126 | return -EBADMSG; |
127 | } | |
128 | ||
129 | if (!(n = strndup(b+1, k-2))) | |
130 | return -ENOMEM; | |
131 | ||
42f4e3c4 LP |
132 | if (sections) { |
133 | const char * const * i; | |
134 | bool good = false; | |
135 | STRV_FOREACH(i, sections) | |
136 | if (streq(*i, n)) { | |
137 | good = true; | |
138 | break; | |
139 | } | |
140 | ||
141 | if (!good) { | |
142 | free(n); | |
143 | return -EBADMSG; | |
144 | } | |
145 | } | |
146 | ||
ed5bcfbe LP |
147 | free(*section); |
148 | *section = n; | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | if (!(e = strchr(b, '='))) { | |
16354eff | 154 | log_error("[%s:%u] Missing '='.", filename, line); |
ed5bcfbe LP |
155 | return -EBADMSG; |
156 | } | |
157 | ||
158 | *e = 0; | |
159 | e++; | |
160 | ||
161 | return next_assignment(filename, line, *section, t, strip(b), strip(e), userdata); | |
162 | } | |
163 | ||
164 | /* Go through the file and parse each line */ | |
42f4e3c4 | 165 | int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata) { |
ed5bcfbe LP |
166 | unsigned line = 0; |
167 | char *section = NULL; | |
168 | FILE *f; | |
169 | int r; | |
170 | ||
171 | assert(filename); | |
172 | assert(t); | |
173 | ||
174 | if (!(f = fopen(filename, "re"))) { | |
175 | r = -errno; | |
16354eff | 176 | log_error("Failed to open configuration file '%s': %s", filename, strerror(-r)); |
ed5bcfbe LP |
177 | goto finish; |
178 | } | |
179 | ||
180 | while (!feof(f)) { | |
181 | char l[LINE_MAX]; | |
182 | ||
183 | if (!fgets(l, sizeof(l), f)) { | |
184 | if (feof(f)) | |
185 | break; | |
186 | ||
187 | r = -errno; | |
16354eff | 188 | log_error("Failed to read configuration file '%s': %s", filename, strerror(-r)); |
ed5bcfbe LP |
189 | goto finish; |
190 | } | |
191 | ||
42f4e3c4 | 192 | if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0) |
ed5bcfbe LP |
193 | goto finish; |
194 | } | |
195 | ||
196 | r = 0; | |
197 | ||
198 | finish: | |
199 | free(section); | |
200 | ||
201 | if (f) | |
202 | fclose(f); | |
203 | ||
204 | return r; | |
205 | } | |
206 | ||
207 | int config_parse_int( | |
208 | const char *filename, | |
209 | unsigned line, | |
210 | const char *section, | |
211 | const char *lvalue, | |
212 | const char *rvalue, | |
213 | void *data, | |
214 | void *userdata) { | |
215 | ||
216 | int *i = data; | |
217 | int r; | |
218 | ||
219 | assert(filename); | |
220 | assert(lvalue); | |
221 | assert(rvalue); | |
222 | assert(data); | |
223 | ||
224 | if ((r = safe_atoi(rvalue, i)) < 0) { | |
16354eff | 225 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
226 | return r; |
227 | } | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | int config_parse_unsigned( | |
233 | const char *filename, | |
234 | unsigned line, | |
235 | const char *section, | |
236 | const char *lvalue, | |
237 | const char *rvalue, | |
238 | void *data, | |
239 | void *userdata) { | |
240 | ||
241 | unsigned *u = data; | |
242 | int r; | |
243 | ||
244 | assert(filename); | |
245 | assert(lvalue); | |
246 | assert(rvalue); | |
247 | assert(data); | |
248 | ||
249 | if ((r = safe_atou(rvalue, u)) < 0) { | |
16354eff | 250 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
251 | return r; |
252 | } | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | int config_parse_size( | |
258 | const char *filename, | |
259 | unsigned line, | |
260 | const char *section, | |
261 | const char *lvalue, | |
262 | const char *rvalue, | |
263 | void *data, | |
264 | void *userdata) { | |
265 | ||
266 | size_t *sz = data; | |
267 | unsigned u; | |
268 | int r; | |
269 | ||
270 | assert(filename); | |
271 | assert(lvalue); | |
272 | assert(rvalue); | |
273 | assert(data); | |
274 | ||
275 | if ((r = safe_atou(rvalue, &u)) < 0) { | |
16354eff | 276 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
277 | return r; |
278 | } | |
279 | ||
280 | *sz = (size_t) u; | |
281 | return 0; | |
282 | } | |
283 | ||
284 | int config_parse_bool( | |
285 | const char *filename, | |
286 | unsigned line, | |
287 | const char *section, | |
288 | const char *lvalue, | |
289 | const char *rvalue, | |
290 | void *data, | |
291 | void *userdata) { | |
292 | ||
293 | int k; | |
294 | bool *b = data; | |
295 | ||
296 | assert(filename); | |
297 | assert(lvalue); | |
298 | assert(rvalue); | |
299 | assert(data); | |
300 | ||
301 | if ((k = parse_boolean(rvalue)) < 0) { | |
16354eff | 302 | log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); |
ed5bcfbe LP |
303 | return k; |
304 | } | |
305 | ||
306 | *b = !!k; | |
307 | return 0; | |
308 | } | |
309 | ||
310 | int config_parse_string( | |
311 | const char *filename, | |
312 | unsigned line, | |
313 | const char *section, | |
314 | const char *lvalue, | |
315 | const char *rvalue, | |
316 | void *data, | |
317 | void *userdata) { | |
318 | ||
319 | char **s = data; | |
320 | char *n; | |
321 | ||
322 | assert(filename); | |
323 | assert(lvalue); | |
324 | assert(rvalue); | |
325 | assert(data); | |
326 | ||
327 | if (*rvalue) { | |
328 | if (!(n = strdup(rvalue))) | |
329 | return -ENOMEM; | |
330 | } else | |
331 | n = NULL; | |
332 | ||
333 | free(*s); | |
334 | *s = n; | |
335 | ||
336 | return 0; | |
337 | } | |
57d42a5f | 338 | |
034c6ed7 LP |
339 | int config_parse_path( |
340 | const char *filename, | |
341 | unsigned line, | |
342 | const char *section, | |
343 | const char *lvalue, | |
344 | const char *rvalue, | |
345 | void *data, | |
346 | void *userdata) { | |
347 | ||
348 | char **s = data; | |
349 | char *n; | |
350 | ||
351 | assert(filename); | |
352 | assert(lvalue); | |
353 | assert(rvalue); | |
354 | assert(data); | |
355 | ||
356 | if (*rvalue != '/') { | |
357 | log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); | |
358 | return -EINVAL; | |
359 | } | |
360 | ||
361 | if (!(n = strdup(rvalue))) | |
362 | return -ENOMEM; | |
363 | ||
364 | free(*s); | |
365 | *s = n; | |
366 | ||
367 | return 0; | |
368 | } | |
57d42a5f LP |
369 | |
370 | int config_parse_strv( | |
371 | const char *filename, | |
372 | unsigned line, | |
373 | const char *section, | |
374 | const char *lvalue, | |
375 | const char *rvalue, | |
376 | void *data, | |
377 | void *userdata) { | |
378 | ||
379 | char*** sv = data; | |
380 | char **n; | |
381 | char *w; | |
382 | unsigned k; | |
383 | size_t l; | |
384 | char *state; | |
385 | ||
386 | assert(filename); | |
387 | assert(lvalue); | |
388 | assert(rvalue); | |
389 | assert(data); | |
390 | ||
391 | k = strv_length(*sv); | |
034c6ed7 | 392 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
393 | k++; |
394 | ||
395 | if (!(n = new(char*, k+1))) | |
396 | return -ENOMEM; | |
397 | ||
398 | for (k = 0; (*sv)[k]; k++) | |
399 | n[k] = (*sv)[k]; | |
034c6ed7 | 400 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
401 | if (!(n[k++] = strndup(w, l))) |
402 | goto fail; | |
403 | ||
404 | n[k] = NULL; | |
405 | free(*sv); | |
406 | *sv = n; | |
407 | ||
408 | return 0; | |
409 | ||
410 | fail: | |
411 | for (; k > 0; k--) | |
412 | free(n[k-1]); | |
034c6ed7 | 413 | free(n); |
57d42a5f LP |
414 | |
415 | return -ENOMEM; | |
416 | } |