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