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