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