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