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