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