diff options
Diffstat (limited to 'src/lib/json-tree.c')
-rw-r--r-- | src/lib/json-tree.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/lib/json-tree.c b/src/lib/json-tree.c new file mode 100644 index 0000000..cb721c2 --- /dev/null +++ b/src/lib/json-tree.c @@ -0,0 +1,173 @@ +/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "json-tree.h" + +struct json_tree { + pool_t pool; + struct json_tree_node *root, *cur, *cur_child; +}; + +struct json_tree * +json_tree_init_type(enum json_type container) +{ + struct json_tree *tree; + pool_t pool; + + pool = pool_alloconly_create("json tree", 1024); + tree = p_new(pool, struct json_tree, 1); + tree->pool = pool; + tree->root = tree->cur = p_new(tree->pool, struct json_tree_node, 1); + tree->cur->value_type = container == JSON_TYPE_ARRAY ? container : JSON_TYPE_OBJECT; + return tree; +} + +void json_tree_deinit(struct json_tree **_tree) +{ + struct json_tree *tree = *_tree; + + *_tree = NULL; + pool_unref(&tree->pool); +} + +static void +json_tree_append_child(struct json_tree *tree, enum json_type type, + const char *value) +{ + struct json_tree_node *node; + + node = p_new(tree->pool, struct json_tree_node, 1); + node->parent = tree->cur; + node->value_type = type; + node->value.str = p_strdup(tree->pool, value); + + if (tree->cur_child == NULL) + tree->cur->value.child = node; + else + tree->cur_child->next = node; + tree->cur_child = node; +} + +static void +json_tree_set_cur(struct json_tree *tree, struct json_tree_node *node) +{ + tree->cur = node; + tree->cur_child = tree->cur->value.child; + if (tree->cur_child != NULL) { + while (tree->cur_child->next != NULL) + tree->cur_child = tree->cur_child->next; + } +} + +static int +json_tree_append_value(struct json_tree *tree, enum json_type type, + const char *value) +{ + switch (tree->cur->value_type) { + case JSON_TYPE_OBJECT_KEY: + /* "key": value - we already added the node and set its key, + so now just set the value */ + tree->cur->value_type = type; + tree->cur->value.str = p_strdup(tree->pool, value); + json_tree_set_cur(tree, tree->cur->parent); + break; + case JSON_TYPE_ARRAY: + /* element in array - add a new node */ + json_tree_append_child(tree, type, value); + break; + default: + return -1; + } + return 0; +} + +int json_tree_append(struct json_tree *tree, enum json_type type, + const char *value) +{ + switch (type) { + case JSON_TYPE_OBJECT_KEY: + if (tree->cur->value_type != JSON_TYPE_OBJECT) + return -1; + json_tree_append_child(tree, type, NULL); + json_tree_set_cur(tree, tree->cur_child); + tree->cur->key = p_strdup(tree->pool, value); + break; + case JSON_TYPE_ARRAY: + if (json_tree_append_value(tree, type, NULL) < 0) + return -1; + json_tree_set_cur(tree, tree->cur_child); + break; + case JSON_TYPE_OBJECT: + if (tree->cur->value_type == JSON_TYPE_OBJECT_KEY) + tree->cur->value_type = JSON_TYPE_OBJECT; + else if (tree->cur->value_type == JSON_TYPE_ARRAY) { + json_tree_append_child(tree, type, NULL); + json_tree_set_cur(tree, tree->cur_child); + } else { + return -1; + } + break; + case JSON_TYPE_OBJECT_END: + if (tree->cur->parent == NULL || + tree->cur->value_type != JSON_TYPE_OBJECT) + return -1; + json_tree_set_cur(tree, tree->cur->parent); + break; + case JSON_TYPE_ARRAY_END: + if (tree->cur->parent == NULL || + tree->cur->value_type != JSON_TYPE_ARRAY) + return -1; + json_tree_set_cur(tree, tree->cur->parent); + break; + case JSON_TYPE_STRING: + case JSON_TYPE_NUMBER: + case JSON_TYPE_TRUE: + case JSON_TYPE_FALSE: + case JSON_TYPE_NULL: + if (json_tree_append_value(tree, type, value) < 0) + return -1; + break; + } + return 0; +} + +const struct json_tree_node * +json_tree_root(const struct json_tree *tree) +{ + return tree->root; +} + +const struct json_tree_node * +json_tree_find_key(const struct json_tree_node *node, const char *key) +{ + i_assert(node->value_type == JSON_TYPE_OBJECT); + + node = json_tree_get_child(node); + for (; node != NULL; node = node->next) { + if (node->key != NULL && strcmp(node->key, key) == 0) + return node; + } + return NULL; +} + +const struct json_tree_node * +json_tree_find_child_with(const struct json_tree_node *node, + const char *key, const char *value) +{ + const struct json_tree_node *child; + + i_assert(node->value_type == JSON_TYPE_OBJECT || + node->value_type == JSON_TYPE_ARRAY); + + for (node = json_tree_get_child(node); node != NULL; node = node->next) { + if (node->value_type != JSON_TYPE_OBJECT) + continue; + + child = json_tree_find_key(node, key); + if (child != NULL && + json_tree_get_value_str(child) != NULL && + strcmp(json_tree_get_value_str(child), value) == 0) + return node; + } + return NULL; +} |