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 23 AConfig* 24 aconfig_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 35 static 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 60 AConfig* 61 aconfig_find(AConfig *root, const char *name) 62 { 63 return _aconfig_find(root, name, 0); 64 } 65 66 int 67 aconfig_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 84 unsigned 85 aconfig_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 95 int 96 aconfig_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 107 const char* 108 aconfig_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 118 void 119 aconfig_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 131 typedef struct 132 { 133 char *data; 134 char *text; 135 int len; 136 char next; 137 } cstate; 138 139 140 static 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 154 restart: 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 253 got_text: 254 cs->text = s; 255 return T_TEXT; 256 } 257 258 #if 0 259 char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" }; 260 261 static 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 272 static int parse_expr(cstate *cs, AConfig *node); 273 274 static int 275 parse_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 292 static int 293 parse_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 318 void 319 aconfig_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 338 int 339 aconfig_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 351 typedef struct 352 { 353 char buff[1024]; 354 char* p; 355 char* end; 356 int fd; 357 } Writer; 358 359 static int 360 writer_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 375 static void 376 writer_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 397 static void 398 writer_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 406 static void 407 writer_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 418 static void 419 writer_c(Writer* w, char c) 420 { 421 writer_write(w, &c, 1); 422 } 423 424 static void 425 writer_str(Writer* w, const char* str) 426 { 427 writer_write(w, str, strlen(str)); 428 } 429 430 static void 431 writer_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 457 int 458 aconfig_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