diff options
Diffstat (limited to 'android_config.c')
-rw-r--r-- | android_config.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/android_config.c b/android_config.c new file mode 100644 index 0000000..c90a219 --- /dev/null +++ b/android_config.c @@ -0,0 +1,471 @@ +/* Copyright (C) 2007-2008 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> + +#include "android_config.h" +#include "android_utils.h" + +#if !defined(_WIN23) && !defined(O_BINARY) +# define O_BINARY 0 +#endif + +AConfig* +aconfig_node(const char *name, const char *value) +{ + AConfig *n; + + n = (AConfig*) calloc(sizeof(AConfig), 1); + n->name = name ? name : ""; + n->value = value ? value : ""; + + return n; +} + +static AConfig* +_aconfig_find(AConfig *root, const char *name, int create) +{ + AConfig *node; + + for(node = root->first_child; node; node = node->next) { + if(!strcmp(node->name, name)) return node; + } + + if(create) { + node = (AConfig*) calloc(sizeof(AConfig), 1); + node->name = name; + node->value = ""; + + if(root->last_child) { + root->last_child->next = node; + } else { + root->first_child = node; + } + root->last_child = node; + } + + return node; +} + +AConfig* +aconfig_find(AConfig *root, const char *name) +{ + return _aconfig_find(root, name, 0); +} + +int +aconfig_bool(AConfig *root, const char *name, int _default) +{ + AConfig *n = _aconfig_find(root, name, 0); + if(n == 0) { + return _default; + } else { + switch(n->value[0]){ + case 'y': + case 'Y': + case '1': + return 1; + default: + return 0; + } + } +} + +unsigned +aconfig_unsigned(AConfig *root, const char *name, unsigned _default) +{ + AConfig *n = _aconfig_find(root, name, 0); + if(n == 0) { + return _default; + } else { + return strtoul(n->value, 0, 0); + } +} + +int +aconfig_int(AConfig *root, const char *name, int _default) +{ + AConfig *n = _aconfig_find(root, name, 0); + if(n == 0) { + return _default; + } else { + return strtol(n->value, 0, 0); + } +} + + +const char* +aconfig_str(AConfig *root, const char *name, const char *_default) +{ + AConfig *n = _aconfig_find(root, name, 0); + if(n == 0) { + return _default; + } else { + return n->value; + } +} + +void +aconfig_set(AConfig *root, const char *name, const char *value) +{ + AConfig *node = _aconfig_find(root, name, 1); + node->value = value; +} + +#define T_EOF 0 +#define T_TEXT 1 +#define T_DOT 2 +#define T_OBRACE 3 +#define T_CBRACE 4 + +typedef struct +{ + char *data; + char *text; + int len; + char next; +} cstate; + + +static int _lex(cstate *cs, int value) +{ + char c; + char *s; + char *data; + + data = cs->data; + + if(cs->next != 0) { + c = cs->next; + cs->next = 0; + goto got_c; + } + +restart: + for(;;) { + c = *data++; + got_c: + if(isspace(c)) continue; + + switch(c) { + case 0: + return T_EOF; + + /* a sharp sign (#) starts a line comment and everything + * behind that is ignored until the end of line + */ + case '#': + for(;;) { + switch(*data) { + case 0: + cs->data = data; + return T_EOF; + case '\n': + cs->data = data + 1; + goto restart; + default: + data++; + } + } + break; + + case '.': + cs->data = data; + return T_DOT; + + case '{': + cs->data = data; + return T_OBRACE; + + case '}': + cs->data = data; + return T_CBRACE; + + default: + s = data - 1; + + if(value) { + /* if we're looking for a value, then take anything + * until the end of line. note that sharp signs do + * not start comments then. the result will be stripped + * from trailing whitespace. + */ + for(;;) { + if(*data == 0) { + cs->data = data; + break; + } + if(*data == '\n') { + cs->data = data + 1; + *data-- = 0; + break; + } + data++; + } + + /* strip trailing whitespace */ + while(data > s){ + if(!isspace(*data)) break; + *data-- = 0; + } + + goto got_text; + } else { + /* looking for a key name. we stop at whitspace, + * EOF, of T_DOT/T_OBRACE/T_CBRACE characters. + * note that the name can include sharp signs + */ + for(;;) { + if(isspace(*data)) { + *data = 0; + cs->data = data + 1; + goto got_text; + } + switch(*data) { + case 0: + cs->data = data; + goto got_text; + case '.': + case '{': + case '}': + cs->next = *data; + *data = 0; + cs->data = data + 1; + goto got_text; + default: + data++; + } + } + } + } + } + +got_text: + cs->text = s; + return T_TEXT; +} + +#if 0 +char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" }; + +static int lex(cstate *cs, int value) +{ + int tok = _lex(cs, value); + printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok], + tok == T_TEXT ? cs->text : ""); + return tok; +} +#else +#define lex(cs,v) _lex(cs,v) +#endif + +static int parse_expr(cstate *cs, AConfig *node); + +static int +parse_block(cstate *cs, AConfig *node) +{ + for(;;){ + switch(lex(cs, 0)){ + case T_TEXT: + if(parse_expr(cs, node)) return -1; + continue; + + case T_CBRACE: + return 0; + + default: + return -1; + } + } +} + +static int +parse_expr(cstate *cs, AConfig *node) +{ + /* last token was T_TEXT */ + node = _aconfig_find(node, cs->text, 1); + + for(;;) { + switch(lex(cs, 1)) { + case T_DOT: + if(lex(cs, 0) != T_TEXT) return -1; + node = _aconfig_find(node, cs->text, 1); + continue; + + case T_TEXT: + node->value = cs->text; + return 0; + + case T_OBRACE: + return parse_block(cs, node); + + default: + return -1; + } + } +} + +void +aconfig_load(AConfig *root, char *data) +{ + if(data != 0) { + cstate cs; + cs.data = data; + cs.next = 0; + + for(;;) { + switch(lex(&cs, 0)){ + case T_TEXT: + if(parse_expr(&cs, root)) return; + break; + default: + return; + } + } + } +} + +int +aconfig_load_file(AConfig *root, const char *fn) +{ + char *data; + data = load_text_file(fn); + if (data == NULL) + return -1; + + aconfig_load(root, data); + return 0; +} + + +typedef struct +{ + char buff[1024]; + char* p; + char* end; + int fd; +} Writer; + +static int +writer_init( Writer* w, const char* fn ) +{ + w->p = w->buff; + w->end = w->buff + sizeof(w->buff); + + w->fd = creat( fn, 0755 ); + if (w->fd < 0) + return -1; + +#ifdef _WIN32 + _setmode( w->fd, _O_BINARY ); +#endif + return 0; +} + +static void +writer_write( Writer* w, const char* src, int len ) +{ + while (len > 0) { + int avail = w->end - w->p; + + if (avail > len) + avail = len; + + memcpy( w->p, src, avail ); + src += avail; + len -= avail; + + w->p += avail; + if (w->p == w->end) { + write( w->fd, w->buff, w->p - w->buff ); + w->p = w->buff; + } + } +} + +static void +writer_done( Writer* w ) +{ + if (w->p > w->buff) + write( w->fd, w->buff, w->p - w->buff ); + close( w->fd ); +} + +static void +writer_margin( Writer* w, int margin) +{ + static const char spaces[10] = " "; + while (margin >= 10) { + writer_write(w,spaces,10); + margin -= 10; + } + if (margin > 0) + writer_write(w,spaces,margin); +} + +static void +writer_c(Writer* w, char c) +{ + writer_write(w, &c, 1); +} + +static void +writer_str(Writer* w, const char* str) +{ + writer_write(w, str, strlen(str)); +} + +static void +writer_node(Writer* w, AConfig* node, int margin) +{ + writer_margin(w,margin); + writer_str(w, node->name); + writer_c(w,' '); + + if (node->value[0]) { + writer_str(w, node->value); + writer_c(w,'\n'); + } + else + { + AConfig* child; + + writer_c(w, '{'); + writer_c(w, '\n'); + + for (child = node->first_child; child; child = child->next) + writer_node(w,child,margin+4); + + writer_margin(w,margin); + writer_c(w,'}'); + writer_c(w,'\n'); + } +} + +int +aconfig_save_file(AConfig *root, const char *fn) +{ + AConfig* child; + Writer w[1]; + + if (writer_init(w,fn) < 0) + return -1; + + for (child = root->first_child; child; child = child->next) + writer_node(w,child,0); + + writer_done(w); + return 0; +} |