1 /* 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
21 AConfig*
aconfig_node(const char * name,const char * value)22 aconfig_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
33 static AConfig*
_aconfig_find(AConfig * root,const char * name,int create)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
58 AConfig*
aconfig_find(AConfig * root,const char * name)59 aconfig_find(AConfig *root, const char *name)
60 {
61 return _aconfig_find(root, name, 0);
62 }
63
64 int
aconfig_bool(AConfig * root,const char * name,int _default)65 aconfig_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
82 unsigned
aconfig_unsigned(AConfig * root,const char * name,unsigned _default)83 aconfig_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
93 int
aconfig_int(AConfig * root,const char * name,int _default)94 aconfig_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
105 const char*
aconfig_str(AConfig * root,const char * name,const char * _default)106 aconfig_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
116 void
aconfig_set(AConfig * root,const char * name,const char * value)117 aconfig_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
129 typedef struct
130 {
131 char *data;
132 char *text;
133 int len;
134 char next;
135 } cstate;
136
137
_lex(cstate * cs,int value)138 static 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
152 restart:
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
251 got_text:
252 cs->text = s;
253 return T_TEXT;
254 }
255
256 #if 0
257 char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
258
259 static 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
270 static int parse_expr(cstate *cs, AConfig *node);
271
272 static int
parse_block(cstate * cs,AConfig * node)273 parse_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
290 static int
parse_expr(cstate * cs,AConfig * node)291 parse_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
316 void
aconfig_load(AConfig * root,char * data)317 aconfig_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
336 int
aconfig_load_file(AConfig * root,const char * fn)337 aconfig_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
349 typedef struct
350 {
351 char buff[1024];
352 char* p;
353 char* end;
354 int fd;
355 } Writer;
356
357 static int
writer_init(Writer * w,const char * fn)358 writer_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
373 static void
writer_write(Writer * w,const char * src,int len)374 writer_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
394 static void
writer_done(Writer * w)395 writer_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
402 static void
writer_margin(Writer * w,int margin)403 writer_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
414 static void
writer_c(Writer * w,char c)415 writer_c(Writer* w, char c)
416 {
417 writer_write(w, &c, 1);
418 }
419
420 static void
writer_str(Writer * w,const char * str)421 writer_str(Writer* w, const char* str)
422 {
423 writer_write(w, str, strlen(str));
424 }
425
426 static void
writer_node(Writer * w,AConfig * node,int margin)427 writer_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
453 int
aconfig_save_file(AConfig * root,const char * fn)454 aconfig_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 }
468