summaryrefslogtreecommitdiffstats
path: root/pigeonhole/src/lib-sieve/sieve-ast.c
diff options
context:
space:
mode:
Diffstat (limited to 'pigeonhole/src/lib-sieve/sieve-ast.c')
-rw-r--r--pigeonhole/src/lib-sieve/sieve-ast.c1111
1 files changed, 1111 insertions, 0 deletions
diff --git a/pigeonhole/src/lib-sieve/sieve-ast.c b/pigeonhole/src/lib-sieve/sieve-ast.c
new file mode 100644
index 0000000..197dbad
--- /dev/null
+++ b/pigeonhole/src/lib-sieve/sieve-ast.c
@@ -0,0 +1,1111 @@
+/* Copyright (c) 2002-2018 Pigeonhole authors, see the included COPYING file
+ */
+
+#include "lib.h"
+#include "str.h"
+#include "mempool.h"
+#include "array.h"
+
+#include "sieve-common.h"
+#include "sieve-script.h"
+#include "sieve-extensions.h"
+
+#include "sieve-ast.h"
+
+#include <stdio.h>
+
+/*
+ * Forward declarations
+ */
+
+static struct sieve_ast_node *
+sieve_ast_node_create(struct sieve_ast *ast, struct sieve_ast_node *parent,
+ enum sieve_ast_type type, unsigned int source_line);
+
+/*
+ * Types
+ */
+
+/* Extensions to the AST */
+
+struct sieve_ast_extension_reg {
+ const struct sieve_extension *ext;
+ const struct sieve_ast_extension *ast_ext;
+ void *context;
+
+ bool required:1;
+};
+
+/*
+ * AST object
+ */
+
+struct sieve_ast {
+ pool_t pool;
+ int refcount;
+
+ struct sieve_instance *svinst;
+
+ struct sieve_script *script;
+
+ struct sieve_ast_node *root;
+
+ ARRAY(const struct sieve_extension *) linked_extensions;
+ ARRAY(struct sieve_ast_extension_reg) extensions;
+};
+
+struct sieve_ast *sieve_ast_create(struct sieve_script *script)
+{
+ pool_t pool;
+ struct sieve_ast *ast;
+ unsigned int ext_count;
+
+ pool = pool_alloconly_create("sieve_ast", 32768);
+ ast = p_new(pool, struct sieve_ast, 1);
+ ast->pool = pool;
+ ast->refcount = 1;
+
+ ast->script = script;
+ sieve_script_ref(script);
+ ast->svinst = sieve_script_svinst(script);
+
+ ast->root = sieve_ast_node_create(ast, NULL, SAT_ROOT, 0);
+ ast->root->identifier = "ROOT";
+
+ ext_count = sieve_extensions_get_count(ast->svinst);
+ p_array_init(&ast->linked_extensions, pool, ext_count);
+ p_array_init(&ast->extensions, pool, ext_count);
+
+ return ast;
+}
+
+void sieve_ast_ref(struct sieve_ast *ast)
+{
+ ast->refcount++;
+}
+
+void sieve_ast_unref(struct sieve_ast **ast)
+{
+ unsigned int i, ext_count;
+ const struct sieve_ast_extension_reg *extrs;
+
+ i_assert((*ast)->refcount > 0);
+
+ if (--(*ast)->refcount != 0)
+ return;
+
+ /* Release script reference */
+ sieve_script_unref(&(*ast)->script);
+
+ /* Signal registered extensions that the AST is being destroyed */
+ extrs = array_get(&(*ast)->extensions, &ext_count);
+ for (i = 0; i < ext_count; i++) {
+ if (extrs[i].ast_ext != NULL && extrs[i].ast_ext->free != NULL)
+ extrs[i].ast_ext->free(extrs[i].ext, *ast,
+ extrs[i].context);
+ }
+
+ /* Destroy AST */
+ pool_unref(&(*ast)->pool);
+
+ *ast = NULL;
+}
+
+struct sieve_ast_node *sieve_ast_root(struct sieve_ast *ast)
+{
+ return ast->root;
+}
+
+pool_t sieve_ast_pool(struct sieve_ast *ast)
+{
+ return ast->pool;
+}
+
+struct sieve_script *sieve_ast_script(struct sieve_ast *ast)
+{
+ return ast->script;
+}
+
+/*
+ * Extension support
+ */
+
+void sieve_ast_extension_link(struct sieve_ast *ast,
+ const struct sieve_extension *ext, bool required)
+{
+ unsigned int i, ext_count;
+ const struct sieve_extension *const *extensions;
+ struct sieve_ast_extension_reg *reg;
+
+ if (ext->id < 0)
+ return;
+
+ /* Initialize registration */
+ reg = array_idx_get_space(&ast->extensions, (unsigned int)ext->id);
+ i_assert(reg->ext == NULL || reg->ext == ext);
+ reg->ext = ext;
+ reg->required = reg->required || required;
+
+ /* Prevent duplicates */
+ extensions = array_get(&ast->linked_extensions, &ext_count);
+ for (i = 0; i < ext_count; i++) {
+ if (extensions[i] == ext)
+ return;
+ }
+
+ /* Add extension */
+ array_append(&ast->linked_extensions, &ext, 1);
+}
+
+const struct sieve_extension * const *
+sieve_ast_extensions_get(struct sieve_ast *ast, unsigned int *count_r)
+{
+ return array_get(&ast->linked_extensions, count_r);
+}
+
+void sieve_ast_extension_register(struct sieve_ast *ast,
+ const struct sieve_extension *ext,
+ const struct sieve_ast_extension *ast_ext,
+ void *context)
+{
+ struct sieve_ast_extension_reg *reg;
+
+ if (ext->id < 0)
+ return;
+
+ /* Initialize registration */
+ reg = array_idx_get_space(&ast->extensions, (unsigned int)ext->id);
+ i_assert(reg->ext == NULL || reg->ext == ext);
+ reg->ext = ext;
+ reg->ast_ext = ast_ext;
+ reg->context = context;
+}
+
+void sieve_ast_extension_set_context(struct sieve_ast *ast,
+ const struct sieve_extension *ext,
+ void *context)
+{
+ struct sieve_ast_extension_reg *reg;
+
+ if (ext->id < 0)
+ return;
+
+ reg = array_idx_get_space(&ast->extensions, (unsigned int)ext->id);
+ reg->context = context;
+}
+
+void *sieve_ast_extension_get_context(struct sieve_ast *ast,
+ const struct sieve_extension *ext)
+{
+ const struct sieve_ast_extension_reg *reg;
+
+ if (ext->id < 0 || ext->id >= (int)array_count(&ast->extensions))
+ return NULL;
+
+ reg = array_idx(&ast->extensions, (unsigned int)ext->id);
+
+ return reg->context;
+}
+
+bool sieve_ast_extension_is_required
+(struct sieve_ast *ast, const struct sieve_extension *ext)
+{
+ const struct sieve_ast_extension_reg *reg;
+
+ i_assert(ext->id >= 0 &&
+ ext->id < (int)array_count(&ast->extensions));
+
+ reg = array_idx(&ast->extensions, (unsigned int)ext->id);
+ return reg->required;
+}
+
+/*
+ * AST list implementations
+ */
+
+/* Very simplistic linked list implementation
+ FIXME: Merge with core
+ */
+#define __LIST_CREATE(pool, type) { \
+ type *list = p_new(pool, type, 1); \
+ list->head = NULL; \
+ list->tail = NULL; \
+ list->len = 0; \
+ return list; \
+ }
+
+#define __LIST_ADD(list, node) { \
+ if (list->len + 1 < list->len) \
+ return FALSE; \
+ \
+ node->next = NULL; \
+ if (list->head == NULL) { \
+ node->prev = NULL; \
+ list->head = node; \
+ list->tail = node; \
+ } else { \
+ list->tail->next = node; \
+ node->prev = list->tail; \
+ list->tail = node; \
+ } \
+ list->len++; \
+ node->list = list; \
+ return TRUE; \
+ }
+
+#define __LIST_INSERT(list, before, node) { \
+ if (list->len + 1 < list->len) \
+ return FALSE; \
+ \
+ node->next = before; \
+ if (list->head == before) { \
+ node->prev = NULL; \
+ list->head = node; \
+ } else { \
+ before->prev->next = node; \
+ } \
+ node->prev = before->prev; \
+ before->prev = node; \
+ list->len++; \
+ node->list = list; \
+ \
+ return TRUE; \
+ }
+
+#define __LIST_JOIN(list, node_type, items) { \
+ node_type *node; \
+ \
+ if (list->len + items->len < list->len) \
+ return FALSE; \
+ \
+ if (items->len == 0) \
+ return TRUE; \
+ \
+ if (list->head == NULL) { \
+ list->head = items->head; \
+ list->tail = items->tail; \
+ } else { \
+ list->tail->next = items->head; \
+ items->head->prev = list->tail; \
+ list->tail = items->tail; \
+ } \
+ list->len += items->len; \
+ \
+ node = items->head; \
+ while (node != NULL) { \
+ node->list = list; \
+ node = node->next; \
+ } \
+ return TRUE; \
+ }
+
+#define __LIST_DETACH(first, node_type, count) { \
+ node_type *last, *result; \
+ unsigned int left; \
+ \
+ i_assert(first->list != NULL); \
+ \
+ left = count - 1; \
+ last = first; \
+ while (left > 0 && last->next != NULL) { \
+ left--; \
+ last = last->next; \
+ } \
+ \
+ if (first->list->head == first) \
+ first->list->head = last->next; \
+ if (first->list->tail == last) \
+ first->list->tail = first->prev; \
+ \
+ if (first->prev != NULL) \
+ first->prev->next = last->next; \
+ if (last->next != NULL) \
+ last->next->prev = first->prev; \
+ \
+ first->list->len -= count - left; \
+ \
+ result = last->next; \
+ first->prev = NULL; \
+ last->next = NULL; \
+ \
+ return result; \
+ }
+
+/* List of AST nodes */
+
+static struct sieve_ast_list *
+sieve_ast_list_create(pool_t pool)
+ __LIST_CREATE(pool, struct sieve_ast_list)
+
+static bool
+sieve_ast_list_add(struct sieve_ast_list *list, struct sieve_ast_node *node)
+ __LIST_ADD(list, node)
+
+static struct sieve_ast_node *
+sieve_ast_list_detach(struct sieve_ast_node *first, unsigned int count)
+ __LIST_DETACH(first, struct sieve_ast_node, count)
+
+/* List of argument AST nodes */
+
+struct sieve_ast_arg_list *sieve_ast_arg_list_create(pool_t pool)
+ __LIST_CREATE(pool, struct sieve_ast_arg_list)
+
+bool sieve_ast_arg_list_add(struct sieve_ast_arg_list *list,
+ struct sieve_ast_argument *argument)
+ __LIST_ADD(list, argument)
+
+bool sieve_ast_arg_list_insert(struct sieve_ast_arg_list *list,
+ struct sieve_ast_argument *before,
+ struct sieve_ast_argument *argument)
+ __LIST_INSERT(list, before, argument)
+
+static bool
+sieve_ast_arg_list_join(struct sieve_ast_arg_list *list,
+ struct sieve_ast_arg_list *items)
+ __LIST_JOIN(list, struct sieve_ast_argument, items)
+
+static struct sieve_ast_argument *
+sieve_ast_arg_list_detach(struct sieve_ast_argument *first,
+ const unsigned int count)
+ __LIST_DETACH(first, struct sieve_ast_argument, count)
+
+void sieve_ast_arg_list_substitute(struct sieve_ast_arg_list *list,
+ struct sieve_ast_argument *argument,
+ struct sieve_ast_argument *replacement)
+{
+ if (list->head == argument)
+ list->head = replacement;
+ if (list->tail == argument)
+ list->tail = replacement;
+
+ if (argument->prev != NULL)
+ argument->prev->next = replacement;
+ if (argument->next != NULL)
+ argument->next->prev = replacement;
+
+ replacement->prev = argument->prev;
+ replacement->next = argument->next;
+ replacement->list = argument->list;
+
+ argument->next = NULL;
+ argument->prev = NULL;
+}
+
+/*
+ * AST node
+ */
+
+static struct sieve_ast_node *
+sieve_ast_node_create(struct sieve_ast *ast, struct sieve_ast_node *parent,
+ enum sieve_ast_type type, unsigned int source_line)
+{
+ struct sieve_ast_node *node =
+ p_new(ast->pool, struct sieve_ast_node, 1);
+
+ node->ast = ast;
+ node->parent = parent;
+ node->type = type;
+
+ node->prev = NULL;
+ node->next = NULL;
+
+ node->arguments = NULL;
+ node->tests = NULL;
+ node->commands = NULL;
+
+ node->test_list = FALSE;
+ node->block = FALSE;
+
+ node->source_line = source_line;
+
+ return node;
+}
+
+static bool
+sieve_ast_node_add_command(struct sieve_ast_node *node,
+ struct sieve_ast_node *command)
+{
+ i_assert(command->type == SAT_COMMAND &&
+ (node->type == SAT_ROOT || node->type == SAT_COMMAND));
+
+ if (node->commands == NULL)
+ node->commands = sieve_ast_list_create(node->ast->pool);
+
+ return sieve_ast_list_add(node->commands, command);
+}
+
+static bool
+sieve_ast_node_add_test(struct sieve_ast_node *node,
+ struct sieve_ast_node *test)
+{
+ i_assert(test->type == SAT_TEST &&
+ (node->type == SAT_TEST || node->type == SAT_COMMAND));
+
+ if (node->tests == NULL)
+ node->tests = sieve_ast_list_create(node->ast->pool);
+
+ return sieve_ast_list_add(node->tests, test);
+}
+
+static bool
+sieve_ast_node_add_argument(struct sieve_ast_node *node,
+ struct sieve_ast_argument *argument)
+{
+ i_assert(node->type == SAT_TEST || node->type == SAT_COMMAND);
+
+ if (node->arguments == NULL)
+ node->arguments = sieve_ast_arg_list_create(node->ast->pool);
+
+ return sieve_ast_arg_list_add(node->arguments, argument);
+}
+
+struct sieve_ast_node *sieve_ast_node_detach(struct sieve_ast_node *first)
+{
+ return sieve_ast_list_detach(first, 1);
+}
+
+const char *sieve_ast_type_name(enum sieve_ast_type ast_type)
+{
+ switch (ast_type) {
+ case SAT_NONE:
+ return "none";
+ case SAT_ROOT:
+ return "ast root node";
+ case SAT_COMMAND:
+ return "command";
+ case SAT_TEST:
+ return "test";
+ default:
+ return "??AST NODE??";
+ }
+}
+
+/*
+ * Argument AST node
+ */
+
+struct sieve_ast_argument *
+sieve_ast_argument_create(struct sieve_ast *ast, unsigned int source_line)
+{
+ struct sieve_ast_argument *arg =
+ p_new(ast->pool, struct sieve_ast_argument, 1);
+
+ arg->ast = ast;
+
+ arg->prev = NULL;
+ arg->next = NULL;
+
+ arg->source_line = source_line;
+
+ arg->argument = NULL;
+
+ return arg;
+}
+
+static void
+sieve_ast_argument_substitute(struct sieve_ast_argument *argument,
+ struct sieve_ast_argument *replacement)
+{
+ sieve_ast_arg_list_substitute(argument->list, argument, replacement);
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_string_create_raw(struct sieve_ast *ast, string_t *str,
+ unsigned int source_line)
+{
+ struct sieve_ast_argument *argument =
+ sieve_ast_argument_create(ast, source_line);
+
+ argument->type = SAAT_STRING;
+ argument->_value.str = str;
+
+ return argument;
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_string_create(struct sieve_ast_node *node,
+ const string_t *str, unsigned int source_line)
+{
+ struct sieve_ast_argument *argument;
+ string_t *newstr;
+
+ /* Allocate new internal string buffer */
+ newstr = str_new(node->ast->pool, str_len(str));
+
+ /* Clone string */
+ str_append_str(newstr, str);
+
+ /* Create string argument */
+ argument = sieve_ast_argument_string_create_raw(
+ node->ast, newstr, source_line);
+
+ /* Add argument to command/test node */
+ sieve_ast_node_add_argument(node, argument);
+
+ return argument;
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_cstring_create(struct sieve_ast_node *node, const char *str,
+ unsigned int source_line)
+{
+ struct sieve_ast_argument *argument;
+ string_t *newstr;
+
+ /* Allocate new internal string buffer */
+ newstr = str_new(node->ast->pool, strlen(str));
+
+ /* Clone string */
+ str_append(newstr, str);
+
+ /* Create string argument */
+ argument = sieve_ast_argument_string_create_raw(
+ node->ast, newstr, source_line);
+
+ /* Add argument to command/test node */
+ sieve_ast_node_add_argument(node, argument);
+
+ return argument;
+}
+
+void sieve_ast_argument_string_set(struct sieve_ast_argument *argument,
+ string_t *newstr)
+{
+ i_assert(argument->type == SAAT_STRING);
+ argument->_value.str = newstr;
+}
+
+void sieve_ast_argument_string_setc(struct sieve_ast_argument *argument,
+ const char *newstr)
+{
+ i_assert(argument->type == SAAT_STRING);
+
+ str_truncate(argument->_value.str, 0);
+ str_append(argument->_value.str, newstr);
+}
+
+void sieve_ast_argument_number_substitute(struct sieve_ast_argument *argument,
+ sieve_number_t number)
+{
+ argument->type = SAAT_NUMBER;
+ argument->_value.number = number;
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_stringlist_create(struct sieve_ast_node *node,
+ unsigned int source_line)
+{
+ struct sieve_ast_argument *argument =
+ sieve_ast_argument_create(node->ast, source_line);
+
+ argument->type = SAAT_STRING_LIST;
+ argument->_value.strlist = NULL;
+
+ sieve_ast_node_add_argument(node, argument);
+
+ return argument;
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_stringlist_substitute(struct sieve_ast_node *node,
+ struct sieve_ast_argument *arg)
+{
+ struct sieve_ast_argument *argument =
+ sieve_ast_argument_create(node->ast, arg->source_line);
+
+ argument->type = SAAT_STRING_LIST;
+ argument->_value.strlist = NULL;
+
+ sieve_ast_argument_substitute(arg, argument);
+
+ return argument;
+}
+
+static inline bool
+_sieve_ast_stringlist_add_item(struct sieve_ast_argument *list,
+ struct sieve_ast_argument *item)
+{
+ i_assert(list->type == SAAT_STRING_LIST);
+
+ if (list->_value.strlist == NULL) {
+ list->_value.strlist =
+ sieve_ast_arg_list_create(list->ast->pool);
+ }
+
+ return sieve_ast_arg_list_add(list->_value.strlist, item);
+}
+
+static bool
+sieve_ast_stringlist_add_stringlist(struct sieve_ast_argument *list,
+ struct sieve_ast_argument *items)
+{
+ i_assert(list->type == SAAT_STRING_LIST);
+ i_assert(items->type == SAAT_STRING_LIST);
+
+ if (list->_value.strlist == NULL) {
+ list->_value.strlist =
+ sieve_ast_arg_list_create(list->ast->pool);
+ }
+
+ return sieve_ast_arg_list_join(list->_value.strlist,
+ items->_value.strlist);
+}
+
+static bool
+_sieve_ast_stringlist_add_str(struct sieve_ast_argument *list, string_t *str,
+ unsigned int source_line)
+{
+ struct sieve_ast_argument *stritem;
+
+ stritem = sieve_ast_argument_create(list->ast, source_line);
+ stritem->type = SAAT_STRING;
+ stritem->_value.str = str;
+
+ return _sieve_ast_stringlist_add_item(list, stritem);
+}
+
+bool sieve_ast_stringlist_add(struct sieve_ast_argument *list,
+ const string_t *str, unsigned int source_line)
+{
+ string_t *copied_str = str_new(list->ast->pool, str_len(str));
+ str_append_str(copied_str, str);
+
+ return _sieve_ast_stringlist_add_str(list, copied_str, source_line);
+}
+
+bool sieve_ast_stringlist_add_strc(struct sieve_ast_argument *list,
+ const char *str, unsigned int source_line)
+{
+ string_t *copied_str = str_new(list->ast->pool, strlen(str));
+ str_append(copied_str, str);
+
+ return _sieve_ast_stringlist_add_str(list, copied_str, source_line);
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_tag_create(struct sieve_ast_node *node, const char *tag,
+ unsigned int source_line)
+{
+ struct sieve_ast_argument *argument =
+ sieve_ast_argument_create(node->ast, source_line);
+
+ argument->type = SAAT_TAG;
+ argument->_value.tag = p_strdup(node->ast->pool, tag);
+
+ if (!sieve_ast_node_add_argument(node, argument))
+ return NULL;
+ return argument;
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_tag_insert(struct sieve_ast_argument *before,
+ const char *tag, unsigned int source_line)
+{
+ struct sieve_ast_argument *argument =
+ sieve_ast_argument_create(before->ast, source_line);
+
+ argument->type = SAAT_TAG;
+ argument->_value.tag = p_strdup(before->ast->pool, tag);
+
+ if (!sieve_ast_arg_list_insert(before->list, before, argument))
+ return NULL;
+ return argument;
+}
+
+struct sieve_ast_argument *
+sieve_ast_argument_number_create(struct sieve_ast_node *node,
+ sieve_number_t number,
+ unsigned int source_line)
+{
+ struct sieve_ast_argument *argument =
+ sieve_ast_argument_create(node->ast, source_line);
+
+ argument->type = SAAT_NUMBER;
+ argument->_value.number = number;
+
+ if (!sieve_ast_node_add_argument(node, argument))
+ return NULL;
+ return argument;
+}
+
+void sieve_ast_argument_number_set(struct sieve_ast_argument *argument,
+ sieve_number_t newnum)
+{
+ i_assert(argument->type == SAAT_NUMBER);
+ argument->_value.number = newnum;
+}
+
+struct sieve_ast_argument *
+sieve_ast_arguments_detach(struct sieve_ast_argument *first,
+ unsigned int count)
+{
+ return sieve_ast_arg_list_detach(first, count);
+}
+
+bool sieve_ast_argument_attach(struct sieve_ast_node *node,
+ struct sieve_ast_argument *argument)
+{
+ return sieve_ast_node_add_argument(node, argument);
+}
+
+const char *sieve_ast_argument_type_name(enum sieve_ast_argument_type arg_type)
+{
+ switch (arg_type) {
+ case SAAT_NONE:
+ return "none";
+ case SAAT_STRING_LIST:
+ return "a string list";
+ case SAAT_STRING:
+ return "a string";
+ case SAAT_NUMBER:
+ return "a number";
+ case SAAT_TAG:
+ return "a tag";
+ default:
+ return "??ARGUMENT??";
+ }
+}
+
+/* Test AST node */
+
+struct sieve_ast_node *
+sieve_ast_test_create(struct sieve_ast_node *parent, const char *identifier,
+ unsigned int source_line)
+{
+ struct sieve_ast_node *test = sieve_ast_node_create(
+ parent->ast, parent, SAT_TEST, source_line);
+
+ test->identifier = p_strdup(parent->ast->pool, identifier);
+
+ if (!sieve_ast_node_add_test(parent, test))
+ return NULL;
+ return test;
+}
+
+/* Command AST node */
+
+struct sieve_ast_node *
+sieve_ast_command_create(struct sieve_ast_node *parent, const char *identifier,
+ unsigned int source_line)
+{
+ struct sieve_ast_node *command = sieve_ast_node_create(
+ parent->ast, parent, SAT_COMMAND, source_line);
+
+ command->identifier = p_strdup(parent->ast->pool, identifier);
+
+ if (!sieve_ast_node_add_command(parent, command))
+ return NULL;
+ return command;
+}
+
+/*
+ * Utility
+ */
+
+int sieve_ast_stringlist_map(
+ struct sieve_ast_argument **listitem, void *context,
+ int (*map_function)(void *context, struct sieve_ast_argument *arg))
+{
+ if (sieve_ast_argument_type(*listitem) == SAAT_STRING) {
+ /* Single string */
+ return map_function(context, *listitem);
+ } else if (sieve_ast_argument_type(*listitem) == SAAT_STRING_LIST) {
+ int ret = 0;
+
+ /* String list */
+ *listitem = sieve_ast_strlist_first(*listitem);
+
+ while (*listitem != NULL) {
+ if ((ret = map_function(context, *listitem)) <= 0)
+ return ret;
+
+ *listitem = sieve_ast_strlist_next(*listitem);
+ }
+
+ return ret;
+ }
+
+ i_unreached();
+ return -1;
+}
+
+struct sieve_ast_argument *
+sieve_ast_stringlist_join(struct sieve_ast_argument *list,
+ struct sieve_ast_argument *items)
+{
+ enum sieve_ast_argument_type list_type, items_type;
+ struct sieve_ast_argument *newlist;
+
+ list_type = sieve_ast_argument_type(list);
+ items_type = sieve_ast_argument_type(items);
+
+ switch (list_type) {
+ case SAAT_STRING:
+ switch (items_type) {
+ case SAAT_STRING:
+ newlist = sieve_ast_argument_create(
+ list->ast, list->source_line);
+ newlist->type = SAAT_STRING_LIST;
+ newlist->_value.strlist = NULL;
+
+ sieve_ast_argument_substitute(list, newlist);
+ sieve_ast_arguments_detach(items, 1);
+
+ if (!_sieve_ast_stringlist_add_item(newlist, list) ||
+ !_sieve_ast_stringlist_add_item(newlist, items))
+ return NULL;
+ return newlist;
+ case SAAT_STRING_LIST:
+ /* Adding stringlist to string; make them swith places
+ and add one to the other.
+ */
+ sieve_ast_arguments_detach(items, 1);
+ sieve_ast_argument_substitute(list, items);
+ if (!_sieve_ast_stringlist_add_item(items, list))
+ return NULL;
+ return list;
+ default:
+ i_unreached();
+ }
+ break;
+ case SAAT_STRING_LIST:
+ switch (items_type) {
+ case SAAT_STRING:
+ /* Adding string to stringlist; straightforward add */
+ sieve_ast_arguments_detach(items, 1);
+ if (!_sieve_ast_stringlist_add_item(list, items))
+ return NULL;
+ return list;
+ case SAAT_STRING_LIST:
+ /* Adding stringlist to stringlist; perform actual join
+ */
+ sieve_ast_arguments_detach(items, 1);
+ if (!sieve_ast_stringlist_add_stringlist(list, items))
+ return NULL;
+ return list;
+ default:
+ i_unreached();
+ }
+ break;
+ default:
+ i_unreached();
+ }
+ return NULL;
+}
+
+/* Debug */
+
+/* Unparsing, currently implemented using plain printf()s */
+
+static void sieve_ast_unparse_string(const string_t *strval)
+{
+ char *str = t_strdup_noconst(str_c((string_t *)strval));
+
+ if (strchr(str, '\n') != NULL && str[strlen(str)-1] == '\n') {
+ /* Print it as a multi-line string and do required dotstuffing
+ */
+ char *spos = str;
+ char *epos = strchr(str, '\n');
+ printf("text:\n");
+
+ while (epos != NULL) {
+ *epos = '\0';
+ if (*spos == '.')
+ printf(".");
+
+ printf("%s\n", spos);
+
+ spos = epos+1;
+ epos = strchr(spos, '\n');
+ }
+ if (*spos == '.')
+ printf(".");
+
+ printf("%s\n.\n", spos);
+ } else {
+ /* Print it as a quoted string and escape " */
+ char *spos = str;
+ char *epos = strchr(str, '"');
+ printf("\"");
+
+ while (epos != NULL) {
+ *epos = '\0';
+ printf("%s\\\"", spos);
+
+ spos = epos+1;
+ epos = strchr(spos, '"');
+ }
+
+ printf("%s\"", spos);
+ }
+}
+
+static void
+sieve_ast_unparse_argument(struct sieve_ast_argument *argument, int level);
+
+static void
+sieve_ast_unparse_stringlist(struct sieve_ast_argument *strlist, int level)
+{
+ struct sieve_ast_argument *stritem;
+
+ if (sieve_ast_strlist_count(strlist) > 1) {
+ int i;
+
+ printf("[\n");
+
+ /* Create indent */
+ for (i = 0; i < level+2; i++)
+ printf(" ");
+
+ stritem = sieve_ast_strlist_first(strlist);
+ if (stritem != NULL) {
+ sieve_ast_unparse_string(
+ sieve_ast_strlist_str(stritem));
+
+ stritem = sieve_ast_strlist_next(stritem);
+ while (stritem != NULL) {
+ printf(",\n");
+ for (i = 0; i < level+2; i++)
+ printf(" ");
+ sieve_ast_unparse_string(
+ sieve_ast_strlist_str(stritem));
+ stritem = sieve_ast_strlist_next(stritem);
+ }
+ }
+
+ printf(" ]");
+ } else {
+ stritem = sieve_ast_strlist_first(strlist);
+ if (stritem != NULL) {
+ sieve_ast_unparse_string(
+ sieve_ast_strlist_str(stritem));
+ }
+ }
+}
+
+static void
+sieve_ast_unparse_argument(struct sieve_ast_argument *argument, int level)
+{
+ switch (argument->type) {
+ case SAAT_STRING:
+ sieve_ast_unparse_string(sieve_ast_argument_str(argument));
+ break;
+ case SAAT_STRING_LIST:
+ sieve_ast_unparse_stringlist(argument, level+1);
+ break;
+ case SAAT_NUMBER:
+ printf("%"SIEVE_PRI_NUMBER,
+ sieve_ast_argument_number(argument));
+ break;
+ case SAAT_TAG:
+ printf(":%s", sieve_ast_argument_tag(argument));
+ break;
+ default:
+ printf("??ARGUMENT??");
+ break;
+ }
+}
+
+static void sieve_ast_unparse_test(struct sieve_ast_node *node, int level);
+
+static void sieve_ast_unparse_tests(struct sieve_ast_node *node, int level)
+{
+ struct sieve_ast_node *test;
+
+ if (sieve_ast_test_count(node) > 1) {
+ int i;
+
+ printf(" (\n");
+
+ /* Create indent */
+ for (i = 0; i < level+2; i++)
+ printf(" ");
+
+ test = sieve_ast_test_first(node);
+ sieve_ast_unparse_test(test, level+1);
+
+ test = sieve_ast_test_next(test);
+ while (test != NULL) {
+ printf(", \n");
+ for (i = 0; i < level+2; i++)
+ printf(" ");
+ sieve_ast_unparse_test(test, level+1);
+ test = sieve_ast_test_next(test);
+ }
+
+ printf(" )");
+ } else {
+ test = sieve_ast_test_first(node);
+ if (test != NULL)
+ sieve_ast_unparse_test(test, level);
+ }
+}
+
+static void sieve_ast_unparse_test(struct sieve_ast_node *node, int level)
+{
+ struct sieve_ast_argument *argument;
+
+ printf(" %s", node->identifier);
+
+ argument = sieve_ast_argument_first(node);
+ while (argument != NULL) {
+ printf(" ");
+ sieve_ast_unparse_argument(argument, level);
+ argument = sieve_ast_argument_next(argument);
+ }
+
+ sieve_ast_unparse_tests(node, level);
+}
+
+static void sieve_ast_unparse_command(struct sieve_ast_node *node, int level)
+{
+ struct sieve_ast_node *command;
+ struct sieve_ast_argument *argument;
+
+ int i;
+
+ /* Create indent */
+ for (i = 0; i < level; i++)
+ printf(" ");
+
+ printf("%s", node->identifier);
+
+ argument = sieve_ast_argument_first(node);
+ while (argument != NULL) {
+ printf(" ");
+ sieve_ast_unparse_argument(argument, level);
+ argument = sieve_ast_argument_next(argument);
+ }
+
+ sieve_ast_unparse_tests(node, level);
+
+ command = sieve_ast_command_first(node);
+ if (command != NULL) {
+ printf(" {\n");
+
+ while (command != NULL) {
+ sieve_ast_unparse_command(command, level+1);
+ command = sieve_ast_command_next(command);
+ }
+
+ for (i = 0; i < level; i++)
+ printf(" ");
+ printf("}\n");
+ } else
+ printf(";\n");
+}
+
+void sieve_ast_unparse(struct sieve_ast *ast)
+{
+ struct sieve_ast_node *command;
+
+ printf("Unparsing Abstract Syntax Tree:\n");
+
+ T_BEGIN {
+ command = sieve_ast_command_first(sieve_ast_root(ast));
+ while (command != NULL) {
+ sieve_ast_unparse_command(command, 0);
+ command = sieve_ast_command_next(command);
+ }
+ } T_END;
+}