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.h"
     20 #include "android/utils/path.h"
     21 
     22 AConfig*
     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*
     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*
     60 aconfig_find(AConfig *root, const char *name)
     61 {
     62     return _aconfig_find(root, name, 0);
     63 }
     64 
     65 int
     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
     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
     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*
    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
    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 
    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
    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
    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
    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
    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
    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
    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
    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
    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
    425 writer_c(Writer*  w, char  c)
    426 {
    427     writer_write(w, &c, 1);
    428 }
    429 
    430 static void
    431 writer_str(Writer*  w, const char*  str)
    432 {
    433     writer_write(w, str, strlen(str));
    434 }
    435 
    436 static void
    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
    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