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