blob: 2c37b03cb92157924a4030307988126089c97ee0 [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>
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +020017#include <errno.h>
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080018
19#include "android/config.h"
20#include "android/utils/path.h"
21
22AConfig*
23aconfig_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
34static AConfig*
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
59AConfig*
60aconfig_find(AConfig *root, const char *name)
61{
62 return _aconfig_find(root, name, 0);
63}
64
65int
66aconfig_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
83unsigned
84aconfig_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
94int
95aconfig_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
106const char*
107aconfig_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
117void
118aconfig_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
130typedef struct
131{
132 char *data;
133 char *text;
134 int len;
135 char next;
136} cstate;
137
138
139static 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
153restart:
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) {
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200197 /* if we're looking for a value, then take anything
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800198 * 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
252got_text:
253 cs->text = s;
254 return T_TEXT;
255}
256
257#if 0
258char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
259
260static 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
271static int parse_expr(cstate *cs, AConfig *node);
272
273static int
274parse_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
291static int
292parse_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
317void
318aconfig_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
337int
338aconfig_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
350typedef struct
351{
352 char buff[1024];
353 char* p;
354 char* end;
355 int fd;
356} Writer;
357
358static int
359writer_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
374static void
375writer_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) {
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200389 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;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800395 w->p = w->buff;
396 }
397 }
398}
399
400static void
401writer_done( Writer* w )
402{
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200403 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 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800409 close( w->fd );
410}
411
412static void
413writer_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
424static void
425writer_c(Writer* w, char c)
426{
427 writer_write(w, &c, 1);
428}
429
430static void
431writer_str(Writer* w, const char* str)
432{
433 writer_write(w, str, strlen(str));
434}
435
436static void
437writer_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
463int
464aconfig_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}