]>
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 | ||
ed5bcfbe | 52 | /* Parse a variable assignment line */ |
42f4e3c4 | 53 | static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) { |
b2aa81ef | 54 | char *e; |
ed5bcfbe | 55 | |
b2aa81ef | 56 | l = strstrip(l); |
ed5bcfbe | 57 | |
b2aa81ef | 58 | if (!*l) |
ed5bcfbe | 59 | return 0; |
1ea86b18 | 60 | |
b2aa81ef | 61 | if (strchr(COMMENTS, *l)) |
1ea86b18 | 62 | return 0; |
ed5bcfbe | 63 | |
b2aa81ef LP |
64 | if (startswith(l, ".include ")) { |
65 | char *fn; | |
ed5bcfbe LP |
66 | int r; |
67 | ||
b2aa81ef LP |
68 | if (!(fn = file_in_same_dir(filename, strstrip(l+9)))) |
69 | return -ENOMEM; | |
ed5bcfbe | 70 | |
87f0e418 | 71 | r = config_parse(fn, NULL, sections, t, userdata); |
b2aa81ef LP |
72 | free(fn); |
73 | ||
ed5bcfbe LP |
74 | return r; |
75 | } | |
76 | ||
b2aa81ef | 77 | if (*l == '[') { |
ed5bcfbe LP |
78 | size_t k; |
79 | char *n; | |
80 | ||
b2aa81ef | 81 | k = strlen(l); |
ed5bcfbe LP |
82 | assert(k > 0); |
83 | ||
b2aa81ef | 84 | if (l[k-1] != ']') { |
16354eff | 85 | log_error("[%s:%u] Invalid section header.", filename, line); |
ed5bcfbe LP |
86 | return -EBADMSG; |
87 | } | |
88 | ||
b2aa81ef | 89 | if (!(n = strndup(l+1, k-2))) |
ed5bcfbe LP |
90 | return -ENOMEM; |
91 | ||
b2aa81ef LP |
92 | if (sections && !strv_contains((char**) sections, n)) { |
93 | free(n); | |
94 | return -EBADMSG; | |
42f4e3c4 LP |
95 | } |
96 | ||
ed5bcfbe LP |
97 | free(*section); |
98 | *section = n; | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
b2aa81ef | 103 | if (!(e = strchr(l, '='))) { |
16354eff | 104 | log_error("[%s:%u] Missing '='.", filename, line); |
ed5bcfbe LP |
105 | return -EBADMSG; |
106 | } | |
107 | ||
108 | *e = 0; | |
109 | e++; | |
110 | ||
b2aa81ef | 111 | return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata); |
ed5bcfbe LP |
112 | } |
113 | ||
114 | /* Go through the file and parse each line */ | |
87f0e418 | 115 | int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) { |
ed5bcfbe LP |
116 | unsigned line = 0; |
117 | char *section = NULL; | |
ed5bcfbe LP |
118 | int r; |
119 | ||
120 | assert(filename); | |
121 | assert(t); | |
122 | ||
87f0e418 LP |
123 | if (!f) { |
124 | if (!(f = fopen(filename, "re"))) { | |
125 | r = -errno; | |
126 | log_error("Failed to open configuration file '%s': %s", filename, strerror(-r)); | |
127 | goto finish; | |
128 | } | |
ed5bcfbe LP |
129 | } |
130 | ||
131 | while (!feof(f)) { | |
132 | char l[LINE_MAX]; | |
133 | ||
134 | if (!fgets(l, sizeof(l), f)) { | |
135 | if (feof(f)) | |
136 | break; | |
137 | ||
138 | r = -errno; | |
16354eff | 139 | log_error("Failed to read configuration file '%s': %s", filename, strerror(-r)); |
ed5bcfbe LP |
140 | goto finish; |
141 | } | |
142 | ||
42f4e3c4 | 143 | if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0) |
ed5bcfbe LP |
144 | goto finish; |
145 | } | |
146 | ||
147 | r = 0; | |
148 | ||
149 | finish: | |
150 | free(section); | |
151 | ||
152 | if (f) | |
153 | fclose(f); | |
154 | ||
155 | return r; | |
156 | } | |
157 | ||
158 | int config_parse_int( | |
159 | const char *filename, | |
160 | unsigned line, | |
161 | const char *section, | |
162 | const char *lvalue, | |
163 | const char *rvalue, | |
164 | void *data, | |
165 | void *userdata) { | |
166 | ||
167 | int *i = data; | |
168 | int r; | |
169 | ||
170 | assert(filename); | |
171 | assert(lvalue); | |
172 | assert(rvalue); | |
173 | assert(data); | |
174 | ||
175 | if ((r = safe_atoi(rvalue, i)) < 0) { | |
16354eff | 176 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
177 | return r; |
178 | } | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | int config_parse_unsigned( | |
184 | const char *filename, | |
185 | unsigned line, | |
186 | const char *section, | |
187 | const char *lvalue, | |
188 | const char *rvalue, | |
189 | void *data, | |
190 | void *userdata) { | |
191 | ||
192 | unsigned *u = data; | |
193 | int r; | |
194 | ||
195 | assert(filename); | |
196 | assert(lvalue); | |
197 | assert(rvalue); | |
198 | assert(data); | |
199 | ||
200 | if ((r = safe_atou(rvalue, u)) < 0) { | |
16354eff | 201 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
202 | return r; |
203 | } | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | int config_parse_size( | |
209 | const char *filename, | |
210 | unsigned line, | |
211 | const char *section, | |
212 | const char *lvalue, | |
213 | const char *rvalue, | |
214 | void *data, | |
215 | void *userdata) { | |
216 | ||
217 | size_t *sz = data; | |
218 | unsigned u; | |
219 | int r; | |
220 | ||
221 | assert(filename); | |
222 | assert(lvalue); | |
223 | assert(rvalue); | |
224 | assert(data); | |
225 | ||
226 | if ((r = safe_atou(rvalue, &u)) < 0) { | |
16354eff | 227 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
228 | return r; |
229 | } | |
230 | ||
231 | *sz = (size_t) u; | |
232 | return 0; | |
233 | } | |
234 | ||
235 | int config_parse_bool( | |
236 | const char *filename, | |
237 | unsigned line, | |
238 | const char *section, | |
239 | const char *lvalue, | |
240 | const char *rvalue, | |
241 | void *data, | |
242 | void *userdata) { | |
243 | ||
244 | int k; | |
245 | bool *b = data; | |
246 | ||
247 | assert(filename); | |
248 | assert(lvalue); | |
249 | assert(rvalue); | |
250 | assert(data); | |
251 | ||
252 | if ((k = parse_boolean(rvalue)) < 0) { | |
16354eff | 253 | log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); |
ed5bcfbe LP |
254 | return k; |
255 | } | |
256 | ||
257 | *b = !!k; | |
258 | return 0; | |
259 | } | |
260 | ||
261 | int config_parse_string( | |
262 | const char *filename, | |
263 | unsigned line, | |
264 | const char *section, | |
265 | const char *lvalue, | |
266 | const char *rvalue, | |
267 | void *data, | |
268 | void *userdata) { | |
269 | ||
270 | char **s = data; | |
271 | char *n; | |
272 | ||
273 | assert(filename); | |
274 | assert(lvalue); | |
275 | assert(rvalue); | |
276 | assert(data); | |
277 | ||
278 | if (*rvalue) { | |
279 | if (!(n = strdup(rvalue))) | |
280 | return -ENOMEM; | |
281 | } else | |
282 | n = NULL; | |
283 | ||
284 | free(*s); | |
285 | *s = n; | |
286 | ||
287 | return 0; | |
288 | } | |
57d42a5f | 289 | |
034c6ed7 LP |
290 | int config_parse_path( |
291 | const char *filename, | |
292 | unsigned line, | |
293 | const char *section, | |
294 | const char *lvalue, | |
295 | const char *rvalue, | |
296 | void *data, | |
297 | void *userdata) { | |
298 | ||
299 | char **s = data; | |
300 | char *n; | |
301 | ||
302 | assert(filename); | |
303 | assert(lvalue); | |
304 | assert(rvalue); | |
305 | assert(data); | |
306 | ||
307 | if (*rvalue != '/') { | |
308 | log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue); | |
309 | return -EINVAL; | |
310 | } | |
311 | ||
312 | if (!(n = strdup(rvalue))) | |
313 | return -ENOMEM; | |
314 | ||
315 | free(*s); | |
316 | *s = n; | |
317 | ||
318 | return 0; | |
319 | } | |
57d42a5f LP |
320 | |
321 | int config_parse_strv( | |
322 | const char *filename, | |
323 | unsigned line, | |
324 | const char *section, | |
325 | const char *lvalue, | |
326 | const char *rvalue, | |
327 | void *data, | |
328 | void *userdata) { | |
329 | ||
330 | char*** sv = data; | |
331 | char **n; | |
332 | char *w; | |
333 | unsigned k; | |
334 | size_t l; | |
335 | char *state; | |
336 | ||
337 | assert(filename); | |
338 | assert(lvalue); | |
339 | assert(rvalue); | |
340 | assert(data); | |
341 | ||
342 | k = strv_length(*sv); | |
034c6ed7 | 343 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
344 | k++; |
345 | ||
346 | if (!(n = new(char*, k+1))) | |
347 | return -ENOMEM; | |
348 | ||
349 | for (k = 0; (*sv)[k]; k++) | |
350 | n[k] = (*sv)[k]; | |
034c6ed7 | 351 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
352 | if (!(n[k++] = strndup(w, l))) |
353 | goto fail; | |
354 | ||
355 | n[k] = NULL; | |
356 | free(*sv); | |
357 | *sv = n; | |
358 | ||
359 | return 0; | |
360 | ||
361 | fail: | |
362 | for (; k > 0; k--) | |
363 | free(n[k-1]); | |
034c6ed7 | 364 | free(n); |
57d42a5f LP |
365 | |
366 | return -ENOMEM; | |
367 | } |