diff options
Diffstat (limited to 'libmu_sieve/sieve-gram.y')
-rw-r--r-- | libmu_sieve/sieve-gram.y | 1598 |
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 (©); +} + +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; |