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