1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string.h>
18#include <ctype.h>
19#include <stdlib.h>
20#include <fcntl.h>
21#include <unistd.h>
22
23#include <cutils/config_utils.h>
24#include <cutils/misc.h>
25
26cnode* config_node(const char *name, const char *value)
27{
28    cnode *node;
29
30    node = calloc(sizeof(cnode), 1);
31    if(node) {
32        node->name = name ? name : "";
33        node->value = value ? value : "";
34    }
35
36    return node;
37}
38
39cnode* config_find(cnode *root, const char *name)
40{
41    cnode *node, *match = NULL;
42
43    /* we walk the whole list, as we need to return the last (newest) entry */
44    for(node = root->first_child; node; node = node->next)
45        if(!strcmp(node->name, name))
46            match = node;
47
48    return match;
49}
50
51static cnode* _config_create(cnode *root, const char *name)
52{
53    cnode *node;
54
55    node = config_node(name, NULL);
56
57    if(root->last_child)
58        root->last_child->next = node;
59    else
60        root->first_child = node;
61
62    root->last_child = node;
63
64    return node;
65}
66
67int config_bool(cnode *root, const char *name, int _default)
68{
69    cnode *node;
70
71    node = config_find(root, name);
72    if(!node)
73        return _default;
74
75    switch(node->value[0]) {
76    case 'y':
77    case 'Y':
78    case '1':
79        return 1;
80    default:
81        return 0;
82    }
83}
84
85const char* config_str(cnode *root, const char *name, const char *_default)
86{
87    cnode *node;
88
89    node = config_find(root, name);
90    if(!node)
91        return _default;
92    return node->value;
93}
94
95void config_set(cnode *root, const char *name, const char *value)
96{
97    cnode *node;
98
99    node = config_find(root, name);
100    if(node)
101        node->value = value;
102    else {
103        node = _config_create(root, name);
104        node->value = value;
105    }
106}
107
108#define T_EOF 0
109#define T_TEXT 1
110#define T_DOT 2
111#define T_OBRACE 3
112#define T_CBRACE 4
113
114typedef struct
115{
116    char *data;
117    char *text;
118    int len;
119    char next;
120} cstate;
121
122static int _lex(cstate *cs, int value)
123{
124    char c;
125    char *s;
126    char *data;
127
128    data = cs->data;
129
130    if(cs->next != 0) {
131        c = cs->next;
132        cs->next = 0;
133        goto got_c;
134    }
135
136restart:
137    for(;;) {
138        c = *data++;
139    got_c:
140        if(isspace(c))
141            continue;
142
143        switch(c) {
144        case 0:
145            return T_EOF;
146
147        case '#':
148            for(;;) {
149                switch(*data) {
150                case 0:
151                    cs->data = data;
152                    return T_EOF;
153                case '\n':
154                    cs->data = data + 1;
155                    goto restart;
156                default:
157                    data++;
158                }
159            }
160            break;
161
162        case '.':
163            cs->data = data;
164            return T_DOT;
165
166        case '{':
167            cs->data = data;
168            return T_OBRACE;
169
170        case '}':
171            cs->data = data;
172            return T_CBRACE;
173
174        default:
175            s = data - 1;
176
177            if(value) {
178                for(;;) {
179                    if(*data == 0) {
180                        cs->data = data;
181                        break;
182                    }
183                    if(*data == '\n') {
184                        cs->data = data + 1;
185                        *data-- = 0;
186                        break;
187                    }
188                    data++;
189                }
190
191                    /* strip trailing whitespace */
192                while(data > s){
193                    if(!isspace(*data)) break;
194                    *data-- = 0;
195                }
196
197                goto got_text;
198            } else {
199                for(;;) {
200                    if(isspace(*data)) {
201                        *data = 0;
202                        cs->data = data + 1;
203                        goto got_text;
204                    }
205                    switch(*data) {
206                    case 0:
207                        cs->data = data;
208                        goto got_text;
209                    case '.':
210                    case '{':
211                    case '}':
212                        cs->next = *data;
213                        *data = 0;
214                        cs->data = data + 1;
215                        goto got_text;
216                    default:
217                        data++;
218                    }
219                }
220            }
221        }
222    }
223
224got_text:
225    cs->text = s;
226    return T_TEXT;
227}
228
229#if 0
230char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
231
232static int lex(cstate *cs, int value)
233{
234    int tok = _lex(cs, value);
235    printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
236           tok == T_TEXT ? cs->text : "");
237    return tok;
238}
239#else
240#define lex(cs,v) _lex(cs,v)
241#endif
242
243static int parse_expr(cstate *cs, cnode *node);
244
245static int parse_block(cstate *cs, cnode *node)
246{
247    for(;;){
248        switch(lex(cs, 0)){
249        case T_TEXT:
250            if(parse_expr(cs, node)) return -1;
251            continue;
252
253        case T_CBRACE:
254            return 0;
255
256        default:
257            return -1;
258        }
259    }
260}
261
262static int parse_expr(cstate *cs, cnode *root)
263{
264    cnode *node;
265
266        /* last token was T_TEXT */
267    node = config_find(root, cs->text);
268    if(!node || *node->value)
269        node = _config_create(root, cs->text);
270
271    for(;;) {
272        switch(lex(cs, 1)) {
273        case T_DOT:
274            if(lex(cs, 0) != T_TEXT)
275                return -1;
276            node = _config_create(node, cs->text);
277            continue;
278
279        case T_TEXT:
280            node->value = cs->text;
281            return 0;
282
283        case T_OBRACE:
284            return parse_block(cs, node);
285
286        default:
287            return -1;
288        }
289    }
290}
291
292void config_load(cnode *root, char *data)
293{
294    if(data != 0) {
295        cstate cs;
296        cs.data = data;
297        cs.next = 0;
298
299        for(;;) {
300            switch(lex(&cs, 0)) {
301            case T_TEXT:
302                if(parse_expr(&cs, root))
303                    return;
304                break;
305            default:
306                return;
307            }
308        }
309    }
310}
311
312void config_load_file(cnode *root, const char *fn)
313{
314    char *data;
315    data = load_file(fn, 0);
316    config_load(root, data);
317}
318