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