%{
/* 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 =
|