blob: 8e55f4edc14bb4fa4fb37bbba511452b519d0459 [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
David 'Digit' Turner73dd5fc2014-02-04 12:50:55 +010019#include "android/config-file.h"
David 'Digit' Turneraf81d742014-02-03 17:11:18 +010020#include "android/utils/eintr_wrapper.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080021#include "android/utils/path.h"
22
23AConfig*
24aconfig_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
35static AConfig*
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
60AConfig*
61aconfig_find(AConfig *root, const char *name)
62{
63 return _aconfig_find(root, name, 0);
64}
65
66int
67aconfig_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
84unsigned
85aconfig_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
95int
96aconfig_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
107const char*
108aconfig_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
118void
119aconfig_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
131typedef struct
132{
133 char *data;
134 char *text;
135 int len;
136 char next;
137} cstate;
138
139
140static 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
154restart:
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) {
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200198 /* if we're looking for a value, then take anything
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800199 * 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
253got_text:
254 cs->text = s;
255 return T_TEXT;
256}
257
258#if 0
259char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
260
261static 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
272static int parse_expr(cstate *cs, AConfig *node);
273
274static int
275parse_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
292static int
293parse_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
318void
319aconfig_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
338int
339aconfig_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
351typedef struct
352{
353 char buff[1024];
354 char* p;
355 char* end;
356 int fd;
357} Writer;
358
359static int
360writer_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
375static void
376writer_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) {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100390 if (HANDLE_EINTR(write(w->fd, w->buff, w->p - w->buff)) < 0)
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200391 break;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800392 w->p = w->buff;
393 }
394 }
395}
396
397static void
398writer_done( Writer* w )
399{
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200400 if (w->p > w->buff) {
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100401 HANDLE_EINTR(write(w->fd, w->buff, w->p - w->buff));
David 'Digit' Turner4e024bb2010-09-22 14:19:28 +0200402 }
David 'Digit' Turneraf81d742014-02-03 17:11:18 +0100403 IGNORE_EINTR(close( w->fd ));
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800404}
405
406static void
407writer_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
418static void
419writer_c(Writer* w, char c)
420{
421 writer_write(w, &c, 1);
422}
423
424static void
425writer_str(Writer* w, const char* str)
426{
427 writer_write(w, str, strlen(str));
428}
429
430static void
431writer_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
457int
458aconfig_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}