| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 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 |  | 
| Elliott Hughes | 8e9aeb9 | 2017-11-10 10:22:07 -0800 | [diff] [blame] | 17 | #include <cutils/config_utils.h> | 
 | 18 |  | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 19 | #include <string.h> | 
 | 20 | #include <ctype.h> | 
 | 21 | #include <stdlib.h> | 
 | 22 | #include <fcntl.h> | 
 | 23 | #include <unistd.h> | 
 | 24 |  | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 25 | #include <cutils/misc.h> | 
 | 26 |  | 
 | 27 | cnode* config_node(const char *name, const char *value) | 
 | 28 | { | 
| Elliott Hughes | 8e9aeb9 | 2017-11-10 10:22:07 -0800 | [diff] [blame] | 29 |     cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1)); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 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 | { | 
| Elliott Hughes | 8e9aeb9 | 2017-11-10 10:22:07 -0800 | [diff] [blame] | 313 |     char* data = static_cast<char*>(load_file(fn, nullptr)); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 314 |     config_load(root, data); | 
| Elliott Hughes | 8e9aeb9 | 2017-11-10 10:22:07 -0800 | [diff] [blame] | 315 |     // TODO: deliberate leak :-/ | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 316 | } | 
| Eric Laurent | c3cf1a8 | 2011-05-26 13:57:03 -0700 | [diff] [blame] | 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 | } |