blob: 284b32a21cc6dda3de59ca8a20d617ebe394d5a0 [file] [log] [blame]
xf.lif1aed282024-02-06 00:31:51 -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#include <string.h>
17#include <ctype.h>
18#include <stdlib.h>
19#include <fcntl.h>
20#include <unistd.h>
21
22#include <config_utils.h>
23
24cnode* config_node(const char *name, const char *value)
25{
26 cnode* node = calloc(sizeof(cnode), 1);
27 if(node) {
28 node->name = name ? name : "";
29 node->value = value ? value : "";
30 }
31
32 return node;
33}
34
35cnode* config_find(cnode *root, const char *name)
36{
37 cnode *node, *match = NULL;
38
39 /* we walk the whole list, as we need to return the last (newest) entry */
40 for(node = root->first_child; node; node = node->next)
41 if(!strcmp(node->name, name))
42 match = node;
43
44 return match;
45}
46
47static cnode* _config_create(cnode *root, const char *name)
48{
49 cnode *node;
50
51 node = config_node(name, NULL);
52
53 if(root->last_child)
54 root->last_child->next = node;
55 else
56 root->first_child = node;
57
58 root->last_child = node;
59
60 return node;
61}
62
63int config_bool(cnode *root, const char *name, int _default)
64{
65 cnode *node;
66
67 node = config_find(root, name);
68 if(!node)
69 return _default;
70
71 switch(node->value[0]) {
72 case 'y':
73 case 'Y':
74 case '1':
75 return 1;
76 default:
77 return 0;
78 }
79}
80
81const char* config_str(cnode *root, const char *name, const char *_default)
82{
83 cnode *node;
84
85 node = config_find(root, name);
86 if(!node)
87 return _default;
88 return node->value;
89}
90
91void config_set(cnode *root, const char *name, const char *value)
92{
93 cnode *node;
94
95 node = config_find(root, name);
96 if(node)
97 node->value = value;
98 else {
99 node = _config_create(root, name);
100 node->value = value;
101 }
102}
103
104#define T_EOF 0
105#define T_TEXT 1
106#define T_DOT 2
107#define T_OBRACE 3
108#define T_CBRACE 4
109
110typedef struct
111{
112 char *data;
113 char *text;
114 int len;
115 char next;
116} cstate;
117
118static int _lex(cstate *cs, int value)
119{
120 char c;
121 char *s;
122 char *data;
123
124 data = cs->data;
125
126 if(cs->next != 0) {
127 c = cs->next;
128 cs->next = 0;
129 goto got_c;
130 }
131
132restart:
133 for(;;) {
134 c = *data++;
135 got_c:
136 if(isspace(c))
137 continue;
138
139 switch(c) {
140 case 0:
141 return T_EOF;
142
143 case '#':
144 for(;;) {
145 switch(*data) {
146 case 0:
147 cs->data = data;
148 return T_EOF;
149 case '\n':
150 cs->data = data + 1;
151 goto restart;
152 default:
153 data++;
154 }
155 }
156 break;
157
158 case '.':
159 cs->data = data;
160 return T_DOT;
161
162 case '{':
163 cs->data = data;
164 return T_OBRACE;
165
166 case '}':
167 cs->data = data;
168 return T_CBRACE;
169
170 default:
171 s = data - 1;
172
173 if(value) {
174 for(;;) {
175 if(*data == 0) {
176 cs->data = data;
177 break;
178 }
179 if(*data == '\n') {
180 cs->data = data + 1;
181 *data-- = 0;
182 break;
183 }
184 data++;
185 }
186
187 /* strip trailing whitespace */
188 while(data > s){
189 if(!isspace(*data)) break;
190 *data-- = 0;
191 }
192
193 goto got_text;
194 } else {
195 for(;;) {
196 if(isspace(*data)) {
197 *data = 0;
198 cs->data = data + 1;
199 goto got_text;
200 }
201 switch(*data) {
202 case 0:
203 cs->data = data;
204 goto got_text;
205 case '.':
206 case '{':
207 case '}':
208 cs->next = *data;
209 *data = 0;
210 cs->data = data + 1;
211 goto got_text;
212 default:
213 data++;
214 }
215 }
216 }
217 }
218 }
219
220got_text:
221 cs->text = s;
222 return T_TEXT;
223}
224
225#if 0
226char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
227
228static int lex(cstate *cs, int value)
229{
230 int tok = _lex(cs, value);
231 printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
232 tok == T_TEXT ? cs->text : "");
233 return tok;
234}
235#else
236#define lex(cs,v) _lex(cs,v)
237#endif
238
239static int parse_expr(cstate *cs, cnode *node);
240
241static int parse_block(cstate *cs, cnode *node)
242{
243 for(;;){
244 switch(lex(cs, 0)){
245 case T_TEXT:
246 if(parse_expr(cs, node)) return -1;
247 continue;
248
249 case T_CBRACE:
250 return 0;
251
252 default:
253 return -1;
254 }
255 }
256}
257
258static int parse_expr(cstate *cs, cnode *root)
259{
260 cnode *node;
261
262 /* last token was T_TEXT */
263 node = config_find(root, cs->text);
264 if(!node || *node->value)
265 node = _config_create(root, cs->text);
266
267 for(;;) {
268 switch(lex(cs, 1)) {
269 case T_DOT:
270 if(lex(cs, 0) != T_TEXT)
271 return -1;
272 node = _config_create(node, cs->text);
273 continue;
274
275 case T_TEXT:
276 node->value = cs->text;
277 return 0;
278
279 case T_OBRACE:
280 return parse_block(cs, node);
281
282 default:
283 return -1;
284 }
285 }
286}
287
288void config_load(cnode *root, char *data)
289{
290 if(data != 0) {
291 cstate cs;
292 cs.data = data;
293 cs.next = 0;
294
295 for(;;) {
296 switch(lex(&cs, 0)) {
297 case T_TEXT:
298 if(parse_expr(&cs, root))
299 return;
300 break;
301 default:
302 return;
303 }
304 }
305 }
306}
307
308void config_load_file(cnode *root, const char *fn)
309{
310 char* data = load_file(fn, 0);
311 config_load(root, data);
312 // TODO: deliberate leak :-/
313}
314
315void config_free(cnode *root)
316{
317 cnode *cur = root->first_child;
318
319 while (cur) {
320 cnode *prev = cur;
321 config_free(cur);
322 cur = cur->next;
323 free(prev);
324 }
325}