/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include cnode* config_node(const char *name, const char *value) { cnode *node; node = calloc(sizeof(cnode), 1); if(node) { node->name = name ? name : ""; node->value = value ? value : ""; } return node; } cnode* config_find(cnode *root, const char *name) { cnode *node, *match = NULL; /* we walk the whole list, as we need to return the last (newest) entry */ for(node = root->first_child; node; node = node->next) if(!strcmp(node->name, name)) match = node; return match; } static cnode* _config_create(cnode *root, const char *name) { cnode *node; node = config_node(name, NULL); if(root->last_child) root->last_child->next = node; else root->first_child = node; root->last_child = node; return node; } int config_bool(cnode *root, const char *name, int _default) { cnode *node; node = config_find(root, name); if(!node) return _default; switch(node->value[0]) { case 'y': case 'Y': case '1': return 1; default: return 0; } } const char* config_str(cnode *root, const char *name, const char *_default) { cnode *node; node = config_find(root, name); if(!node) return _default; return node->value; } void config_set(cnode *root, const char *name, const char *value) { cnode *node; node = config_find(root, name); if(node) node->value = value; else { node = _config_create(root, name); 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; 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) { 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 { 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, cnode *node); static int parse_block(cstate *cs, cnode *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, cnode *root) { cnode *node; /* last token was T_TEXT */ node = config_find(root, cs->text); if(!node || *node->value) node = _config_create(root, cs->text); for(;;) { switch(lex(cs, 1)) { case T_DOT: if(lex(cs, 0) != T_TEXT) return -1; node = _config_create(node, cs->text); continue; case T_TEXT: node->value = cs->text; return 0; case T_OBRACE: return parse_block(cs, node); default: return -1; } } } void config_load(cnode *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; } } } } void config_load_file(cnode *root, const char *fn) { char *data; data = load_file(fn, 0); config_load(root, data); } void config_free(cnode *root) { cnode *cur = root->first_child; while (cur) { cnode *prev = cur; config_free(cur); cur = cur->next; free(prev); } }