]> git.ipfire.org Git - people/ms/systemd.git/blob - conf-parser.c
conf-parser: add ini/.desktop file parser
[people/ms/systemd.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 }