blob: a3af01a58aef43b6d7c3f88e5b9244ed172e8ec6 [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
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080017#include <cutils/config_utils.h>
18
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080019#include <string.h>
20#include <ctype.h>
21#include <stdlib.h>
22#include <fcntl.h>
23#include <unistd.h>
24
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080025#include <cutils/misc.h>
26
27cnode* config_node(const char *name, const char *value)
28{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -080029 cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -080030 if(node) {
31 node->name = name ? name : "";
32 node->value = value ? value : "";
33 }
34
35 return node;
36}
37
38cnode* 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
50static 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
66int 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
84const 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
94void 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
113typedef struct
114{
115 char *data;
116 char *text;
117 int len;
118 char next;
119} cstate;
120
121static 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
135restart:
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
223got_text:
224 cs->text = s;
225 return T_TEXT;
226}
227
228#if 0
229char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
230
231static 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
242static int parse_expr(cstate *cs, cnode *node);
243
244static 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
261static 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
291void 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
311void config_load_file(cnode *root, const char *fn)
312{
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800313 char* data = static_cast<char*>(load_file(fn, nullptr));
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800314 config_load(root, data);
Elliott Hughes8e9aeb92017-11-10 10:22:07 -0800315 // TODO: deliberate leak :-/
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800316}
Eric Laurentc3cf1a82011-05-26 13:57:03 -0700317
318void 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}