blob: fc5ca78761a224ce0a4919cdde2294f9853182ac [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
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}
Eric Laurentc3cf1a82011-05-26 13:57:03 -0700318
319void 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}