• 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 #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