1/* Copyright (C) 2007-2008 The Android Open Source Project 2** 3** This software is licensed under the terms of the GNU General Public 4** License version 2, as published by the Free Software Foundation, and 5** may be copied, distributed, and modified under those terms. 6** 7** This program is distributed in the hope that it will be useful, 8** but WITHOUT ANY WARRANTY; without even the implied warranty of 9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10** GNU General Public License for more details. 11*/ 12#include <string.h> 13#include <ctype.h> 14#include <stdlib.h> 15#include <fcntl.h> 16#include <unistd.h> 17 18#include "android/config.h" 19#include "android/utils/path.h" 20 21AConfig* 22aconfig_node(const char *name, const char *value) 23{ 24 AConfig *n; 25 26 n = (AConfig*) calloc(sizeof(AConfig), 1); 27 n->name = name ? name : ""; 28 n->value = value ? value : ""; 29 30 return n; 31} 32 33static AConfig* 34_aconfig_find(AConfig *root, const char *name, int create) 35{ 36 AConfig *node; 37 38 for(node = root->first_child; node; node = node->next) { 39 if(!strcmp(node->name, name)) return node; 40 } 41 42 if(create) { 43 node = (AConfig*) calloc(sizeof(AConfig), 1); 44 node->name = name; 45 node->value = ""; 46 47 if(root->last_child) { 48 root->last_child->next = node; 49 } else { 50 root->first_child = node; 51 } 52 root->last_child = node; 53 } 54 55 return node; 56} 57 58AConfig* 59aconfig_find(AConfig *root, const char *name) 60{ 61 return _aconfig_find(root, name, 0); 62} 63 64int 65aconfig_bool(AConfig *root, const char *name, int _default) 66{ 67 AConfig *n = _aconfig_find(root, name, 0); 68 if(n == 0) { 69 return _default; 70 } else { 71 switch(n->value[0]){ 72 case 'y': 73 case 'Y': 74 case '1': 75 return 1; 76 default: 77 return 0; 78 } 79 } 80} 81 82unsigned 83aconfig_unsigned(AConfig *root, const char *name, unsigned _default) 84{ 85 AConfig *n = _aconfig_find(root, name, 0); 86 if(n == 0) { 87 return _default; 88 } else { 89 return strtoul(n->value, 0, 0); 90 } 91} 92 93int 94aconfig_int(AConfig *root, const char *name, int _default) 95{ 96 AConfig *n = _aconfig_find(root, name, 0); 97 if(n == 0) { 98 return _default; 99 } else { 100 return strtol(n->value, 0, 0); 101 } 102} 103 104 105const char* 106aconfig_str(AConfig *root, const char *name, const char *_default) 107{ 108 AConfig *n = _aconfig_find(root, name, 0); 109 if(n == 0) { 110 return _default; 111 } else { 112 return n->value; 113 } 114} 115 116void 117aconfig_set(AConfig *root, const char *name, const char *value) 118{ 119 AConfig *node = _aconfig_find(root, name, 1); 120 node->value = value; 121} 122 123#define T_EOF 0 124#define T_TEXT 1 125#define T_DOT 2 126#define T_OBRACE 3 127#define T_CBRACE 4 128 129typedef struct 130{ 131 char *data; 132 char *text; 133 int len; 134 char next; 135} cstate; 136 137 138static int _lex(cstate *cs, int value) 139{ 140 char c; 141 char *s; 142 char *data; 143 144 data = cs->data; 145 146 if(cs->next != 0) { 147 c = cs->next; 148 cs->next = 0; 149 goto got_c; 150 } 151 152restart: 153 for(;;) { 154 c = *data++; 155 got_c: 156 if(isspace(c)) continue; 157 158 switch(c) { 159 case 0: 160 return T_EOF; 161 162 /* a sharp sign (#) starts a line comment and everything 163 * behind that is ignored until the end of line 164 */ 165 case '#': 166 for(;;) { 167 switch(*data) { 168 case 0: 169 cs->data = data; 170 return T_EOF; 171 case '\n': 172 cs->data = data + 1; 173 goto restart; 174 default: 175 data++; 176 } 177 } 178 break; 179 180 case '.': 181 cs->data = data; 182 return T_DOT; 183 184 case '{': 185 cs->data = data; 186 return T_OBRACE; 187 188 case '}': 189 cs->data = data; 190 return T_CBRACE; 191 192 default: 193 s = data - 1; 194 195 if(value) { 196 /* if we're looking for a value, then take anything 197 * until the end of line. note that sharp signs do 198 * not start comments then. the result will be stripped 199 * from trailing whitespace. 200 */ 201 for(;;) { 202 if(*data == 0) { 203 cs->data = data; 204 break; 205 } 206 if(*data == '\n') { 207 cs->data = data + 1; 208 *data-- = 0; 209 break; 210 } 211 data++; 212 } 213 214 /* strip trailing whitespace */ 215 while(data > s){ 216 if(!isspace(*data)) break; 217 *data-- = 0; 218 } 219 220 goto got_text; 221 } else { 222 /* looking for a key name. we stop at whitspace, 223 * EOF, of T_DOT/T_OBRACE/T_CBRACE characters. 224 * note that the name can include sharp signs 225 */ 226 for(;;) { 227 if(isspace(*data)) { 228 *data = 0; 229 cs->data = data + 1; 230 goto got_text; 231 } 232 switch(*data) { 233 case 0: 234 cs->data = data; 235 goto got_text; 236 case '.': 237 case '{': 238 case '}': 239 cs->next = *data; 240 *data = 0; 241 cs->data = data + 1; 242 goto got_text; 243 default: 244 data++; 245 } 246 } 247 } 248 } 249 } 250 251got_text: 252 cs->text = s; 253 return T_TEXT; 254} 255 256#if 0 257char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" }; 258 259static int lex(cstate *cs, int value) 260{ 261 int tok = _lex(cs, value); 262 printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok], 263 tok == T_TEXT ? cs->text : ""); 264 return tok; 265} 266#else 267#define lex(cs,v) _lex(cs,v) 268#endif 269 270static int parse_expr(cstate *cs, AConfig *node); 271 272static int 273parse_block(cstate *cs, AConfig *node) 274{ 275 for(;;){ 276 switch(lex(cs, 0)){ 277 case T_TEXT: 278 if(parse_expr(cs, node)) return -1; 279 continue; 280 281 case T_CBRACE: 282 return 0; 283 284 default: 285 return -1; 286 } 287 } 288} 289 290static int 291parse_expr(cstate *cs, AConfig *node) 292{ 293 /* last token was T_TEXT */ 294 node = _aconfig_find(node, cs->text, 1); 295 296 for(;;) { 297 switch(lex(cs, 1)) { 298 case T_DOT: 299 if(lex(cs, 0) != T_TEXT) return -1; 300 node = _aconfig_find(node, cs->text, 1); 301 continue; 302 303 case T_TEXT: 304 node->value = cs->text; 305 return 0; 306 307 case T_OBRACE: 308 return parse_block(cs, node); 309 310 default: 311 return -1; 312 } 313 } 314} 315 316void 317aconfig_load(AConfig *root, char *data) 318{ 319 if(data != 0) { 320 cstate cs; 321 cs.data = data; 322 cs.next = 0; 323 324 for(;;) { 325 switch(lex(&cs, 0)){ 326 case T_TEXT: 327 if(parse_expr(&cs, root)) return; 328 break; 329 default: 330 return; 331 } 332 } 333 } 334} 335 336int 337aconfig_load_file(AConfig *root, const char *fn) 338{ 339 char *data; 340 data = path_load_file(fn, NULL); 341 if (data == NULL) 342 return -1; 343 344 aconfig_load(root, data); 345 return 0; 346} 347 348 349typedef struct 350{ 351 char buff[1024]; 352 char* p; 353 char* end; 354 int fd; 355} Writer; 356 357static int 358writer_init( Writer* w, const char* fn ) 359{ 360 w->p = w->buff; 361 w->end = w->buff + sizeof(w->buff); 362 363 w->fd = creat( fn, 0755 ); 364 if (w->fd < 0) 365 return -1; 366 367#ifdef _WIN32 368 _setmode( w->fd, _O_BINARY ); 369#endif 370 return 0; 371} 372 373static void 374writer_write( Writer* w, const char* src, int len ) 375{ 376 while (len > 0) { 377 int avail = w->end - w->p; 378 379 if (avail > len) 380 avail = len; 381 382 memcpy( w->p, src, avail ); 383 src += avail; 384 len -= avail; 385 386 w->p += avail; 387 if (w->p == w->end) { 388 write( w->fd, w->buff, w->p - w->buff ); 389 w->p = w->buff; 390 } 391 } 392} 393 394static void 395writer_done( Writer* w ) 396{ 397 if (w->p > w->buff) 398 write( w->fd, w->buff, w->p - w->buff ); 399 close( w->fd ); 400} 401 402static void 403writer_margin( Writer* w, int margin) 404{ 405 static const char spaces[10] = " "; 406 while (margin >= 10) { 407 writer_write(w,spaces,10); 408 margin -= 10; 409 } 410 if (margin > 0) 411 writer_write(w,spaces,margin); 412} 413 414static void 415writer_c(Writer* w, char c) 416{ 417 writer_write(w, &c, 1); 418} 419 420static void 421writer_str(Writer* w, const char* str) 422{ 423 writer_write(w, str, strlen(str)); 424} 425 426static void 427writer_node(Writer* w, AConfig* node, int margin) 428{ 429 writer_margin(w,margin); 430 writer_str(w, node->name); 431 writer_c(w,' '); 432 433 if (node->value[0]) { 434 writer_str(w, node->value); 435 writer_c(w,'\n'); 436 } 437 else 438 { 439 AConfig* child; 440 441 writer_c(w, '{'); 442 writer_c(w, '\n'); 443 444 for (child = node->first_child; child; child = child->next) 445 writer_node(w,child,margin+4); 446 447 writer_margin(w,margin); 448 writer_c(w,'}'); 449 writer_c(w,'\n'); 450 } 451} 452 453int 454aconfig_save_file(AConfig *root, const char *fn) 455{ 456 AConfig* child; 457 Writer w[1]; 458 459 if (writer_init(w,fn) < 0) 460 return -1; 461 462 for (child = root->first_child; child; child = child->next) 463 writer_node(w,child,0); 464 465 writer_done(w); 466 return 0; 467} 468