summaryrefslogtreecommitdiffstats
path: root/src/lib/json-tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/json-tree.c')
-rw-r--r--src/lib/json-tree.c173
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;
+}