Home | History | Annotate | Download | only in libcutils
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <cutils/config_utils.h>
     18 
     19 #include <string.h>
     20 #include <ctype.h>
     21 #include <stdlib.h>
     22 #include <fcntl.h>
     23 #include <unistd.h>
     24 
     25 #include <cutils/misc.h>
     26 
     27 cnode* config_node(const char *name, const char *value)
     28 {
     29     cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));
     30     if(node) {
     31         node->name = name ? name : "";
     32         node->value = value ? value : "";
     33     }
     34 
     35     return node;
     36 }
     37 
     38 cnode* config_find(cnode *root, const char *name)
     39 {
     40     cnode *node, *match = NULL;
     41 
     42     /* we walk the whole list, as we need to return the last (newest) entry */
     43     for(node = root->first_child; node; node = node->next)
     44         if(!strcmp(node->name, name))
     45             match = node;
     46 
     47     return match;
     48 }
     49 
     50 static cnode* _config_create(cnode *root, const char *name)
     51 {
     52     cnode *node;
     53 
     54     node = config_node(name, NULL);
     55 
     56     if(root->last_child)
     57         root->last_child->next = node;
     58     else
     59         root->first_child = node;
     60 
     61     root->last_child = node;
     62 
     63     return node;
     64 }
     65 
     66 int config_bool(cnode *root, const char *name, int _default)
     67 {
     68     cnode *node;
     69 
     70     node = config_find(root, name);
     71     if(!node)
     72         return _default;
     73 
     74     switch(node->value[0]) {
     75     case 'y':
     76     case 'Y':
     77     case '1':
     78         return 1;
     79     default:
     80         return 0;
     81     }
     82 }
     83 
     84 const char* config_str(cnode *root, const char *name, const char *_default)
     85 {
     86     cnode *node;
     87 
     88     node = config_find(root, name);
     89     if(!node)
     90         return _default;
     91     return node->value;
     92 }
     93 
     94 void config_set(cnode *root, const char *name, const char *value)
     95 {
     96     cnode *node;
     97 
     98     node = config_find(root, name);
     99     if(node)
    100         node->value = value;
    101     else {
    102         node = _config_create(root, name);
    103         node->value = value;
    104     }
    105 }
    106 
    107 #define T_EOF 0
    108 #define T_TEXT 1
    109 #define T_DOT 2
    110 #define T_OBRACE 3
    111 #define T_CBRACE 4
    112 
    113 typedef struct
    114 {
    115     char *data;
    116     char *text;
    117     int len;
    118     char next;
    119 } cstate;
    120 
    121 static int _lex(cstate *cs, int value)
    122 {
    123     char c;
    124     char *s;
    125     char *data;
    126 
    127     data = cs->data;
    128 
    129     if(cs->next != 0) {
    130         c = cs->next;
    131         cs->next = 0;
    132         goto got_c;
    133     }
    134 
    135 restart:
    136     for(;;) {
    137         c = *data++;
    138     got_c:
    139         if(isspace(c))
    140             continue;
    141 
    142         switch(c) {
    143         case 0:
    144             return T_EOF;
    145 
    146         case '#':
    147             for(;;) {
    148                 switch(*data) {
    149                 case 0:
    150                     cs->data = data;
    151                     return T_EOF;
    152                 case '\n':
    153                     cs->data = data + 1;
    154                     goto restart;
    155                 default:
    156                     data++;
    157                 }
    158             }
    159             break;
    160 
    161         case '.':
    162             cs->data = data;
    163             return T_DOT;
    164 
    165         case '{':
    166             cs->data = data;
    167             return T_OBRACE;
    168 
    169         case '}':
    170             cs->data = data;
    171             return T_CBRACE;
    172 
    173         default:
    174             s = data - 1;
    175 
    176             if(value) {
    177                 for(;;) {
    178                     if(*data == 0) {
    179                         cs->data = data;
    180                         break;
    181                     }
    182                     if(*data == '\n') {
    183                         cs->data = data + 1;
    184                         *data-- = 0;
    185                         break;
    186                     }
    187                     data++;
    188                 }
    189 
    190                     /* strip trailing whitespace */
    191                 while(data > s){
    192                     if(!isspace(*data)) break;
    193                     *data-- = 0;
    194                 }
    195 
    196                 goto got_text;
    197             } else {
    198                 for(;;) {
    199                     if(isspace(*data)) {
    200                         *data = 0;
    201                         cs->data = data + 1;
    202                         goto got_text;
    203                     }
    204                     switch(*data) {
    205                     case 0:
    206                         cs->data = data;
    207                         goto got_text;
    208                     case '.':
    209                     case '{':
    210                     case '}':
    211                         cs->next = *data;
    212                         *data = 0;
    213                         cs->data = data + 1;
    214                         goto got_text;
    215                     default:
    216                         data++;
    217                     }
    218                 }
    219             }
    220         }
    221     }
    222 
    223 got_text:
    224     cs->text = s;
    225     return T_TEXT;
    226 }
    227 
    228 #if 0
    229 char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
    230 
    231 static int lex(cstate *cs, int value)
    232 {
    233     int tok = _lex(cs, value);
    234     printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
    235            tok == T_TEXT ? cs->text : "");
    236     return tok;
    237 }
    238 #else
    239 #define lex(cs,v) _lex(cs,v)
    240 #endif
    241 
    242 static int parse_expr(cstate *cs, cnode *node);
    243 
    244 static int parse_block(cstate *cs, cnode *node)
    245 {
    246     for(;;){
    247         switch(lex(cs, 0)){
    248         case T_TEXT:
    249             if(parse_expr(cs, node)) return -1;
    250             continue;
    251 
    252         case T_CBRACE:
    253             return 0;
    254 
    255         default:
    256             return -1;
    257         }
    258     }
    259 }
    260 
    261 static int parse_expr(cstate *cs, cnode *root)
    262 {
    263     cnode *node;
    264 
    265         /* last token was T_TEXT */
    266     node = config_find(root, cs->text);
    267     if(!node || *node->value)
    268         node = _config_create(root, cs->text);
    269 
    270     for(;;) {
    271         switch(lex(cs, 1)) {
    272         case T_DOT:
    273             if(lex(cs, 0) != T_TEXT)
    274                 return -1;
    275             node = _config_create(node, cs->text);
    276             continue;
    277 
    278         case T_TEXT:
    279             node->value = cs->text;
    280             return 0;
    281 
    282         case T_OBRACE:
    283             return parse_block(cs, node);
    284 
    285         default:
    286             return -1;
    287         }
    288     }
    289 }
    290 
    291 void config_load(cnode *root, char *data)
    292 {
    293     if(data != 0) {
    294         cstate cs;
    295         cs.data = data;
    296         cs.next = 0;
    297 
    298         for(;;) {
    299             switch(lex(&cs, 0)) {
    300             case T_TEXT:
    301                 if(parse_expr(&cs, root))
    302                     return;
    303                 break;
    304             default:
    305                 return;
    306             }
    307         }
    308     }
    309 }
    310 
    311 void config_load_file(cnode *root, const char *fn)
    312 {
    313     char* data = static_cast<char*>(load_file(fn, nullptr));
    314     config_load(root, data);
    315     // TODO: deliberate leak :-/
    316 }
    317 
    318 void config_free(cnode *root)
    319 {
    320     cnode *cur = root->first_child;
    321 
    322     while (cur) {
    323         cnode *prev = cur;
    324         config_free(cur);
    325         cur = cur->next;
    326         free(prev);
    327     }
    328 }
    329