summaryrefslogtreecommitdiff
path: root/libmu_sieve/sieve-gram.y
diff options
context:
space:
mode:
Diffstat (limited to 'libmu_sieve/sieve-gram.y')
-rw-r--r--libmu_sieve/sieve-gram.y1598
1 files changed, 1598 insertions, 0 deletions
diff --git a/libmu_sieve/sieve-gram.y b/libmu_sieve/sieve-gram.y
new file mode 100644
index 000000000..38a85b269
--- /dev/null
+++ b/libmu_sieve/sieve-gram.y
@@ -0,0 +1,1598 @@
+%{
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999-2002, 2005-2012, 2014-2017 Free Software
+ Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library. If not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sieve-priv.h>
+#include <mailutils/stdstream.h>
+
+mu_sieve_machine_t mu_sieve_machine;
+int mu_sieve_error_count;
+static struct mu_sieve_node *sieve_tree;
+
+static struct mu_sieve_node *node_alloc (enum mu_sieve_node_type,
+ struct mu_locus_range *);
+
+static void node_list_add (struct mu_sieve_node_list *list,
+ struct mu_sieve_node *node);
+
+%}
+
+%error-verbose
+%locations
+
+%union {
+ char *string;
+ size_t number;
+ size_t idx;
+ struct mu_sieve_slice slice;
+ struct
+ {
+ char *ident;
+ struct mu_locus_range idloc;
+ size_t first;
+ size_t count;
+ } command;
+ struct mu_sieve_node_list node_list;
+ struct mu_sieve_node *node;
+}
+
+%token <string> IDENT TAG
+%token <number> NUMBER
+%token <string> STRING MULTILINE
+%token REQUIRE IF ELSIF ELSE ANYOF ALLOF NOT FALSE TRUE
+
+%type <idx> arg
+%type <slice> arglist maybe_arglist slist stringlist stringorlist
+%type <command> command
+%type <node> action test statement block cond
+%type <node> else_part
+%type <node_list> list testlist elsif_branch maybe_elsif
+
+%%
+
+input : /* empty */
+ {
+ sieve_tree = NULL;
+ }
+ | list
+ {
+ struct mu_locus_range lr;
+
+ lr.beg = lr.end = @1.end;
+
+ node_list_add (&$1, node_alloc (mu_sieve_node_end, &lr));
+ sieve_tree = $1.head;
+ }
+ ;
+
+list : statement
+ {
+ $$.head = $$.tail = $1;
+ }
+ | list statement
+ {
+ node_list_add (&$1, $2);
+ $$ = $1;
+ }
+ ;
+
+statement : REQUIRE stringorlist ';'
+ {
+ mu_sieve_require (mu_sieve_machine, &$2);
+ /* Reclaim string slots. The string data referred to by
+ $2 are registered in memory_pool, so we don't free them */
+ mu_sieve_machine->stringcount -= $2.count;
+ $$ = NULL;
+ }
+ | action ';'
+ /* 1 2 3 4 */
+ | IF cond block else_part
+ {
+ $$ = node_alloc (mu_sieve_node_cond, &@1);
+ $$->v.cond.expr = $2;
+ $$->v.cond.iftrue = $3;
+ $$->v.cond.iffalse = $4;
+ }
+ ;
+
+else_part : maybe_elsif
+ {
+ $$ = $1.head;
+ }
+ | maybe_elsif ELSE block
+ {
+ if ($1.tail)
+ {
+ $1.tail->v.cond.iffalse = $3;
+ $$ = $1.head;
+ }
+ else
+ $$ = $3;
+ }
+ ;
+
+maybe_elsif : /* empty */
+ {
+ $$.head = $$.tail = NULL;
+ }
+ | elsif_branch
+ ;
+
+/* elsif branches form a singly-linked version of node_list. Nodes in
+ this list are linked by v.cond.iffalse pointer */
+elsif_branch : ELSIF cond block
+ {
+ struct mu_sieve_node *node =
+ node_alloc (mu_sieve_node_cond, &@1);
+ node->v.cond.expr = $2;
+ node->v.cond.iftrue = $3;
+ node->v.cond.iffalse = NULL;
+ $$.head = $$.tail = node;
+ }
+ | elsif_branch ELSIF cond block
+ {
+ struct mu_sieve_node *node =
+ node_alloc (mu_sieve_node_cond, &@2);
+ node->v.cond.expr = $3;
+ node->v.cond.iftrue = $4;
+ node->v.cond.iffalse = NULL;
+
+ $1.tail->v.cond.iffalse = node;
+ $1.tail = node;
+
+ $$ = $1;
+ }
+ ;
+
+block : '{' list '}'
+ {
+ $$ = $2.head;
+ }
+ ;
+
+testlist : cond
+ {
+ $$.head = $$.tail = $1;
+ }
+ | testlist ',' cond
+ {
+ $3->prev = $1.tail;
+ $1.tail->next = $3;
+ $1.tail = $3;
+ }
+ ;
+
+cond : test
+ | ANYOF '(' testlist ')'
+ {
+ $$ = node_alloc (mu_sieve_node_anyof, &@1);
+ $$->v.node = $3.head;
+ }
+ | ALLOF '(' testlist ')'
+ {
+ $$ = node_alloc (mu_sieve_node_allof, &@1);
+ $$->v.node = $3.head;
+ }
+ | NOT cond
+ {
+ $$ = node_alloc (mu_sieve_node_not, &@1);
+ $$->v.node = $2;
+ }
+ ;
+
+test : command
+ {
+ mu_sieve_registry_t *reg;
+
+ mu_locus_range_copy (&mu_sieve_machine->locus, &@1);
+ reg = mu_sieve_registry_lookup (mu_sieve_machine, $1.ident,
+ mu_sieve_record_test);
+ if (!reg)
+ {
+ mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc,
+ _("unknown test: %s"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
+ else if (!reg->required)
+ {
+ mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc,
+ _("test `%s' has not been required"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
+
+ $$ = node_alloc (mu_sieve_node_test, &@1);
+ $$->v.command.reg = reg;
+ $$->v.command.argstart = $1.first;
+ $$->v.command.argcount = $1.count;
+ $$->v.command.tagcount = 0;
+ $$->v.command.comparator = NULL;
+ mu_i_sv_lint_command (mu_sieve_machine, $$);
+ }
+ | TRUE
+ {
+ $$ = node_alloc (mu_sieve_node_true, &@1);
+ }
+ | FALSE
+ {
+ $$ = node_alloc (mu_sieve_node_false, &@1);
+ }
+ ;
+
+command : IDENT maybe_arglist
+ {
+ $$.ident = $1;
+ $$.idloc = @1;
+ $$.first = $2.first;
+ $$.count = $2.count;
+ }
+ ;
+
+action : command
+ {
+ mu_sieve_registry_t *reg;
+
+ mu_locus_range_copy (&mu_sieve_machine->locus, &@1);
+ reg = mu_sieve_registry_lookup (mu_sieve_machine, $1.ident,
+ mu_sieve_record_action);
+
+ if (!reg)
+ {
+ mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc,
+ _("unknown action: %s"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
+ else if (!reg->required)
+ {
+ mu_diag_at_locus_range (MU_LOG_ERROR, &$1.idloc,
+ _("action `%s' has not been required"),
+ $1.ident);
+ mu_i_sv_error (mu_sieve_machine);
+ }
+
+ $$ = node_alloc(mu_sieve_node_action, &@1);
+ $$->v.command.reg = reg;
+ $$->v.command.argstart = $1.first;
+ $$->v.command.argcount = $1.count;
+ $$->v.command.tagcount = 0;
+ mu_i_sv_lint_command (mu_sieve_machine, $$);
+ }
+ ;
+
+maybe_arglist: /* empty */
+ {
+ $$.first = 0;
+ $$.count = 0;
+ }
+ | arglist
+ ;
+
+arglist : arg
+ {
+ $$.first = $1;
+ $$.count = 1;
+ }
+ | arglist arg
+ {
+ $1.count++;
+ $$ = $1;
+ }
+ ;
+
+arg : stringlist
+ {
+ $$ = mu_sieve_value_create (mu_sieve_machine,
+ SVT_STRING_LIST, &@1, &$1);
+ }
+ | STRING
+ {
+ $$ = mu_sieve_value_create (mu_sieve_machine, SVT_STRING,
+ &@1, $1);
+ }
+ | MULTILINE
+ {
+ $$ = mu_sieve_value_create (mu_sieve_machine, SVT_STRING,
+ &@1, $1);
+ }
+ | NUMBER
+ {
+ $$ = mu_sieve_value_create (mu_sieve_machine, SVT_NUMBER,
+ &@1, &$1);
+ }
+ | TAG
+ {
+ $$ = mu_sieve_value_create (mu_sieve_machine, SVT_TAG,
+ &@1, $1);
+ }
+ ;
+
+stringorlist : STRING
+ {
+ $$.first = mu_i_sv_string_create (mu_sieve_machine, $1);
+ $$.count = 1;
+ }
+ | stringlist
+ ;
+
+stringlist : '[' slist ']'
+ {
+ $$ = $2;
+ }
+ ;
+
+slist : STRING
+ {
+ $$.first = mu_i_sv_string_create (mu_sieve_machine, $1);
+ $$.count = 1;
+ }
+ | slist ',' STRING
+ {
+ mu_i_sv_string_create (mu_sieve_machine, $3);
+ $1.count++;
+ $$ = $1;
+ }
+ ;
+
+%%
+
+int
+yyerror (const char *s)
+{
+ mu_error ("%s", s);
+ mu_i_sv_error (mu_sieve_machine);
+ return 0;
+}
+
+static void
+node_list_add (struct mu_sieve_node_list *list, struct mu_sieve_node *node)
+{
+ if (!node)
+ return;
+
+ node->prev = list->tail;
+ if (list->tail)
+ list->tail->next = node;
+ else
+ list->head = node;
+ list->tail = node;
+}
+
+static struct mu_sieve_node *
+node_alloc (enum mu_sieve_node_type type, struct mu_locus_range *lr)
+{
+ struct mu_sieve_node *node = malloc (sizeof (*node));
+ if (node)
+ {
+ node->prev = node->next = NULL;
+ node->type = type;
+ mu_locus_range_init (&node->locus);
+ mu_locus_range_copy (&node->locus, lr);
+ }
+ return node;
+}
+
+static void node_optimize (struct mu_sieve_node *node);
+static void node_free (struct mu_sieve_node *node);
+static void node_replace (struct mu_sieve_node *node,
+ struct mu_sieve_node *repl);
+static void node_code (struct mu_sieve_machine *mach,
+ struct mu_sieve_node *node);
+static void node_dump (mu_stream_t str, struct mu_sieve_node *node,
+ unsigned level, struct mu_sieve_machine *mach);
+
+static void tree_free (struct mu_sieve_node **tree);
+static void tree_optimize (struct mu_sieve_node *tree);
+static void tree_code (struct mu_sieve_machine *mach,
+ struct mu_sieve_node *tree);
+static void tree_dump (mu_stream_t str,
+ struct mu_sieve_node *tree, unsigned level,
+ struct mu_sieve_machine *mach);
+
+static void
+indent (mu_stream_t str, unsigned level)
+{
+#define tab " "
+#define tablen (sizeof (tab) - 1)
+ while (level--)
+ mu_stream_write (str, tab, tablen, NULL);
+}
+
+/* mu_sieve_node_noop */
+static void
+dump_node_noop (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "NOOP\n");
+}
+
+/* mu_sieve_node_false */
+static void
+dump_node_false (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "FALSE\n");
+}
+
+/* mu_sieve_node_true */
+static void
+dump_node_true (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "TRUE\n");
+}
+
+/* mu_sieve_node_test & mu_sieve_node_action */
+static void
+free_node_command (struct mu_sieve_node *node)
+{
+ /* nothing */
+}
+
+static void
+code_node_test (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ mu_i_sv_code_test (mach, node);
+}
+
+static void
+code_node_action (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ mu_i_sv_code_action (mach, node);
+}
+
+void
+mu_i_sv_valf (mu_sieve_machine_t mach, mu_stream_t str, mu_sieve_value_t *val)
+{
+ mu_stream_printf (str, " ");
+ if (val->tag)
+ {
+ mu_stream_printf (str, ":%s", val->tag);
+ if (val->type == SVT_VOID)
+ return;
+ mu_stream_printf (str, " ");
+ }
+ switch (val->type)
+ {
+ case SVT_VOID:
+ mu_stream_printf (str, "(void)");
+ break;
+
+ case SVT_NUMBER:
+ mu_stream_printf (str, "%zu", val->v.number);
+ break;
+
+ case SVT_STRING:
+ mu_stream_printf (str, "\"%s\"",
+ mu_sieve_string_raw (mach, &val->v.list, 0)->orig);
+ break;
+
+ case SVT_STRING_LIST:
+ {
+ size_t i;
+
+ mu_stream_printf (str, "[");
+ for (i = 0; i < val->v.list.count; i++)
+ {
+ if (i)
+ mu_stream_printf (str, ", ");
+ mu_stream_printf (str, "\"%s\"",
+ mu_sieve_string_raw (mach, &val->v.list, i)->orig);
+ }
+ mu_stream_printf (str, "]");
+ }
+ break;
+
+ case SVT_TAG:
+ mu_stream_printf (str, ":%s", val->v.string);
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+static void
+dump_node_command (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ size_t i;
+
+ indent (str, level);
+ mu_stream_printf (str, "COMMAND %s", node->v.command.reg->name);
+ for (i = 0; i < node->v.command.argcount + node->v.command.tagcount; i++)
+ mu_i_sv_valf (mach, str, &mach->valspace[node->v.command.argstart + i]);
+ mu_stream_printf (str, "\n");
+}
+
+/* mu_sieve_node_cond */
+static void
+free_node_cond (struct mu_sieve_node *node)
+{
+ tree_free (&node->v.cond.expr);
+ tree_free (&node->v.cond.iftrue);
+ tree_free (&node->v.cond.iffalse);
+}
+
+static void
+optimize_node_cond (struct mu_sieve_node *node)
+{
+ tree_optimize (node->v.cond.expr);
+ switch (node->v.cond.expr->type)
+ {
+ case mu_sieve_node_true:
+ tree_optimize (node->v.cond.iftrue);
+ node_replace (node, node->v.cond.iftrue);
+ break;
+
+ case mu_sieve_node_false:
+ tree_optimize (node->v.cond.iffalse);
+ node_replace (node, node->v.cond.iffalse);
+ break;
+
+ default:
+ tree_optimize (node->v.cond.iftrue);
+ tree_optimize (node->v.cond.iffalse);
+ }
+}
+
+static void
+code_node_cond (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ size_t br1;
+
+ tree_code (mach, node->v.cond.expr);
+ mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_brz);
+ br1 = mach->pc;
+ mu_i_sv_code (mach, (sieve_op_t) 0);
+ tree_code (mach, node->v.cond.iftrue);
+
+ if (node->v.cond.iffalse)
+ {
+ size_t br2;
+
+ mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_branch);
+ br2 = mach->pc;
+ mu_i_sv_code (mach, (sieve_op_t) 0);
+
+ mach->prog[br1].pc = mach->pc - br1 - 1;
+
+ tree_code (mach, node->v.cond.iffalse);
+ mach->prog[br2].pc = mach->pc - br2 - 1;
+ }
+ else
+ mach->prog[br1].pc = mach->pc - br1 - 1;
+}
+
+static void
+dump_node_cond (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "COND\n");
+
+ ++level;
+
+ indent (str, level);
+ mu_stream_printf (str, "EXPR:\n");
+ tree_dump (str, node->v.cond.expr, level + 1, mach);
+
+ indent (str, level);
+ mu_stream_printf (str, "IFTRUE:\n");
+ tree_dump (str, node->v.cond.iftrue, level + 1, mach);
+
+ indent (str, level);
+ mu_stream_printf (str, "IFFALSE:\n");
+ tree_dump (str, node->v.cond.iffalse, level + 1, mach);
+}
+
+/* mu_sieve_node_anyof & mu_sieve_node_allof */
+static void
+free_node_x_of (struct mu_sieve_node *node)
+{
+ tree_free (&node->v.node);
+}
+
+static void
+optimize_x_of (struct mu_sieve_node *node, enum mu_sieve_node_type solve)
+{
+ struct mu_sieve_node *cur;
+ tree_optimize (node->v.node);
+ cur = node->v.node;
+ while (cur)
+ {
+ struct mu_sieve_node *next = cur->next;
+ switch (cur->type)
+ {
+ case mu_sieve_node_false:
+ case mu_sieve_node_true:
+ if (cur->type == solve)
+ {
+ tree_free (&node->v.node);
+ node->type = solve;
+ return;
+ }
+ else
+ {
+ if (cur->prev)
+ cur->prev->next = next;
+ else
+ node->v.node = next;
+ if (next)
+ next->prev = cur->prev;
+ node_free (cur);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ cur = next;
+ }
+
+ if (!node->v.node)
+ node->type = solve == mu_sieve_node_false ? mu_sieve_node_true : mu_sieve_node_false;
+}
+
+static void
+code_node_x_of (struct mu_sieve_machine *mach, struct mu_sieve_node *node,
+ sieve_op_t op)
+{
+ struct mu_sieve_node *cur = node->v.node;
+ size_t pc = 0;
+ size_t end;
+
+ while (cur)
+ {
+ node_code (mach, cur);
+ if (cur->next)
+ {
+ mu_i_sv_code (mach, op);
+ mu_i_sv_code (mach, (sieve_op_t) pc);
+ pc = mach->pc - 1;
+ }
+ cur = cur->next;
+ }
+
+ /* Fix-up locations */
+ end = mach->pc;
+ while (pc != 0)
+ {
+ size_t prev = mach->prog[pc].pc;
+ mach->prog[pc].pc = end - pc - 1;
+ pc = prev;
+ }
+}
+
+static void
+dump_node_x_of (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ mu_sieve_machine_t mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "%s:\n",
+ node->type == mu_sieve_node_allof ? "ALLOF" : "ANYOF");
+
+ ++level;
+ node = node->v.node;
+ while (node)
+ {
+ node_dump (str, node, level + 1, mach);
+ node = node->next;
+ if (node)
+ {
+ indent (str, level);
+ mu_stream_printf (str, "%s:\n",
+ node->type == mu_sieve_node_allof ? "AND" : "OR");
+ }
+ }
+}
+
+/* mu_sieve_node_anyof */
+static void
+optimize_node_anyof (struct mu_sieve_node *node)
+{
+ optimize_x_of (node, mu_sieve_node_true);
+}
+
+static void
+code_node_anyof (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ code_node_x_of (mach, node, (sieve_op_t) _mu_i_sv_instr_brnz);
+}
+
+/* mu_sieve_node_allof */
+static void
+optimize_node_allof (struct mu_sieve_node *node)
+{
+ return optimize_x_of (node, mu_sieve_node_false);
+}
+
+static void
+code_node_allof (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ code_node_x_of (mach, node, (sieve_op_t) _mu_i_sv_instr_brz);
+}
+
+/* mu_sieve_node_not */
+static void
+free_node_not (struct mu_sieve_node *node)
+{
+ tree_free (&node->v.node);
+}
+
+static void
+optimize_node_not (struct mu_sieve_node *node)
+{
+ tree_optimize (node->v.node);
+ switch (node->v.node->type)
+ {
+ case mu_sieve_node_false:
+ tree_free (&node->v.node);
+ node->type = mu_sieve_node_true;
+ break;
+
+ case mu_sieve_node_true:
+ tree_free (&node->v.node);
+ node->type = mu_sieve_node_false;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+code_node_not (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ node_code (mach, node->v.node);
+ mu_i_sv_code (mach, (sieve_op_t) _mu_i_sv_instr_not);
+}
+
+static void
+dump_node_not (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "NOT\n");
+ node_dump (str, node->v.node, level + 1, mach);
+}
+
+/* mu_sieve_node_end */
+static void
+code_node_end (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ mu_i_sv_code (mach, (sieve_op_t) (sieve_instr_t) 0);
+}
+
+static void
+dump_node_end (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ indent (str, level);
+ mu_stream_printf (str, "END\n");
+}
+
+struct node_descr
+{
+ void (*code_fn) (struct mu_sieve_machine *mach, struct mu_sieve_node *node);
+ void (*optimize_fn) (struct mu_sieve_node *node);
+ void (*free_fn) (struct mu_sieve_node *node);
+ void (*dump_fn) (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ mu_sieve_machine_t);
+
+};
+
+static struct node_descr node_descr[] = {
+ [mu_sieve_node_noop] = { NULL, NULL, NULL, dump_node_noop },
+ [mu_sieve_node_false] = { NULL, NULL, NULL, dump_node_false },
+ [mu_sieve_node_true] = { NULL, NULL, NULL, dump_node_true },
+ [mu_sieve_node_test] = { code_node_test, NULL,
+ free_node_command, dump_node_command },
+ [mu_sieve_node_action] = { code_node_action, NULL,
+ free_node_command, dump_node_command },
+ [mu_sieve_node_cond] = { code_node_cond, optimize_node_cond,
+ free_node_cond, dump_node_cond },
+ [mu_sieve_node_anyof] = { code_node_anyof, optimize_node_anyof,
+ free_node_x_of, dump_node_x_of },
+ [mu_sieve_node_allof] = { code_node_allof, optimize_node_allof,
+ free_node_x_of, dump_node_x_of },
+ [mu_sieve_node_not] = { code_node_not, optimize_node_not,
+ free_node_not, dump_node_not },
+ [mu_sieve_node_end] = { code_node_end, NULL, NULL, dump_node_end }
+};
+
+static void
+node_optimize (struct mu_sieve_node *node)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+ if (node_descr[node->type].optimize_fn)
+ node_descr[node->type].optimize_fn (node);
+}
+
+static void
+node_free (struct mu_sieve_node *node)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+ if (node_descr[node->type].free_fn)
+ node_descr[node->type].free_fn (node);
+ free (node);
+}
+
+static void
+node_replace (struct mu_sieve_node *node, struct mu_sieve_node *repl)
+{
+ struct mu_sieve_node copy;
+
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+
+ copy = *node;
+ if (repl)
+ {
+ node->type = repl->type;
+ node->v = repl->v;
+
+ switch (copy.type)
+ {
+ case mu_sieve_node_cond:
+ if (repl == copy.v.cond.expr)
+ copy.v.cond.expr = NULL;
+ else if (repl == copy.v.cond.iftrue)
+ copy.v.cond.iftrue = NULL;
+ else if (repl == copy.v.cond.iffalse)
+ copy.v.cond.iffalse = NULL;
+ break;
+
+ case mu_sieve_node_not:
+ if (repl == copy.v.node)
+ copy.v.node = NULL;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ node->type = mu_sieve_node_noop;
+
+ if (node_descr[node->type].free_fn)
+ node_descr[node->type].free_fn (&copy);
+}
+
+static void
+node_code (struct mu_sieve_machine *mach, struct mu_sieve_node *node)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr))
+ abort ();
+
+ if (node_descr[node->type].code_fn)
+ {
+ mu_i_sv_locus (mach, &node->locus);
+ node_descr[node->type].code_fn (mach, node);
+ }
+}
+
+static void
+node_dump (mu_stream_t str, struct mu_sieve_node *node, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ if ((int)node->type >= MU_ARRAY_SIZE (node_descr)
+ || !node_descr[node->type].dump_fn)
+ abort ();
+ node_descr[node->type].dump_fn (str, node, level, mach);
+}
+
+
+static void
+tree_free (struct mu_sieve_node **tree)
+{
+ struct mu_sieve_node *cur = *tree;
+ while (cur)
+ {
+ struct mu_sieve_node *next = cur->next;
+ node_free (cur);
+ cur = next;
+ }
+}
+
+static void
+tree_optimize (struct mu_sieve_node *tree)
+{
+ while (tree)
+ {
+ node_optimize (tree);
+ tree = tree->next;
+ }
+}
+
+static void
+tree_code (struct mu_sieve_machine *mach, struct mu_sieve_node *tree)
+{
+ while (tree)
+ {
+ node_code (mach, tree);
+ tree = tree->next;
+ }
+}
+
+static void
+tree_dump (mu_stream_t str, struct mu_sieve_node *tree, unsigned level,
+ struct mu_sieve_machine *mach)
+{
+ while (tree)
+ {
+ node_dump (str, tree, level, mach);
+ tree = tree->next;
+ }
+}
+
+void
+mu_i_sv_error (mu_sieve_machine_t mach)
+{
+ mach->state = mu_sieve_state_error;
+}
+
+int
+mu_sieve_machine_create (mu_sieve_machine_t *pmach)
+{
+ int rc;
+ mu_sieve_machine_t mach;
+
+ mu_sieve_debug_init ();
+ mach = malloc (sizeof (*mach));
+ if (!mach)
+ return ENOMEM;
+ memset (mach, 0, sizeof (*mach));
+ mach->memory_pool = NULL;
+ rc = mu_opool_create (&mach->string_pool, MU_OPOOL_DEFAULT);
+ if (rc)
+ {
+ mu_list_destroy (&mach->memory_pool);
+ free (mach);
+ return rc;
+ }
+
+ mach->data = NULL;
+
+ mu_sieve_set_diag_stream (mach, mu_strerr);
+ mu_sieve_set_dbg_stream (mach, mu_strerr);
+
+ *pmach = mach;
+ return 0;
+}
+
+void
+mu_i_sv_free_stringspace (mu_sieve_machine_t mach)
+{
+ size_t i;
+
+ for (i = 0; i < mach->stringcount; i++)
+ {
+ if (mach->stringspace[i].rx)
+ {
+ regex_t *rx = mach->stringspace[i].rx;
+ regfree (rx);
+ }
+ /* There's no need to free mach->stringspace[i].exp, because
+ it is allocated in mach's memory pool */
+ }
+}
+
+int
+mu_sieve_machine_reset (mu_sieve_machine_t mach)
+{
+ switch (mach->state)
+ {
+ case mu_sieve_state_init:
+ /* Nothing to do */
+ return 0;
+
+ case mu_sieve_state_error:
+ case mu_sieve_state_compiled:
+ /* Do the right thing */
+ break;
+
+ case mu_sieve_state_running:
+ case mu_sieve_state_disass:
+ /* Can't reset a running machine */
+ return MU_ERR_FAILURE;
+ }
+
+ mu_i_sv_free_stringspace (mach);
+ mu_list_clear (mach->memory_pool);
+ mu_list_clear (mach->destr_list);
+ mu_opool_free (mach->string_pool, NULL);
+ mu_i_sv_free_idspace (mach);
+ mu_list_clear (mach->registry);
+
+ mach->stringspace = NULL;
+ mach->stringcount = 0;
+ mach->stringmax = 0;
+
+ mach->valspace = NULL;
+ mach->valcount = 0;
+ mach->valmax = 0;
+
+ mach->progsize = 0;
+ mach->prog = NULL;
+
+ mu_assoc_destroy (&mach->vartab);
+ mach->match_string = NULL;
+ mach->match_buf = NULL;
+ mach->match_count = 0;
+ mach->match_max = 0;
+
+ mach->state = mu_sieve_state_init;
+
+ return 0;
+}
+
+static int
+regdup (void *item, void *data)
+{
+ mu_sieve_registry_t *reg = item;
+ mu_sieve_machine_t mach = data;
+
+ mu_sieve_registry_require (mach, reg->name, reg->type);
+ return 0;
+}
+
+static void
+copy_stream_state (mu_sieve_machine_t child, mu_sieve_machine_t parent)
+{
+ child->state_flags = parent->state_flags;
+ child->err_mode = parent->err_mode;
+ mu_locus_range_copy (&child->err_locus, &parent->err_locus);
+ child->dbg_mode = parent->dbg_mode;
+ mu_locus_range_copy (&child->dbg_locus, &parent->dbg_locus);
+ child->errstream = parent->errstream;
+ mu_stream_ref (child->errstream);
+ child->dbgstream = parent->dbgstream;
+ mu_stream_ref (child->dbgstream);
+}
+
+int
+mu_sieve_machine_clone (mu_sieve_machine_t const parent,
+ mu_sieve_machine_t *pmach)
+{
+ size_t i;
+ mu_sieve_machine_t child;
+ int rc;
+
+ if (!parent || parent->state == mu_sieve_state_error)
+ return EINVAL;
+
+ rc = mu_sieve_machine_create (&child);
+ if (rc)
+ return rc;
+
+ rc = setjmp (child->errbuf);
+
+ if (rc == 0)
+ {
+ child->state = mu_sieve_state_init;
+ mu_i_sv_register_standard_actions (child);
+ mu_i_sv_register_standard_tests (child);
+ mu_i_sv_register_standard_comparators (child);
+
+ /* Load necessary modules */
+ mu_list_foreach (parent->registry, regdup, child);
+
+ /* Copy identifiers */
+ child->idspace = mu_sieve_calloc (child, parent->idcount,
+ sizeof (child->idspace[0]));
+ child->idcount = child->idmax = parent->idcount;
+ for (i = 0; i < child->idcount; i++)
+ child->idspace[i] = mu_sieve_strdup (parent, parent->idspace[i]);
+
+ /* Copy string constants */
+ child->stringspace = mu_sieve_calloc (child, parent->stringcount,
+ sizeof (child->stringspace[0]));
+ child->stringcount = child->stringmax = parent->stringcount;
+ for (i = 0; i < parent->stringcount; i++)
+ {
+ memset (&child->stringspace[i], 0, sizeof (child->stringspace[0]));
+ child->stringspace[i].orig =
+ mu_sieve_strdup (parent, parent->stringspace[i].orig);
+ }
+
+ /* Copy value space */
+ child->valspace = mu_sieve_calloc (child, parent->valcount,
+ sizeof child->valspace[0]);
+ child->valcount = child->valmax = parent->valcount;
+ for (i = 0; i < child->valcount; i++)
+ {
+ child->valspace[i].type = parent->valspace[i].type;
+ child->valspace[i].tag =
+ mu_sieve_strdup (child, parent->valspace[i].tag);
+ switch (child->valspace[i].type)
+ {
+ case SVT_TAG:
+ child->valspace[i].v.string =
+ mu_sieve_strdup (child, parent->valspace[i].v.string);
+ break;
+
+ default:
+ child->valspace[i].v = parent->valspace[i].v;
+ }
+ }
+
+ /* Copy progspace */
+ child->progsize = parent->progsize;
+ child->prog = mu_sieve_calloc (child, parent->progsize,
+ sizeof child->prog[0]);
+ memcpy (child->prog, parent->prog,
+ parent->progsize * sizeof (child->prog[0]));
+
+ /* Copy variables */
+ if (mu_sieve_has_variables (parent))
+ {
+ mu_i_sv_copy_variables (child, parent);
+ child->match_string = NULL;
+ child->match_buf = NULL;
+ child->match_count = 0;
+ child->match_max = 0;
+ }
+
+ /* Copy user-defined settings */
+
+ child->dry_run = parent->dry_run;
+
+ copy_stream_state (child, parent);
+
+ child->data = parent->data;
+ child->logger = parent->logger;
+ child->daemon_email = parent->daemon_email;
+
+ *pmach = child;
+ }
+ else
+ mu_sieve_machine_destroy (&child);
+
+ return rc;
+}
+
+int
+mu_sieve_machine_dup (mu_sieve_machine_t const in, mu_sieve_machine_t *out)
+{
+ int rc;
+ mu_sieve_machine_t mach;
+
+ if (!in || in->state == mu_sieve_state_error)
+ return EINVAL;
+ mach = malloc (sizeof (*mach));
+ if (!mach)
+ return ENOMEM;
+ memset (mach, 0, sizeof (*mach));
+ rc = mu_list_create (&mach->memory_pool);
+ if (rc)
+ {
+ free (mach);
+ return rc;
+ }
+ mach->destr_list = NULL;
+ mach->registry = NULL;
+
+ mach->progsize = in->progsize;
+ mach->prog = in->prog;
+
+ switch (in->state)
+ {
+ case mu_sieve_state_running:
+ case mu_sieve_state_disass:
+ mach->state = mu_sieve_state_compiled;
+ break;
+
+ default:
+ mach->state = in->state;
+ }
+
+ rc = setjmp (mach->errbuf);
+
+ if (rc == 0)
+ {
+ mach->pc = 0;
+ mach->reg = 0;
+
+ mach->dry_run = in->dry_run;
+
+ mach->state_flags = in->state_flags;
+ mach->err_mode = in->err_mode;
+ mu_locus_range_copy (&mach->err_locus, &in->err_locus);
+ mach->dbg_mode = in->dbg_mode;
+ mu_locus_range_copy (&mach->dbg_locus, &in->dbg_locus);
+
+ copy_stream_state (mach, in);
+
+ mach->data = in->data;
+ mach->logger = in->logger;
+ mach->daemon_email = in->daemon_email;