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