blob: 36fab11eb2f5896005ec56f93c1e8726105d6a3f [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12#include <string.h>
13#include <ctype.h>
14#include <stdlib.h>
15#include <fcntl.h>
16#include <unistd.h>
17
18#include "android/config.h"
19#include "android/utils/path.h"
20
21AConfig*
22aconfig_node(const char *name, const char *value)
23{
24 AConfig *n;
25
26 n = (AConfig*) calloc(sizeof(AConfig), 1);
27 n->name = name ? name : "";
28 n->value = value ? value : "";
29
30 return n;
31}
32
33static AConfig*
34_aconfig_find(AConfig *root, const char *name, int create)
35{
36 AConfig *node;
37
38 for(node = root->first_child; node; node = node->next) {
39 if(!strcmp(node->name, name)) return node;
40 }
41
42 if(create) {
43 node = (AConfig*) calloc(sizeof(AConfig), 1);
44 node->name = name;
45 node->value = "";
46
47 if(root->last_child) {
48 root->last_child->next = node;
49 } else {
50 root->first_child = node;
51 }
52 root->last_child = node;
53 }
54
55 return node;
56}
57
58AConfig*
59aconfig_find(AConfig *root, const char *name)
60{
61 return _aconfig_find(root, name, 0);
62}
63
64int
65aconfig_bool(AConfig *root, const char *name, int _default)
66{
67 AConfig *n = _aconfig_find(root, name, 0);
68 if(n == 0) {
69 return _default;
70 } else {
71 switch(n->value[0]){
72 case 'y':
73 case 'Y':
74 case '1':
75 return 1;
76 default:
77 return 0;
78 }
79 }
80}
81
82unsigned
83aconfig_unsigned(AConfig *root, const char *name, unsigned _default)
84{
85 AConfig *n = _aconfig_find(root, name, 0);
86 if(n == 0) {
87 return _default;
88 } else {
89 return strtoul(n->value, 0, 0);
90 }
91}
92
93int
94aconfig_int(AConfig *root, const char *name, int _default)
95{
96 AConfig *n = _aconfig_find(root, name, 0);
97 if(n == 0) {
98 return _default;
99 } else {
100 return strtol(n->value, 0, 0);
101 }
102}
103
104
105const char*
106aconfig_str(AConfig *root, const char *name, const char *_default)
107{
108 AConfig *n = _aconfig_find(root, name, 0);
109 if(n == 0) {
110 return _default;
111 } else {
112 return n->value;
113 }
114}
115
116void
117aconfig_set(AConfig *root, const char *name, const char *value)
118{
119 AConfig *node = _aconfig_find(root, name, 1);
120 node->value = value;
121}
122
123#define T_EOF 0
124#define T_TEXT 1
125#define T_DOT 2
126#define T_OBRACE 3
127#define T_CBRACE 4
128
129typedef struct
130{
131 char *data;
132 char *text;
133 int len;
134 char next;
135} cstate;
136
137
138static int _lex(cstate *cs, int value)
139{
140 char c;
141 char *s;
142 char *data;
143
144 data = cs->data;
145
146 if(cs->next != 0) {
147 c = cs->next;
148 cs->next = 0;
149 goto got_c;
150 }
151
152restart:
153 for(;;) {
154 c = *data++;
155 got_c:
156 if(isspace(c)) continue;
157
158 switch(c) {
159 case 0:
160 return T_EOF;
161
162 /* a sharp sign (#) starts a line comment and everything
163 * behind that is ignored until the end of line
164 */
165 case '#':
166 for(;;) {
167 switch(*data) {
168 case 0:
169 cs->data = data;
170 return T_EOF;
171 case '\n':
172 cs->data = data + 1;
173 goto restart;
174 default:
175 data++;
176 }
177 }
178 break;
179
180 case '.':
181 cs->data = data;
182 return T_DOT;
183
184 case '{':
185 cs->data = data;
186 return T_OBRACE;
187
188 case '}':
189 cs->data = data;
190 return T_CBRACE;
191
192 default:
193 s = data - 1;
194
195 if(value) {
196 /* if we're looking for a value, then take anything
197 * until the end of line. note that sharp signs do
198 * not start comments then. the result will be stripped
199 * from trailing whitespace.
200 */
201 for(;;) {
202 if(*data == 0) {
203 cs->data = data;
204 break;
205 }
206 if(*data == '\n') {
207 cs->data = data + 1;
208 *data-- = 0;
209 break;
210 }
211 data++;
212 }
213
214 /* strip trailing whitespace */
215 while(data > s){
216 if(!isspace(*data)) break;
217 *data-- = 0;
218 }
219
220 goto got_text;
221 } else {
222 /* looking for a key name. we stop at whitspace,
223 * EOF, of T_DOT/T_OBRACE/T_CBRACE characters.
224 * note that the name can include sharp signs
225 */
226 for(;;) {
227 if(isspace(*data)) {
228 *data = 0;
229 cs->data = data + 1;
230 goto got_text;
231 }
232 switch(*data) {
233 case 0:
234 cs->data = data;
235 goto got_text;
236 case '.':
237 case '{':
238 case '}':
239 cs->next = *data;
240 *data = 0;
241 cs->data = data + 1;
242 goto got_text;
243 default:
244 data++;
245 }
246 }
247 }
248 }
249 }
250
251got_text:
252 cs->text = s;
253 return T_TEXT;
254}
255
256#if 0
257char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
258
259static int lex(cstate *cs, int value)
260{
261 int tok = _lex(cs, value);
262 printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
263 tok == T_TEXT ? cs->text : "");
264 return tok;
265}
266#else
267#define lex(cs,v) _lex(cs,v)
268#endif
269
270static int parse_expr(cstate *cs, AConfig *node);
271
272static int
273parse_block(cstate *cs, AConfig *node)
274{
275 for(;;){
276 switch(lex(cs, 0)){
277 case T_TEXT:
278 if(parse_expr(cs, node)) return -1;
279 continue;
280
281 case T_CBRACE:
282 return 0;
283
284 default:
285 return -1;
286 }
287 }
288}
289
290static int
291parse_expr(cstate *cs, AConfig *node)
292{
293 /* last token was T_TEXT */
294 node = _aconfig_find(node, cs->text, 1);
295
296 for(;;) {
297 switch(lex(cs, 1)) {
298 case T_DOT:
299 if(lex(cs, 0) != T_TEXT) return -1;
300 node = _aconfig_find(node, cs->text, 1);
301 continue;
302
303 case T_TEXT:
304 node->value = cs->text;
305 return 0;
306
307 case T_OBRACE:
308 return parse_block(cs, node);
309
310 default:
311 return -1;
312 }
313 }
314}
315
316void
317aconfig_load(AConfig *root, char *data)
318{
319 if(data != 0) {
320 cstate cs;
321 cs.data = data;
322 cs.next = 0;
323
324 for(;;) {
325 switch(lex(&cs, 0)){
326 case T_TEXT:
327 if(parse_expr(&cs, root)) return;
328 break;
329 default:
330 return;
331 }
332 }
333 }
334}
335
336int
337aconfig_load_file(AConfig *root, const char *fn)
338{
339 char *data;
340 data = path_load_file(fn, NULL);
341 if (data == NULL)
342 return -1;
343
344 aconfig_load(root, data);
345 return 0;
346}
347
348
349typedef struct
350{
351 char buff[1024];
352 char* p;
353 char* end;
354 int fd;
355} Writer;
356
357static int
358writer_init( Writer* w, const char* fn )
359{
360 w->p = w->buff;
361 w->end = w->buff + sizeof(w->buff);
362
363 w->fd = creat( fn, 0755 );
364 if (w->fd < 0)
365 return -1;
366
367#ifdef _WIN32
368 _setmode( w->fd, _O_BINARY );
369#endif
370 return 0;
371}
372
373static void
374writer_write( Writer* w, const char* src, int len )
375{
376 while (len > 0) {
377 int avail = w->end - w->p;
378
379 if (avail > len)
380 avail = len;
381
382 memcpy( w->p, src, avail );
383 src += avail;
384 len -= avail;
385
386 w->p += avail;
387 if (w->p == w->end) {
388 write( w->fd, w->buff, w->p - w->buff );
389 w->p = w->buff;
390 }
391 }
392}
393
394static void
395writer_done( Writer* w )
396{
397 if (w->p > w->buff)
398 write( w->fd, w->buff, w->p - w->buff );
399 close( w->fd );
400}
401
402static void
403writer_margin( Writer* w, int margin)
404{
405 static const char spaces[10] = " ";
406 while (margin >= 10) {
407 writer_write(w,spaces,10);
408 margin -= 10;
409 }
410 if (margin > 0)
411 writer_write(w,spaces,margin);
412}
413
414static void
415writer_c(Writer* w, char c)
416{
417 writer_write(w, &c, 1);
418}
419
420static void
421writer_str(Writer* w, const char* str)
422{
423 writer_write(w, str, strlen(str));
424}
425
426static void
427writer_node(Writer* w, AConfig* node, int margin)
428{
429 writer_margin(w,margin);
430 writer_str(w, node->name);
431 writer_c(w,' ');
432
433 if (node->value[0]) {
434 writer_str(w, node->value);
435 writer_c(w,'\n');
436 }
437 else
438 {
439 AConfig* child;
440
441 writer_c(w, '{');
442 writer_c(w, '\n');
443
444 for (child = node->first_child; child; child = child->next)
445 writer_node(w,child,margin+4);
446
447 writer_margin(w,margin);
448 writer_c(w,'}');
449 writer_c(w,'\n');
450 }
451}
452
453int
454aconfig_save_file(AConfig *root, const char *fn)
455{
456 AConfig* child;
457 Writer w[1];
458
459 if (writer_init(w,fn) < 0)
460 return -1;
461
462 for (child = root->first_child; child; child = child->next)
463 writer_node(w,child,0);
464
465 writer_done(w);
466 return 0;
467}