• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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