Home | History | Annotate | Download | only in android
      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*
     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*
     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*
     61 aconfig_find(AConfig *root, const char *name)
     62 {
     63     return _aconfig_find(root, name, 0);
     64 }
     65 
     66 int
     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
     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
     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*
    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
    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 
    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
    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
    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
    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
    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
    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
    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
    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
    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
    419 writer_c(Writer*  w, char  c)
    420 {
    421     writer_write(w, &c, 1);
    422 }
    423 
    424 static void
    425 writer_str(Writer*  w, const char*  str)
    426 {
    427     writer_write(w, str, strlen(str));
    428 }
    429 
    430 static void
    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
    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