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 26cnode* 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 39cnode* 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 51static 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 67int 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 85const 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 95void 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 114typedef struct 115{ 116 char *data; 117 char *text; 118 int len; 119 char next; 120} cstate; 121 122static 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 136restart: 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 224got_text: 225 cs->text = s; 226 return T_TEXT; 227} 228 229#if 0 230char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" }; 231 232static 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 243static int parse_expr(cstate *cs, cnode *node); 244 245static 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 262static 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 292void 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 312void 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