common: provide parsing multiple data types
[platform/core/system/resourced.git] / src / common / config-parser.c
1 /*
2  * resourced
3  *
4  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <assert.h>
23 #include <limits.h>
24
25 #include "util.h"
26 #include "trace.h"
27 #include "config-parser.h"
28
29 #define MAX_SECTION     64
30
31 static inline char *trim_str(char *s)
32 {
33         char *t;
34         /* left trim */
35         s += strspn(s, WHITESPACE);
36
37         /* right trim */
38         for (t = strchr(s, 0); t > s; t--)
39                 if (!strchr(WHITESPACE, t[-1]))
40                         break;
41         *t = 0;
42         return s;
43 }
44
45 int config_parse(const char *file_name, int cb(struct parse_result *result,
46                         void *user_data), void *user_data)
47 {
48         FILE *f = NULL;
49         struct parse_result result;
50         /* use stack for parsing */
51         char line[LINE_MAX];
52         char section[MAX_SECTION];
53         char *start, *end, *name, *value;
54         int lineno = 0, ret = 0;
55
56         if (!file_name || !cb) {
57                 ret = -EINVAL;
58                 goto error;
59         }
60
61         /* open conf file */
62         f = fopen(file_name, "r");
63         if (!f) {
64                 _E("Failed to open file %s", file_name);
65                 ret = -EIO;
66                 goto error;
67         }
68
69         /* parsing line by line */
70         while (fgets(line, LINE_MAX, f) != NULL) {
71                 lineno++;
72
73                 start = line;
74                 start[strcspn(start, NEWLINE)] = '\0';
75                 start = trim_str(start);
76
77                 if (*start == COMMENT) {
78                         continue;
79                 } else if (*start == '[') {
80                         /* parse section */
81                         end = strchr(start, ']');
82                         if (!end || *end != ']') {
83                                 ret = -EBADMSG;
84                                 goto error;
85                         }
86
87                         *end = '\0';
88                         strncpy(section, start + 1, sizeof(section)-1);
89                         section[MAX_SECTION-1] = '\0';
90                 } else if (*start) {
91                         /* parse name & value */
92                         end = strchr(start, '=');
93                         if (!end || *end != '=') {
94                                 ret = -EBADMSG;
95                                 goto error;
96                         }
97                         *end = '\0';
98                         name = trim_str(start);
99                         value = trim_str(end + 1);
100                         end = strchr(value, COMMENT);
101                         if (end && *end == COMMENT) {
102                                 *end = '\0';
103                                 value = trim_str(value);
104                         }
105
106                         result.section = section;
107                         result.name = name;
108                         result.value = value;
109                         /* callback with parse result */
110                         ret = cb(&result, user_data);
111                         if (ret < 0) {
112                                 ret = -EBADMSG;
113                                 goto error;
114                         }
115                 }
116         }
117         _D("Success to load %s", file_name);
118         fclose(f);
119         return 0;
120
121 error:
122         if (f)
123                 fclose(f);
124         _E("Failed to read %s:%d!", file_name, lineno);
125         return ret;
126 }
127
128 static int config_table_lookup(void *table,
129                                const char *section,
130                                const char *lvalue,
131                                ConfigParserCallback *func,
132                                int *ltype,
133                                void **data)
134 {
135         ConfigTableItem *t;
136
137         assert(table);
138         assert(lvalue);
139         assert(func);
140         assert(ltype);
141         assert(data);
142
143         for (t = table; t->lvalue; t++) {
144
145                 if (!streq(lvalue, t->lvalue))
146                         continue;
147
148                 if (!streq_ptr(section, t->section))
149                         continue;
150
151                 *func = t->cb;
152                 *ltype = t->ltype;
153                 *data = t->data;
154                 return 1;
155         }
156
157         return 0;
158 }
159
160 /* Run the user supplied parser for an assignment */
161 static int config_parse_table(const char *filename,
162                               unsigned line,
163                               void *table,
164                               const char *section,
165                               const char *lvalue,
166                               const char *rvalue)
167 {
168         ConfigParserCallback cb = NULL;
169         int ltype = 0;
170         void *data = NULL;
171         int r;
172
173         assert(filename);
174         assert(section);
175         assert(lvalue);
176         assert(rvalue);
177
178         r = config_table_lookup(table,
179                                 section,
180                                 lvalue,
181                                 &cb,
182                                 &ltype,
183                                 &data);
184         if (r <= 0)
185                 return r;
186
187         if (cb)
188                 return cb(filename,
189                           line,
190                           section,
191                           lvalue,
192                           ltype,
193                           rvalue,
194                           data);
195
196         return 0;
197 }
198
199 int config_parse_new(const char *filename, void *table)
200 {
201         _cleanup_fclose_ FILE *f = NULL;
202         char *sections[MAX_SECTION] = { 0 };
203         char *section = NULL, *n, *e, l[LINE_MAX];
204         size_t len;
205         int i, r, num_section = 0;
206         bool already;
207         unsigned line = 0;
208
209         assert(filename);
210
211         f = fopen(filename, "r");
212         if (!f) {
213                 _E("Failed to open file %s", filename);
214                 return -errno;
215         }
216
217         while (!feof(f)) {
218                 _cleanup_free_ char *lvalue = NULL, *rvalue = NULL;
219                 char *rs = NULL;
220
221                 if (fgets(l, LINE_MAX, f) == NULL) {
222                         if (feof(f))
223                                 break;
224
225                         _E("Failed to parse configuration file '%s': %m", filename);
226                         r = -errno;
227                         goto finish;
228                 }
229
230                 line++;
231                 truncate_nl(l);
232
233                 if (strchr(COMMENTS NEWLINE, *l))
234                         continue;
235
236                 if (*l == '[') {
237                         len = strlen(l);
238                         if (l[len-1] != ']') {
239                                 _E("Error: Invalid section header: %s", l);
240                                 r = -EBADMSG;
241                                 goto finish;
242                         }
243
244                         n = strndup(l+1, len-2);
245                         if (!n) {
246                                 r = -ENOMEM;
247                                 goto finish;
248                         }
249
250                         already = false;
251                         for (i = 0; i < num_section; i++) {
252                                 if (streq(n, sections[i])) {
253                                         section = sections[i];
254                                         already = true;
255                                         free(n);
256                                         break;
257                                 }
258                         }
259
260                         if (already)
261                                 continue;
262
263                         section = n;
264                         sections[num_section] = n;
265                         num_section++;
266                         if (num_section > MAX_SECTION) {
267                                 _E("Error: max number of section reached: %d", num_section);
268                                 r = -EOVERFLOW;
269                                 goto finish;
270                         }
271
272                         continue;
273                 }
274
275                 if (!section)
276                         continue;
277
278                 e = strchr(l, '=');
279                 if (e == NULL) {
280                         _D("Warning: config: no '=' character in line '%s'.", l);
281                         continue;
282                 }
283
284                 lvalue = strndup(l, e-l);
285                 if (!lvalue) {
286                         r = -ENOMEM;
287                         goto finish;
288                 }
289
290                 strstrip(lvalue);
291
292                 rs = strstrip(e+1);
293                 rvalue = strdup(rs);
294                 if (!rvalue) {
295                         r = -ENOMEM;
296                         goto finish;
297                 }
298
299                 strstrip(rvalue);
300
301                 r = config_parse_table(filename,
302                                        line,
303                                        table,
304                                        section,
305                                        lvalue,
306                                        rvalue);
307                 if (r < 0)
308                         goto finish;
309         }
310
311         r = 0;
312
313 finish:
314         for (i = 0; i < num_section; i++)
315                 if (sections[i])
316                         free(sections[i]);
317
318         return r;
319 }
320
321 int config_parse_dir(const char *dir, ConfigParseFunc fp, void *data)
322 {
323         _cleanup_closedir_ DIR *d = NULL;
324         struct dirent *de;
325
326         d = opendir(dir);
327         if (!d) {
328                 _E("Failed to open dir: %s", dir);
329                 return errno;
330         }
331
332         for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) {
333                 if (!de) {
334                         if (errno > 0)
335                                 return -errno;
336                         break;
337                 } else if (streq(de->d_name, ".") || streq(de->d_name, "..")) {
338                         continue;
339                 }
340
341                 _cleanup_free_ char *path = NULL;
342                 int r;
343
344                 if (de->d_type != DT_REG)
345                         continue;
346
347                 r = asprintf(&path, "%s/%s", dir, de->d_name);
348                 if (r < 0)
349                         return -ENOMEM;
350
351                 r = fp(path, data);
352                 /* Do not just break loop until parse all file of
353                  * dir. Just only put log */
354                 if (r < 0)
355                         _D("Failed to parse config: %s", de->d_name);
356         }
357
358         return 0;
359 }
360
361 int config_parse_bool(const char *filename,
362                       unsigned line,
363                       const char *section,
364                       const char *lvalue,
365                       int ltype,
366                       const char *rvalue,
367                       void *data)
368 {
369         int k;
370         bool *b = data;
371
372         assert(filename);
373         assert(lvalue);
374         assert(rvalue);
375         assert(data);
376
377         k = parse_boolean(rvalue);
378         if (k < 0) {
379                 _E("Failed to parse boolean value, ignoring: %s", rvalue);
380                 return 0;
381         }
382
383         *b = !!k;
384
385         return 0;
386 }
387
388 #define DEFINE_PARSER(type, vartype, conv_func)          \
389         int config_parse_##type(                                        \
390                                 const char *filename,                   \
391                                 unsigned line,                          \
392                                 const char *section,                    \
393                                 const char *lvalue,                     \
394                                 int ltype,                              \
395                                 const char *rvalue,                     \
396                                 void *data) {                           \
397                                                                                                                 \
398                 vartype *i = data;                                      \
399                                                                         \
400                 assert(filename);                                       \
401                 assert(lvalue);                                         \
402                 assert(rvalue);                                         \
403                 assert(data);                                           \
404                                                                         \
405                 *i = conv_func(rvalue);                          \
406                 return 0;                                               \
407                 }
408
409 DEFINE_PARSER(int, int, atoi)
410 DEFINE_PARSER(float, float, atof)
411 DEFINE_PARSER(long, long, atol)
412
413 int config_parse_string(const char *filename,
414                         unsigned line,
415                         const char *section,
416                         const char *lvalue,
417                         int ltype,
418                         const char *rvalue,
419                         void *data)
420 {
421         char **s = data, *n;
422
423         assert(filename);
424         assert(lvalue);
425         assert(rvalue);
426         assert(data);
427
428         if (is_empty(rvalue))
429                 n = NULL;
430         else {
431                 n = strdup(rvalue);
432                 if (!n)
433                         return -ENOMEM;
434         }
435
436         free(*s);
437         *s = n;
438
439         return 0;
440 }
441
442 int config_parse_bytes(const char *filename,
443                        unsigned line,
444                        const char *section,
445                        const char *lvalue,
446                        int ltype,
447                        const char *rvalue,
448                        void *data)
449 {
450         size_t *s = data;
451         int r;
452
453         assert(filename);
454         assert(lvalue);
455         assert(rvalue);
456         assert(data);
457
458         if (is_empty(rvalue))
459                 *s = 0;
460         else {
461                 r = parse_bytes(rvalue, s);
462                 if (r < 0)
463                         return r;
464         }
465
466         return 0;
467 }
468
469 int config_parse_strv(const char *filename,
470                       unsigned line,
471                       const char *section,
472                       const char *lvalue,
473                       int ltype,
474                       const char *rvalue,
475                       void *data)
476 {
477         char ***strv = data;
478         char **o = NULL, **v = NULL, **vv = NULL;
479         int r;
480
481         assert(filename);
482         assert(lvalue);
483         assert(rvalue);
484         assert(data);
485
486         if (is_empty(rvalue))
487                 return 0;
488
489         r = str_to_strv(rvalue, &v, WHITESPACE);
490         if (r < 0)
491                 return r;
492
493         o = *strv;
494
495         r = strv_attach(o, v, &vv, true);
496         if (r < 0)
497                 return r;
498
499         *strv = vv;
500
501         return 0;
502 }