summaryrefslogtreecommitdiffstats
path: root/libcutils/config_utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libcutils/config_utils.cpp')
-rw-r--r--libcutils/config_utils.cpp328
1 files changed, 328 insertions, 0 deletions
diff --git a/libcutils/config_utils.cpp b/libcutils/config_utils.cpp
new file mode 100644
index 000000000..a3af01a58
--- /dev/null
+++ b/libcutils/config_utils.cpp
@@ -0,0 +1,328 @@
+/*
+ * 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 <cutils/config_utils.h>
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <cutils/misc.h>
+
+cnode* config_node(const char *name, const char *value)
+{
+ cnode* node = static_cast<cnode*>(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 = static_cast<char*>(load_file(fn, nullptr));
+ config_load(root, data);
+ // TODO: deliberate leak :-/
+}
+
+void config_free(cnode *root)
+{
+ cnode *cur = root->first_child;
+
+ while (cur) {
+ cnode *prev = cur;
+ config_free(cur);
+ cur = cur->next;
+ free(prev);
+ }
+}