• 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.h"
20 #include "android/utils/path.h"
21 
22 AConfig*
aconfig_node(const char * name,const char * value)23 aconfig_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 
34 static AConfig*
_aconfig_find(AConfig * root,const char * name,int create)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 
59 AConfig*
aconfig_find(AConfig * root,const char * name)60 aconfig_find(AConfig *root, const char *name)
61 {
62     return _aconfig_find(root, name, 0);
63 }
64 
65 int
aconfig_bool(AConfig * root,const char * name,int _default)66 aconfig_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 
83 unsigned
aconfig_unsigned(AConfig * root,const char * name,unsigned _default)84 aconfig_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 
94 int
aconfig_int(AConfig * root,const char * name,int _default)95 aconfig_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 
106 const char*
aconfig_str(AConfig * root,const char * name,const char * _default)107 aconfig_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 
117 void
aconfig_set(AConfig * root,const char * name,const char * value)118 aconfig_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 
130 typedef struct
131 {
132     char *data;
133     char *text;
134     int len;
135     char next;
136 } cstate;
137 
138 
_lex(cstate * cs,int value)139 static 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 
153 restart:
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) {
197                /* if we're looking for a value, then take anything
198                 * 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 
252 got_text:
253     cs->text = s;
254     return T_TEXT;
255 }
256 
257 #if 0
258 char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
259 
260 static 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 
271 static int parse_expr(cstate *cs, AConfig *node);
272 
273 static int
parse_block(cstate * cs,AConfig * node)274 parse_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 
291 static int
parse_expr(cstate * cs,AConfig * node)292 parse_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 
317 void
aconfig_load(AConfig * root,char * data)318 aconfig_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 
337 int
aconfig_load_file(AConfig * root,const char * fn)338 aconfig_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 
350 typedef struct
351 {
352     char   buff[1024];
353     char*  p;
354     char*  end;
355     int    fd;
356 } Writer;
357 
358 static int
writer_init(Writer * w,const char * fn)359 writer_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 
374 static void
writer_write(Writer * w,const char * src,int len)375 writer_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) {
389             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;
395             w->p = w->buff;
396         }
397     }
398 }
399 
400 static void
writer_done(Writer * w)401 writer_done( Writer*  w )
402 {
403     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     }
409     close( w->fd );
410 }
411 
412 static void
writer_margin(Writer * w,int margin)413 writer_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 
424 static void
writer_c(Writer * w,char c)425 writer_c(Writer*  w, char  c)
426 {
427     writer_write(w, &c, 1);
428 }
429 
430 static void
writer_str(Writer * w,const char * str)431 writer_str(Writer*  w, const char*  str)
432 {
433     writer_write(w, str, strlen(str));
434 }
435 
436 static void
writer_node(Writer * w,AConfig * node,int margin)437 writer_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 
463 int
aconfig_save_file(AConfig * root,const char * fn)464 aconfig_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 }
478