]>
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 | 15 | #define COMMENTS "#;\n" |
1ea86b18 | 16 | #define NEWLINES "\n\r" |
ed5bcfbe LP |
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 | ||
f1857be0 LP |
48 | log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section)); |
49 | return 0; | |
ed5bcfbe LP |
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 */ | |
42f4e3c4 | 80 | static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) { |
1ea86b18 | 81 | char *e, *b, *c; |
ed5bcfbe LP |
82 | |
83 | b = l+strspn(l, WHITESPACE); | |
84 | ||
1ea86b18 | 85 | if ((c = strpbrk(b, NEWLINES))) |
ed5bcfbe LP |
86 | *c = 0; |
87 | ||
88 | if (!*b) | |
89 | return 0; | |
1ea86b18 LP |
90 | |
91 | if (strchr(COMMENTS, *b)) | |
92 | return 0; | |
ed5bcfbe LP |
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 | ||
87f0e418 | 116 | r = config_parse(fn, NULL, sections, t, userdata); |
ed5bcfbe LP |
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] != ']') { | |
16354eff | 129 | log_error("[%s:%u] Invalid section header.", filename, line); |
ed5bcfbe LP |
130 | return -EBADMSG; |
131 | } | |
132 | ||
133 | if (!(n = strndup(b+1, k-2))) | |
134 | return -ENOMEM; | |
135 | ||
42f4e3c4 LP |
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 | ||
ed5bcfbe LP |
151 | free(*section); |
152 | *section = n; | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | if (!(e = strchr(b, '='))) { | |
16354eff | 158 | log_error("[%s:%u] Missing '='.", filename, line); |
ed5bcfbe LP |
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 */ | |
87f0e418 | 169 | int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) { |
ed5bcfbe LP |
170 | unsigned line = 0; |
171 | char *section = NULL; | |
ed5bcfbe LP |
172 | int r; |
173 | ||
174 | assert(filename); | |
175 | assert(t); | |
176 | ||
87f0e418 LP |
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 | } | |
ed5bcfbe LP |
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; | |
16354eff | 193 | log_error("Failed to read configuration file '%s': %s", filename, strerror(-r)); |
ed5bcfbe LP |
194 | goto finish; |
195 | } | |
196 | ||
42f4e3c4 | 197 | if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0) |
ed5bcfbe LP |
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) { | |
16354eff | 230 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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) { | |
16354eff | 255 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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) { | |
16354eff | 281 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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) { | |
16354eff | 307 | log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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 | } | |
57d42a5f | 343 | |
034c6ed7 LP |
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 | } | |
57d42a5f LP |
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); | |
034c6ed7 | 397 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
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]; | |
034c6ed7 | 405 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
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]); | |
034c6ed7 | 418 | free(n); |
57d42a5f LP |
419 | |
420 | return -ENOMEM; | |
421 | } |