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 
config_node(const char * name,const char * value)26 cnode* 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 
config_find(cnode * root,const char * name)39 cnode* 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 
_config_create(cnode * root,const char * name)51 static 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 
config_bool(cnode * root,const char * name,int _default)67 int 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 
config_str(cnode * root,const char * name,const char * _default)85 const 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 
config_set(cnode * root,const char * name,const char * value)95 void 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 
114 typedef struct
115 {
116     char *data;
117     char *text;
118     int len;
119     char next;
120 } cstate;
121 
_lex(cstate * cs,int value)122 static 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 
136 restart:
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 
224 got_text:
225     cs->text = s;
226     return T_TEXT;
227 }
228 
229 #if 0
230 char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
231 
232 static 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 
243 static int parse_expr(cstate *cs, cnode *node);
244 
parse_block(cstate * cs,cnode * node)245 static 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 
parse_expr(cstate * cs,cnode * root)262 static 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 
config_load(cnode * root,char * data)292 void 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 
config_load_file(cnode * root,const char * fn)312 void 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 
config_free(cnode * root)319 void config_free(cnode *root)
320 {
321     cnode *cur = root->first_child;
322 
323     while (cur) {
324         cnode *prev = cur;
325         config_free(cur);
326         cur = cur->next;
327         free(prev);
328     }
329 }
330