%{
/* This file is part of Mailfromd.
Copyright (C) 2005-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include "mailfromd.h"
#include
#include "prog.h"
#include "optab.h"
static void free_node(NODE *node);
static void set_poll_arg(struct poll_data *poll, int kw, NODE *expr);
static int codegen(prog_counter_t *pc, NODE *node, struct exmask *exmask,
int finalize, size_t nautos);
static void mark(NODE *node);
static void dataseg_layout(void);
static void regex_layout(void);
static int optimize_tree(NODE *node);
static void compile_tree(NODE *node);
static NODE *reverse(NODE *in);
static size_t nodelistlength(NODE *list);
static NODE *function_call(struct function *function, size_t count,
NODE *subtree);
static NODE *declare_function(struct function *func,
struct mu_locus_range const *loc,
size_t nautos);
static data_type_t node_type(NODE *node);
static NODE *cast_arg_list(NODE *args, size_t parmc, data_type_t *parmtype,
int disable_prom);
void add_xref(struct variable *var, struct mu_locus_range const *locus);
static struct variable *vardecl(const char *name, data_type_t type,
storage_class_t sc,
struct mu_locus_range const *loc);
static struct variable *externdecl(const char *name, struct value *value,
struct mu_locus_range const *loc);
static int initialize_variable(struct variable *var, struct value *val,
struct mu_locus_range const *locus);
static void apply_deferred_init(void);
static NODE *create_asgn_node(struct variable *var, NODE *expr,
struct mu_locus_range const *loc);
NODE *defined_parm(struct variable *var, struct mu_locus_range const *locus);
static void register_auto(struct variable *var);
static void unregister_auto(struct variable *var);
static size_t forget_autos(size_t nparam, size_t prev, size_t hidden_arg);
static void optimize(NODE *node);
static NODE *root_node[smtp_state_count];
prog_counter_t entry_point[smtp_state_count];
#define PS_BEGIN 0
#define PS_END 1
int regex_flags; /* Should default to REG_NOSUB ? */
unsigned error_count; /* Number of detected errors */
size_t variable_count = 0; /* Number of variables in the data segment. */
size_t precious_count = 0;
static enum smtp_state state_tag; /* State tag of the currently processed
PROG */
static struct function *func; /* The currently compiled function */
static prog_counter_t jump_pc; /* Pointer to the chain of jmp instructions */
/* Outermost context decides how to code exits from catches.
Innermost context is used to access parameters. */
enum lexical_context outer_context, inner_context;
size_t catch_nesting; /* Nesting level for catch statements */
static struct stmtlist genstmt; /* List of generated statements */
/* State handlers and their positional parameters */
struct state_parms {
int cnt; /* Number or positional parameters */
data_type_t types[4]; /* Their data types */
} state_parms[] = {
{ 0, }, /* smtp_state_none */
{ 0, }, /* smtp_state_begin */
{ 4, { dtype_string, dtype_number, dtype_number, dtype_string } },
/* smtp_state_connect */
{ 1, { dtype_string } }, /* smtp_state_helo */
{ 2, { dtype_string, dtype_string } }, /* smtp_state_envfrom */
{ 2, { dtype_string, dtype_string } }, /* smtp_state_envrcpt */
{ 0, }, /* smtp_state_data */
{ 2, { dtype_string, dtype_string } }, /* smtp_state_header */
{ 0, }, /* smtp_state_eoh */
{ 2, { dtype_pointer, dtype_number } }, /* smtp_state_body */
{ 0, }, /* smtp_state_eom */
{ 0, } /* smtp_state_end */
};
struct parmtype {
struct parmtype *next;
data_type_t type;
};
static int
parmcount_none()
{
return 0;
}
static data_type_t
parmtype_none(int n)
{
return dtype_unspecified;
}
static int
parmcount_handler()
{
return state_parms[state_tag].cnt;
}
static data_type_t
parmtype_handler(int n)
{
return state_parms[state_tag].types[n-1];
}
static int
parmcount_catch()
{
return 2;
}
static data_type_t
parmtype_catch(int n)
{
switch (n) {
case 1:
return dtype_number;
case 2:
return dtype_string;
}
abort();
}
static int
parmcount_function()
{
return func->parmcount;
}
static data_type_t
parmtype_function(int n)
{
if (func->varargs && n > func->parmcount)
return dtype_string;
return func->parmtype[n-1];
}
struct parminfo {
int (*parmcount)(void);
data_type_t (*parmtype)(int n);
} parminfo[] = {
{ parmcount_none, parmtype_none },
{ parmcount_handler, parmtype_handler },
{ parmcount_catch, parmtype_catch },
{ parmcount_function, parmtype_function },
};
#define PARMCOUNT() parminfo[inner_context].parmcount()
#define PARMTYPE(n) parminfo[inner_context].parmtype(n)
#define FUNC_HIDDEN_ARGS(f) (((f)->optcount || (f)->varargs) ? 1 : 0)
data_type_t
string_to_type(const char *s)
{
if (strcmp(s, "n") == 0)
return dtype_number;
else if (strcmp(s, "s") == 0)
return dtype_string;
else
return dtype_unspecified;
}
const char *
type_to_string(data_type_t t)
{
switch (t) {
case dtype_number:
return "number";
case dtype_string:
return "string";
case dtype_unspecified:
return "unspecified";
case dtype_pointer:
return "pointer";
default:
abort();
}
}
static int
check_func_usage(struct function *fp, struct mu_locus_range const *locus)
{
switch (outer_context) {
case context_handler:
if (fp->statemask && !(STATMASK(state_tag) & fp->statemask)) {
parse_error_locus(locus,
_("function `%s' cannot be used in "
"prog `%s'"),
fp->sym.name,
state_to_string(state_tag));
return 1;
}
break;
case context_function:
func->statemask |= fp->statemask;
break;
default:
break;
}
return 0;
}
static int
check_builtin_usage(const struct builtin *bp,
struct mu_locus_range const *locus)
{
switch (outer_context) {
case context_handler:
if (bp->statemask && !(STATMASK(state_tag) & bp->statemask)) {
parse_error_locus(locus,
_("built-in function `%s' cannot be used in "
"prog `%s'"),
bp->name,
state_to_string(state_tag));
return 1;
}
break;
case context_function:
func->statemask |= bp->statemask;
break;
default:
break;
}
if (bp->flags & MFD_BUILTIN_CAPTURE)
capture_on();
return 0;
}
static void jump_fixup(prog_counter_t pos, prog_counter_t endpos);
#define LITERAL_TEXT(lit) ((lit) ? (lit)->text : NULL)
#define LITERAL_OFF(lit) ((lit) ? (lit)->off : 0)
static int
_create_alias(void *item, void *data)
{
struct literal *lit = item;
struct function *fun = data;
/* FIXME: Ideally we should pass a locus of `lit' instead of
fun->locus. However, its only purpose is for use in redefinition
diagnostic messages and these are never produced, because the
grammar catches these errors earlier. This might change in
the future. 2008-09-14 */
install_alias(lit->text, fun, &fun->sym.locus);
return 0;
}
%}
%error-verbose
%locations
/*%expect 1*/
%union {
struct literal *literal;
struct stmtlist stmtlist;
NODE *node;
struct return_node ret;
struct poll_data poll;
struct pollarg {
int kw;
NODE *expr;
} pollarg;
struct arglist {
NODE *head;
NODE *tail;
size_t count;
} arglist;
long number;
const struct builtin *builtin;
struct variable *var;
enum smtp_state state;
struct {
int qualifier;
} matchtype;
data_type_t type;
struct parmtype *parm;
struct parmlist {
struct parmtype *head, *tail;
size_t count;
size_t optcount;
int varargs;
} parmlist;
enum lexical_context tie_in;
struct function *function;
struct {
struct valist *head, *tail;
} valist_list;
struct {
int all;
struct valist *valist;
} catchlist;
struct valist *valist;
struct value value;
struct {
struct case_stmt *head, *tail;
} case_list ;
struct case_stmt *case_stmt;
struct loop_node loop;
struct {
int code;
} progspecial;
struct enumlist {
struct constant *cv;
struct enumlist *prev;
} enumlist;
mu_list_t list;
struct import_rule_list import_rule_list;
char *string;
};
%token T_ACCEPT "accept"
T_REJECT "reject"
T_TEMPFAIL "tempfail"
T_CONTINUE "continue"
T_DISCARD "discard"
T_ADD "add" T_REPLACE "replace" T_DELETE "delete"
T_PROG "prog"
T_BEGIN "begin"
T_END "end"
T_IF "if"
T_FI "fi"
T_ELSE "else"
T_ELIF "elif"
T_ON "on"
T_HOST "host"
T_FROM "from"
T_AS "as"
T_DO "do" T_DONE "done"
T_POLL "poll"
T_MATCHES "matches"
T_FNMATCHES "fnmatches"
T_MXMATCHES "mx matches"
T_MXFNMATCHES "mx fnmatches"
T_WHEN "when"
T_PASS "pass"
T_SET "set"
T_CATCH "catch"
T_TRY "try"
T_THROW "throw"
T_ECHO "echo"
T_RETURNS "returns"
T_RETURN "return"
T_FUNC "func"
T_SWITCH "switch"
T_CASE "case"
T_DEFAULT "default"
T_CONST "const"
T_FOR "for"
T_LOOP "loop"
T_WHILE "while"
T_BREAK "break"
T_NEXT "next"
T_ARGCOUNT "$#"
T_ALIAS "alias"
T_DOTS "..."
T_ARGX "$(n)"
T_VAPTR "vaptr"
T_PRECIOUS "precious"
T_OR "or"
T_AND "and"
T_EQ "=="
T_NE "!="
T_LT "<"
T_LE "<="
T_GT ">"
T_GE ">="
T_NOT "!"
T_LOGAND "&"
T_LOGOR "|"
T_LOGXOR "^"
T_LOGNOT "~"
T_REQUIRE "require"
T_IMPORT "import"
T_STATIC "static"
T_PUBLIC "public"
T_MODULE "module"
T_BYE "bye"
T_DCLEX "dclex"
T_SHL "<<"
T_SHR ">>"
%token T_COMPOSE "composed string"
%token T_MODBEG T_MODEND
%token T_STRING "string"
%token T_SYMBOL "MTA macro" T_IDENTIFIER "identifier"
%token T_ARG "$n" T_NUMBER "number" T_BACKREF "back reference"
%token T_BUILTIN "builtin function"
%token T_FUNCTION "function"
%token T_TYPE "data type" T_TYPECAST "typecast"
%token T_VARIABLE "variable"
%token T_BOGUS
%left '.'
%left T_OR
%left T_AND
%left T_NOT
%left T_LOGOR
%left T_LOGXOR
%left T_LOGAND
%nonassoc T_EQ T_NE T_MATCHES T_FNMATCHES T_MXMATCHES T_MXFNMATCHES
%nonassoc T_LT T_LE T_GT T_GE
%left T_SHL T_SHR
%left '+' '-'
%left '*' '/' '%'
%left T_UMINUS
%type decl stmt condition action sendmail_action header_action
if_cond else_cond on_cond atom argref paren_argref
funcall expr maybe_expr maybe_xcode_expr
simp_expr atom_expr
asgn catch simple_catch try_block throw return
case_cond autodcl constdecl
loopstmt opt_while jumpstmt strval strcat
%type program stmtlist decllist modcntl
%type triplet maybe_triplet
%type pollstmt pollarglist
%type pollarg loop_parm
%type opt_loop_parms loop_parm_list
%type arglist
%type variable
%type literal string opt_ident loop_ident alias code xcode varname
%type state_ident
%type matches fnmatches
%type retdecl
%type params opt_parmlist parmlist fparmlist parmdecl
%type parm
%type fundecl
%type value
%type valist
%type catchlist
%type cond_branches branches
%type cond_branch branch
%type on
%type progspecial
%type aliases aliasdecl
%type imports
%type qualifiers qualifier qualconst
%type enumlist constdefn
%%
input : program
{
if (error_count == 0) {
if (genstmt.head) {
genstmt.tail->next = $1.head;
genstmt.tail = $1.tail;
} else
genstmt = $1;
optimize_tree(genstmt.head);
if (error_count)
YYERROR;
mark(genstmt.head);
if (error_count)
YYERROR;
apply_deferred_init();
fixup_exceptions();
dataseg_layout();
if (optimization_level)
regex_layout();
compile_tree(genstmt.head);
if (!optimization_level)
regex_layout();
}
}
;
program : decllist
| moddecl decllist bye
{
$$ = $2;
}
;
decllist : decl
{
$$.head = $$.tail = $1;
}
| modcntl
| decllist decl
{
if ($2) {
if ($1.tail)
$1.tail->next = $2;
else
$1.head = $2;
$1.tail = $2;
}
$$ = $1;
}
| decllist modcntl
{
if ($2.tail) {
if ($1.tail) {
$$.head = $1.head;
$1.tail->next = $2.head;
$$.tail = $2.tail;
} else
$$ = $2;
}
}
;
modcntl : require
{
$$.head = $$.tail = NULL;
}
| require T_MODBEG opt_moddecl decllist bye T_MODEND
{
$$ = $4;
pop_top_module();
}
;
opt_moddecl: /* empty */
{
parse_warning(_("missing module declaration"));
}
| moddecl
;
moddecl : T_MODULE literal '.'
{
if (top_module->dclname) {
struct mu_locus_range lr;
lr.beg = @1.beg;
lr.end = @3.end;
parse_error_locus(&lr,
_("duplicate module declaration"));
} else
top_module->dclname = $2->text;
}
| T_MODULE literal qualifier '.'
{
if (top_module->dclname) {
struct mu_locus_range lr;
lr.beg = @1.beg;
lr.end = @4.end;
parse_error_locus(&lr,
_("duplicate module declaration"));
} else {
if (($3 & (SYM_STATIC|SYM_PUBLIC)) == 0)
parse_error_locus(&@3,
_("invalid module declaration"));
top_module->dclname = $2->text;
top_module->flags = $3;
}
}
;
require : T_REQUIRE literal
{
require_module($2->text, NULL);
}
| T_FROM literal T_IMPORT imports '.'
{
require_module($2->text, $4.head);
}
;
bye : /* empty */
| T_BYE
{
lex_bye();
}
;
imports : literal
{
struct import_rule *rule = import_rule_create($1);
$$.head = $$.tail = rule;
}
| imports ',' literal
{
struct import_rule *rule = import_rule_create($3);
$$.tail->next = rule;
$$.tail = rule;
}
;
literal : T_STRING
| T_IDENTIFIER
;
decl : T_PROG state_ident T_DO stmtlist T_DONE
{
$$ = alloc_node(node_type_progdecl, &@1);
$$->v.progdecl.tag = $2;
$$->v.progdecl.auto_count = forget_autos(PARMCOUNT(),
0, 0);
$$->v.progdecl.tree = $4.head;
outer_context = inner_context = context_none;
state_tag = smtp_state_none;
}
| progspecial T_DO stmtlist T_DONE
{
static NODE *progspecial[2];
NODE *np = progspecial[$1.code];
if (!np) {
np = alloc_node(node_type_progdecl, &@1);
$$ = progspecial[$1.code] = np;
np->v.progdecl.tag = state_tag;
np->v.progdecl.tree = $3.head;
np->v.progdecl.auto_count = 0;
} else {
NODE *cur;
for (cur = np->v.progdecl.tree; cur->next;
cur = cur->next)
;
cur->next = $3.head;
$$ = NULL;
}
np->v.progdecl.auto_count = forget_autos(PARMCOUNT(),
np->v.progdecl.auto_count,
0);
outer_context = inner_context = context_none;
state_tag = smtp_state_none;
}
| qualifiers T_FUNC fundecl T_DO stmtlist T_DONE
{
if ($1 & SYM_PRECIOUS)
parse_error_locus(&@2,
_("`precious' used with func"));
if (($1 & (SYM_STATIC|SYM_PUBLIC)) ==
(SYM_STATIC|SYM_PUBLIC))
parse_error_locus(&@2,
_("`static' and `public' "
"used together"));
func->sym.flags = $1;
func->node = $5.head;
$$ = declare_function(func, &@2,
forget_autos(PARMCOUNT() + FUNC_HIDDEN_ARGS(func),
0, FUNC_HIDDEN_ARGS(func)));
outer_context = inner_context = context_none;
func = NULL;
}
| vardecl
{
$$ = NULL;
}
| constdecl
{
$$ = NULL;
}
| exdecl
{
$$ = NULL;
}
;
progspecial: T_BEGIN
{
state_tag = smtp_state_begin;
outer_context = inner_context = context_handler;
$$.code = PS_BEGIN;
}
| T_END
{
state_tag = smtp_state_end;
outer_context = inner_context = context_handler;
$$.code = PS_END;
}
;
varname : T_IDENTIFIER
| T_VARIABLE
{
$$ = literal_lookup($1->sym.name);
}
;
vardecl : qualifiers T_TYPE varname
{
struct variable *var;
if (($1 & (SYM_STATIC|SYM_PUBLIC))
== (SYM_STATIC|SYM_PUBLIC))
parse_error_locus(&@1,
_("`static' and `public' "
"used together"));
var = vardecl($3->text, $2, storage_extern, &@3);
if (!var)
YYERROR;
var->sym.flags |= $1;
}
| qualifiers T_TYPE varname expr
{
struct value value;
struct variable *var;
if (($1 & (SYM_STATIC|SYM_PUBLIC))
== (SYM_STATIC|SYM_PUBLIC))
parse_error_locus(&@1,
_("`static' and `public' "
"used together"));
var = vardecl($3->text, $2, storage_extern, &@2);
if (!var)
YYERROR;
var->sym.flags |= $1;
if (optimization_level)
optimize($4);
value.type = node_type($4);
switch ($4->type) {
case node_type_string:
value.v.literal = $4->v.literal;
break;
case node_type_number:
value.v.number = $4->v.number;
break;
default:
yyerror("initializer element is not constant");
YYERROR;
}
if (initialize_variable(var, &value, &@3))
YYERROR;
}
| T_SET varname expr
/* FIXME: Optimize if varname: T_VARIABLE */
{
struct value value;
if (optimization_level)
optimize($3);
value.type = node_type($3);
switch ($3->type) {
case node_type_string:
value.v.literal = $3->v.literal;
break;
case node_type_number:
value.v.number = $3->v.number;
break;
default:
yyerror("initializer element is not constant");
YYERROR;
}
if (!externdecl($2->text, &value, &@2))
YYERROR;
}
;
qualifiers : /* empty */
{
$$ = 0;
}
| qualifiers qualifier
{
if ($1 & $2)
parse_warning(_("duplicate `%s'"),
symbit_to_qualifier($2));
$$ = $1 | $2;
}
;
qualifier : T_PRECIOUS
{
$$ = SYM_PRECIOUS;
}
| T_STATIC
{
$$ = SYM_STATIC;
}
| T_PUBLIC
{
$$ = SYM_PUBLIC;
}
;
constdecl : qualconst constdefn
{
$2.cv->sym.flags = $1;
$$ = NULL;
}
| qualconst T_DO enumlist T_DONE
{
struct enumlist *elist;
while ((elist = $3.prev)) {
elist->cv->sym.flags = $1;
elist = elist->prev;
free($3.prev);
$3.prev = elist;
}
$$ = NULL;
}
;
qualconst : qualifiers T_CONST
{
if ($1 & SYM_PRECIOUS)
parse_error_locus(&@1,
_("`precious' used with const"));
if (($1 & (SYM_STATIC|SYM_PUBLIC)) ==
(SYM_STATIC|SYM_PUBLIC))
parse_error_locus(&@1,
_("`static' and `public' "
"used together"));
$$ = $1;
}
;
constdefn : varname expr
/* FIXME: Optimize if varname: T_IDENTIFIER */
{
struct value value;
struct variable *pvar;
/* FIXME: This is necessary because constants can be
referred to the same way as variables. */
if (pvar = variable_lookup($1->text)) {
parse_warning(_("constant name `%s' clashes with a variable name"),
$1->text);
parse_warning_locus(&pvar->sym.locus,
_("this is the location of the "
"previous definition"));
}
if (optimization_level)
optimize($2);
switch ($2->type) {
case node_type_string:
value.type = dtype_string;
value.v.literal = $2->v.literal;
break;
case node_type_number:
value.type = dtype_number;
value.v.number = $2->v.number;
break;
default:
yyerror(_("initializer element is not constant"));
YYERROR;
}
$$.cv = define_constant($1->text, &value, 0, &@1);
$$.prev = NULL;
}
;
enumlist : varname
{
struct enumlist *elist;
struct value value;
struct variable *pvar;
if (pvar = variable_lookup($1->text)) {
parse_warning(_("constant name `%s' clashes with a variable name"),
$1->text);
parse_warning_locus(&pvar->sym.locus,
_("this is the location of the "
"previous definition"));
}
value.type = dtype_number;
value.v.number = 0;
elist = mu_alloc(sizeof(*elist));
elist->cv = define_constant($1->text, &value, 0, &@1);
elist->prev = NULL;
$$.cv = NULL;
$$.prev = elist;
}
| constdefn
{
struct enumlist *elist = mu_alloc(sizeof(*elist));
elist->cv = $1.cv;
elist->prev = NULL;
$$.cv = NULL;
$$.prev = elist;
}
| enumlist varname
{
if ($1.prev->cv->value.type == dtype_number) {
struct enumlist *elist = mu_alloc(sizeof(*elist));
struct value value;
value.type = dtype_number;
value.v.number = $1.prev->cv->value.v.number + 1;
elist->cv = define_constant($2->text, &value, 0,
&@2);
elist->prev = $1.prev;
$$.prev = elist;
} else {
yyerror(_("initializer element is not numeric"));
YYERROR;
}
}
| enumlist constdefn
{
struct enumlist *elist = mu_alloc(sizeof(*elist));
elist->cv = $2.cv;
elist->prev = $1.prev;
$1.prev = elist;
$$ = $1;
}
;
exdecl : T_DCLEX T_IDENTIFIER
{
define_exception($2, &@1);
}
;
fundecl : varname '(' parmdecl ')' aliasdecl retdecl
{
data_type_t *ptypes = NULL;
if ($3.count) {
int i;
struct parmtype *p;
ptypes = mu_alloc($3.count * sizeof *ptypes);
for (i = 0, p = $3.head; p; i++) {
struct parmtype *next = p->next;
ptypes[i] = p->type;
free(p);
p = next;
}
}
$$ = func = function_install($1->text,
$3.count, $3.optcount,
$3.varargs,
ptypes, $6,
&@1);
if ($5) {
mu_list_foreach($5, _create_alias, $$);
mu_list_destroy(&$5);
}
outer_context = inner_context = context_function;
}
;
parmdecl : /* empty */
{
$$.count = $$.optcount = 0;
$$.varargs = 0;
}
| params
;
params : fparmlist
| T_DOTS
{
$$.count = $$.optcount = 0;
$$.varargs = 1;
}
| opt_parmlist ';' fparmlist
{
$1.count += $3.count;
$1.optcount = $3.count;
$1.varargs = $3.varargs;
if ($1.tail)
$1.tail->next = $3.head;
else
$1.head = $3.head;
$1.tail = $3.tail;
$$ = $1;
}
;
opt_parmlist: /* empty */
{
$$.count = 0;
$$.varargs = 0;
$$.head = $$.tail = NULL;
}
| parmlist
;
parmlist : parm
{
$$.count = 1;
$$.varargs = 0;
$$.optcount = 0;
$$.head = $$.tail = $1;
}
| parmlist ',' parm
{
$1.count++;
$1.tail->next = $3;
$1.tail = $3;
$$ = $1;
}
;
fparmlist : parmlist
| parmlist ',' T_DOTS
{
$1.varargs = 1;
$$ = $1;
}
;
parm : T_TYPE varname
{
if (!vardecl($2->text, $1, storage_param, &@2))
YYERROR;
$$ = mu_alloc(sizeof *$$);
$$->next = NULL;
$$->type = $1;
}
/* FIXME: Is this still needed? */
| T_TYPE
{
parse_warning_locus(&@1,
_("unnamed formal parameters are deprecated"));
$$ = mu_alloc(sizeof *$$);
$$->next = NULL;
$$->type = $1;
}
;
aliasdecl : /* empty */
{
$$ = NULL;
}
| aliases
;
aliases : alias
{
mu_list_create(&$$);
mu_list_append($$, $1);
}
| aliases alias
{
mu_list_append($1, $2);
$$ = $1;
}
;
alias : T_ALIAS varname
{
$$ = $2;
}
;
retdecl : /* empty */
{
$$ = dtype_unspecified;
}
| T_RETURNS T_TYPE
{
$$ = $2;
}
;
state_ident: T_IDENTIFIER
{
$$ = string_to_state($1->text);
if ($$ == smtp_state_none)
parse_error_locus(&@1,
_("unknown smtp state tag: %s"),
$1->text);
state_tag = $$;
outer_context = inner_context = context_handler;
}
;
stmtlist : stmt
{
if ($1)
$1->next = NULL;
$$.head = $$.tail = $1;
}
| stmtlist stmt
{
if ($2) {
if ($1.tail)
$1.tail->next = $2;
else
$1.head = $2;
$1.tail = $2;
}
$$ = $1;
}
;
stmt : condition
| action
| asgn
| autodcl
| catch
| throw
| return
| funcall
{
if (node_type($1) != dtype_unspecified)
parse_warning(_("return from %s is ignored"),
$1->type == node_type_builtin ?
$1->v.builtin.builtin->name :
$1->v.call.func->sym.name);
}
| constdecl
| loopstmt
| jumpstmt
;
asgn : T_SET varname expr
{
struct variable *var;
data_type_t t = node_type($3);
if (t == dtype_unspecified) {
parse_error_locus(&@3,
_("unspecified value not ignored as "
"it should be"));
YYERROR;
}
var = variable_lookup($2->text);
if (!var) {
var = vardecl($2->text, t, storage_auto, &@2);
if (!var)
YYERROR;
}
$$ = create_asgn_node(var, $3, &@1);
if (!$$)
YYERROR;
}
;
autodcl : T_TYPE varname
{
if (!vardecl($2->text, $1, storage_auto, &@2))
YYERROR;
$$ = NULL;
}
| T_TYPE varname expr
{
struct variable *var = vardecl($2->text, $1,
storage_auto, &@2);
if (!var)
YYERROR;
$$ = create_asgn_node(var, $3, &@1);
if (!$$)
YYERROR;
}
;
action : sendmail_action
{
if (inner_context == context_handler) {
if (state_tag == smtp_state_begin)
parse_error_locus(&@1,
_("Sendmail action is not "
"allowed in begin block"));
else if (state_tag == smtp_state_end)
parse_error_locus(&@1,
_("Sendmail action is not "
"allowed in end block"));
}
}
| header_action
{
if (inner_context == context_handler
&& state_tag == smtp_state_end)
parse_error_locus(&@1,
_("header action is not allowed "
"in end block"));
}
| T_PASS
{
$$ = alloc_node(node_type_noop, &@1);
}
| T_ECHO expr
{
$$ = alloc_node(node_type_echo, &@1);
$$->v.node = cast_to(dtype_string, $2);
}
;
sendmail_action:
T_ACCEPT maybe_triplet
{
if ($2.code || $2.xcode || $2.message)
parse_warning(_("arguments are ignored for accept"));
$$ = alloc_node(node_type_result, &@1);
$$->v.ret = $2;
$$->v.ret.stat = SMFIS_ACCEPT;
}
| T_REJECT maybe_triplet
{
$$ = alloc_node(node_type_result, &@1);
$$->v.ret = $2;
$$->v.ret.stat = SMFIS_REJECT;
}
| T_REJECT '(' maybe_expr ',' maybe_xcode_expr ',' maybe_expr ')'
{
$$ = alloc_node(node_type_result, &@1);
$$->v.ret.stat = SMFIS_REJECT;
$$->v.ret.code = $3 ? cast_to(dtype_string, $3) : NULL;
$$->v.ret.xcode = $5 ? cast_to(dtype_string, $5) : NULL;
$$->v.ret.message = $7 ? cast_to(dtype_string, $7) : NULL;
}
| T_TEMPFAIL maybe_triplet
{
$$ = alloc_node(node_type_result, &@1);
$$->v.ret = $2;
$$->v.ret.stat = SMFIS_TEMPFAIL;
}
| T_TEMPFAIL '(' maybe_expr ',' maybe_xcode_expr ',' maybe_expr ')'
{
$$ = alloc_node(node_type_result, &@1);
$$->v.ret.stat = SMFIS_TEMPFAIL;
$$->v.ret.code = $3 ? cast_to(dtype_string, $3) : NULL;
$$->v.ret.xcode = $5 ? cast_to(dtype_string, $5) : NULL;
$$->v.ret.message = $7 ? cast_to(dtype_string, $7) : NULL;
}
| T_CONTINUE
{
$$ = alloc_node(node_type_result, &@1);
memset(&$$->v.ret, 0, sizeof $$->v.ret);
$$->v.ret.stat = SMFIS_CONTINUE;
}
| T_DISCARD
{
$$ = alloc_node(node_type_result, &@1);
memset(&$$->v.ret, 0, sizeof $$->v.ret);
$$->v.ret.stat = SMFIS_DISCARD;
}
;
maybe_xcode_expr: maybe_expr
| xcode
{
$$ = alloc_node(node_type_string, &@1);
$$->v.literal = $1;
}
;
header_action:
T_ADD string expr
{
$$ = alloc_node(node_type_header, &@1);
$$->v.hdr.opcode = header_add;
$$->v.hdr.name = $2;
$$->v.hdr.value = cast_to(dtype_string, $3);
}
| T_REPLACE string expr
{
$$ = alloc_node(node_type_header, &@1);
$$->v.hdr.opcode = header_replace;
$$->v.hdr.name = $2;
$$->v.hdr.value = cast_to(dtype_string, $3);
}
| T_DELETE string
{
$$ = alloc_node(node_type_header, &@1);
$$->v.hdr.opcode = header_delete;
$$->v.hdr.name = $2;
$$->v.hdr.value = NULL;
}
;
maybe_triplet: /* empty */
{
memset(&$$, 0, sizeof $$);
}
| triplet
;
triplet : code
{
$$.code = alloc_node(node_type_string, &@1);
$$.code->v.literal = $1;
$$.xcode = NULL;
$$.message = NULL;
}
| code xcode
{
$$.code = alloc_node(node_type_string, &@1);
$$.code->v.literal = $1;
$$.xcode = alloc_node(node_type_string, &@2);
$$.xcode->v.literal = $2;
$$.message = NULL;
}
| code xcode expr
{
$$.code = alloc_node(node_type_string, &@1);
$$.code->v.literal = $1;
$$.xcode = alloc_node(node_type_string, &@2);
$$.xcode->v.literal = $2;
$$.message = cast_to(dtype_string, $3);
}
| code expr
{
$$.code = alloc_node(node_type_string, &@1);
$$.code->v.literal = $1;
$$.xcode = NULL;
$$.message = cast_to(dtype_string, $2);
}
;
code : T_NUMBER
{
char buf[4];
if ($1 < 200 || $1 > 599) {
yyerror(_("invalid SMTP reply code"));
buf[0] = 0;
} else
snprintf(buf, sizeof(buf), "%lu", $1);
$$ = string_alloc(buf, strlen(buf));
}
;
xcode : T_NUMBER '.' T_NUMBER '.' T_NUMBER
{
char buf[sizeof("5.999.999")];
/* RFC 1893:
The syntax of the new status codes is defined as:
status-code = class "." subject "." detail
class = "2"/"4"/"5"
subject = 1*3digit
detail = 1*3digit
*/
if (($1 != 2 && $1 != 4 && $1 !=5)
|| $3 > 999 || $5 > 999) {
yyerror(_("invalid extended reply code"));
buf[0] = 0;
} else
snprintf(buf, sizeof(buf), "%lu.%lu.%lu",
$1, $3, $5);
$$ = string_alloc(buf, strlen(buf));
}
;
condition : if_cond
| case_cond
| on_cond
;
if_cond : T_IF expr stmtlist else_cond T_FI
{
$$ = alloc_node(node_type_if, &@1);
$$->v.cond.cond = $2;
$$->v.cond.if_true = $3.head;
$$->v.cond.if_false = $4;
}
;
else_cond : /* empty */
{
$$ = NULL;
}
| T_ELIF expr stmtlist else_cond
{
$$ = alloc_node(node_type_if, &@1);
$$->v.cond.cond = $2;
$$->v.cond.if_true = $3.head;
$$->v.cond.if_false = $4;
}
| T_ELSE stmtlist
{
$$ = $2.head;
}
;
case_cond : T_SWITCH expr T_DO cond_branches T_DONE
{
struct case_stmt *defcase = NULL, *pcase, *prev;
$$ = alloc_node(node_type_switch, &@1);
$$->v.switch_stmt.node = $2;
/* Make sure there is only one default case and
place it at the beginning of the list */
pcase = $4.head;
if (!pcase->valist) {
defcase = pcase;
$4.head = $4.head->next;
}
prev = pcase;
pcase = pcase->next;
while (pcase) {
if (!pcase->valist) {
if (defcase) {
parse_error_locus(&pcase->locus,
_("duplicate default statement"));
parse_error_locus(&defcase->locus,
_("previously defined here"));
YYERROR;
}
defcase = pcase;
prev->next = pcase->next;
} else
prev = pcase;
pcase = pcase->next;
}
if (!defcase) {
defcase = mu_alloc(sizeof *defcase);
mu_locus_range_init(&defcase->locus);
mu_locus_range_copy(&defcase->locus, &@5);
defcase->valist = NULL;
defcase->node = alloc_node(node_type_noop,
&defcase->locus);
}
defcase->next = $4.head;
$$->v.switch_stmt.cases = defcase;
}
;
cond_branches: cond_branch
{
$$.head = $$.tail = $1;
}
| cond_branches cond_branch
{
$$.tail->next = $2;
$$.tail = $2;
}
;
cond_branch: T_CASE valist ':' stmtlist
{
$$ = mu_alloc(sizeof *$$);
$$->next = NULL;
mu_locus_range_init (&$$->locus);
mu_locus_range_copy (&$$->locus, &@1);
$$->valist = $2.head;
$$->node = $4.head;
}
| T_DEFAULT ':' stmtlist
{
$$ = mu_alloc(sizeof *$$);
$$->next = NULL;
mu_locus_range_init (&$$->locus);
mu_locus_range_copy (&$$->locus, &@1);
$$->valist = NULL;
$$->node = $3.head;
}
;
valist : value
{
$$.head = $$.tail = mu_alloc(sizeof($$.head[0]));
$$.head->next = NULL;
$$.head->value = $1;
}
| valist T_OR value
{
struct valist *p = mu_alloc(sizeof(*p));
p->value = $3;
p->next = NULL;
$1.tail->next = p;
$1.tail = p;
$$ = $1;
}
;
value : T_STRING
{
$$.type = dtype_string;
$$.v.literal = $1;
}
| T_NUMBER
{
$$.type = dtype_number;
$$.v.number = $1;
}
;
string : value
{
if ($1.type != dtype_string) {
parse_error_locus(&@1,
_("expected string, but found %s"),
type_to_string($1.type));
/* Make sure we return something usable: */
$$ = string_alloc("ERROR", 5);
} else
$$ = $1.v.literal;
}
;
matches : T_MATCHES
{
$$.qualifier = 0;
}
| T_MXMATCHES
{
$$.qualifier = QUALIFIER_MX;
}
;
fnmatches : T_FNMATCHES
{
$$.qualifier = 0;
}
| T_MXFNMATCHES
{
$$.qualifier = QUALIFIER_MX;
}
;
/* Loop statements */
loopstmt : T_LOOP loop_ident opt_loop_parms T_DO stmtlist T_DONE opt_while
{
leave_loop();
$3.end_while = $7;
$$ = alloc_node(node_type_loop, &@1);
$3.body = $5.head;
$3.ident = $2;
$$->v.loop = $3;
}
;
loop_ident : opt_ident
{
enter_loop($1, NULL, NULL);
}
;
opt_ident : /* empty */
{
$$ = NULL;
}
| varname
;
opt_loop_parms: /* empty */
{
memset(&$$, 0, sizeof $$);
}
| loop_parm_list
;
loop_parm_list: loop_parm
{
memset(&$$, 0, sizeof $$);
switch ($1.kw) {
case 0:
$$.stmt = $1.expr;
break;
case T_FOR:
$$.for_stmt = $1.expr;
break;
case T_WHILE:
$$.beg_while = $1.expr;
break;
default:
abort();
}
}
| loop_parm_list ',' loop_parm
{
switch ($3.kw) {
case 0:
if ($$.stmt)
parse_error_locus(&@3,
_("duplicate loop increment"));
$$.stmt = $3.expr;
break;
case T_FOR:
if ($$.for_stmt)
parse_error_locus(&@3,
_("duplicate for statement"));
$$.for_stmt = $3.expr;
break;
case T_WHILE:
if ($$.beg_while)
parse_error_locus(&@3,
_("duplicate while statement"));
$$.beg_while = $3.expr;
break;
default:
abort();
}
}
;
loop_parm : stmtlist
{
$$.kw = 0;
$$.expr = $1.head;
}
| T_FOR stmtlist
{
$$.kw = T_FOR;
$$.expr = $2.head;
}
| T_WHILE expr
{
$$.kw = T_WHILE;
$$.expr = $2;
}
;
opt_while : /* empty */
{
$$ = NULL;
}
| T_WHILE expr
{
$$ = $2;
}
;
jumpstmt : T_BREAK opt_ident
{
if (!within_loop($2)) {
if ($2)
parse_error_locus(&@2,
_("no such loop: %s"),
$2->text);
parse_error_locus(&@1,
_("`break' used outside of `loop'"));
YYERROR;
}
$$ = alloc_node(node_type_break, &@1);
$$->v.literal = $2;
}
| T_NEXT opt_ident
{
if (!within_loop($2)) {
if ($2) {
parse_error_locus(&@2,
_("no such loop: %s"),
$2->text);
parse_error_locus(&@1,
_("`next' used outside `loop'"));
YYERROR;
} else {
parse_error_locus(&@1,
_("`next' is used outside `loop'; "
"did you mean `pass'?"));
YYERROR;
}
} else {
$$ = alloc_node(node_type_next, &@1);
$$->v.literal = $2;
}
}
;
/* Expressions */
expr : T_NOT expr
{
$$ = alloc_node(node_type_un, &@1);
$$->v.un.opcode = unary_not;
$$->v.un.arg = cast_to(dtype_number, $2);
}
| expr T_EQ expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_eq;
$$->v.bin.arg[0] = $1;
$$->v.bin.arg[1] = cast_to(node_type($1), $3);
}
| expr T_NE expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_ne;
$$->v.bin.arg[0] = $1;
$$->v.bin.arg[1] = cast_to(node_type($1), $3);
}
| expr T_LT expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_lt;
$$->v.bin.arg[0] = $1;
$$->v.bin.arg[1] = cast_to(node_type($1), $3);
}
| expr T_LE expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_le;
$$->v.bin.arg[0] = $1;
$$->v.bin.arg[1] = cast_to(node_type($1), $3);
}
| expr T_GT expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_gt;
$$->v.bin.arg[0] = $1;
$$->v.bin.arg[1] = cast_to(node_type($1), $3);
}
| expr T_GE expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_ge;
$$->v.bin.arg[0] = $1;
$$->v.bin.arg[1] = cast_to(node_type($1), $3);
}
| expr matches expr %prec T_MATCHES
{
NODE *p;
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_match;
$$->v.bin.qualifier = $2.qualifier;
$$->v.bin.arg[0] = cast_to(dtype_string, $1);
$$->v.bin.arg[1] = p = alloc_node(node_type_regcomp,
&@2);
p->v.regcomp_data.expr = cast_to(dtype_string, $3);
p->v.regcomp_data.flags = regex_flags;
p->v.regcomp_data.regind = -1;
}
| expr fnmatches expr %prec T_MATCHES
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_fnmatch;
$$->v.bin.qualifier = $2.qualifier;
$$->v.bin.arg[0] = cast_to(dtype_string, $1);
$$->v.bin.arg[1] = cast_to(dtype_string, $3);
}
| expr T_OR expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_or;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| expr T_AND expr
{
$$ = alloc_node(node_type_bin, &@2);
$$->v.bin.opcode = bin_and;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr
;
maybe_expr : /* empty */
{
$$ = NULL;
}
| expr
;
simp_expr : atom_expr
| simp_expr '+' simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_add;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr '.' simp_expr
{
$$ = alloc_node(node_type_concat, &@2);
$$->v.concat.arg[0] = cast_to(dtype_string, $1);
$$->v.concat.arg[1] = cast_to(dtype_string, $3);
}
| simp_expr '-' simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_sub;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr '*' simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_mul;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr '/' simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_div;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr '%' simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_mod;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr T_LOGAND simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_logand;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr T_LOGOR simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_logor;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr T_LOGXOR simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_logxor;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr T_SHL simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_shl;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
| simp_expr T_SHR simp_expr
{
$$ = alloc_node(node_type_bin, &@1);
$$->v.bin.opcode = bin_shr;
$$->v.bin.arg[0] = cast_to(dtype_number, $1);
$$->v.bin.arg[1] = cast_to(dtype_number, $3);
}
;
atom_expr : funcall
{
if (node_type($1) == dtype_unspecified)
parse_error(_("unspecified value not ignored as it should be"));
}
| '(' expr ')'
{
$$ = $2;
}
| T_TYPECAST '(' expr ')'
{
$$ = cast_to($1, $3);
}
| atom
| '-' simp_expr %prec T_UMINUS
{
$$ = alloc_node(node_type_un, &@1);
$$->v.un.opcode = unary_minus;
$$->v.un.arg = cast_to(dtype_number, $2);
}
| '+' simp_expr %prec T_UMINUS
{
$$ = $2;
}
| T_LOGNOT simp_expr %prec T_UMINUS
{
$$ = alloc_node(node_type_un, &@1);
$$->v.un.opcode = unary_lognot;
$$->v.un.arg = cast_to(dtype_number, $2);
}
;
atom : T_SYMBOL
{
$$ = create_node_symbol($1, &@1);
}
| T_NUMBER
{
$$ = alloc_node(node_type_number, &@1);
$$->v.number = $1;
}
| T_BACKREF
{
$$ = create_node_backref($1, &@1);
}
| argref
| T_ARGCOUNT
{
$$ = create_node_argcount(&@1);
}
| '@' T_VARIABLE
{
$$ = alloc_node(node_type_offset, &@1);
$$->v.var_ref.variable = $2;
$$->v.var_ref.nframes = catch_nesting;
}
| T_VAPTR paren_argref
{
$$ = alloc_node(node_type_vaptr, &@1);
$$->v.node = $2;
}
| strcat
;
strcat : strval
| strcat strval
{
$$ = alloc_node(node_type_concat, &@2);
$$->v.concat.arg[0] = $1;
$$->v.concat.arg[1] = $2;
}
;
strval : T_STRING
{
$$ = alloc_node(node_type_string, &@1);
$$->v.literal = $1;
}
| T_COMPOSE
;
argref : variable
{
$$ = create_node_variable($1, &@1);
}
| T_ARG
{
$$ = create_node_arg($1, &@1);
}
| T_ARGX '(' expr ')'
{
if (outer_context == context_function) {
if (func->varargs) {
$$ = alloc_node(node_type_argx, &@1);
$$->v.argx.nargs = PARMCOUNT() +
FUNC_HIDDEN_ARGS(func);
$$->v.argx.node = $3;
} else {
$$ = alloc_node(node_type_noop, &@1);
parse_error_locus(&@1,
_("$(expr) is allowed only "
"in a function with "
"variable number of "
"arguments"));
}
} else {
$$ = alloc_node(node_type_noop, &@1);
parse_error_locus(&@1,
_("$(expr) is allowed only "
"in a function with "
"variable number of "
"arguments"));
}
}
;
paren_argref: argref
| '(' argref ')'
{
$$ = $2;
}
;
funcall : T_BUILTIN '(' arglist ')'
{
if (check_builtin_usage($1, &@1))
YYERROR;
if ($3.count < $1->parmcount - $1->optcount) {
parse_error_locus(&@1,
_("too few arguments in call to `%s'"),
$1->name);
YYERROR;
} else if ($3.count > $1->parmcount
&& !($1->flags & MFD_BUILTIN_VARIADIC)) {
parse_error_locus(&@1,
_("too many arguments in call to `%s'"),
$1->name);
YYERROR;
} else {
$$ = alloc_node(node_type_builtin, &@1);
$$->v.builtin.builtin = $1;
$$->v.builtin.args =
reverse(cast_arg_list($3.head,
$1->parmcount,
$1->parmtype,
$$->v.builtin.builtin->flags & MFD_BUILTIN_NO_PROMOTE));
}
}
| T_BUILTIN '(' ')'
{
if (check_builtin_usage($1, &@1))
YYERROR;
if ($1->parmcount - $1->optcount) {
parse_error_locus(&@1,
_("too few arguments in call to `%s'"),
$1->name);
YYERROR;
} else {
$$ = alloc_node(node_type_builtin, &@1);
$$->v.builtin.builtin = $1;
$$->v.builtin.args = NULL;
}
}
| T_FUNCTION '(' arglist ')'
{
if (check_func_usage($1, &@1))
YYERROR;
$$ = function_call($1, $3.count, $3.head);
if (!$$)
YYERROR;
}
| T_FUNCTION '(' ')'
{
if (check_func_usage($1, &@1))
YYERROR;
$$ = function_call($1, 0, NULL);
if (!$$)
YYERROR;
}
;
arglist : expr
{
$1->next = NULL;
$$.head = $$.tail = $1;
$$.count = 1;
}
| arglist ',' expr
{
$1.tail->next = $3;
$1.tail = $3;
$1.count++;
$$ = $1;
}
;
variable : T_VARIABLE
{
add_xref($1, &@1);
}
| T_BOGUS
{
YYERROR;
}
;
catch : simple_catch
{
if (outer_context == context_function) {
func->exmask->all |= $$->v.catch.exmask->all;
bitmask_merge(&func->exmask->bm,
&$$->v.catch.exmask->bm);
}
}
| try_block simple_catch
{
$$ = alloc_node(node_type_try, &@1);
$$->v.try.node = $1;
$$->v.try.catch = $2;
}
;
try_block : T_TRY T_DO stmtlist T_DONE
{
$$ = $3.head;
}
;
simple_catch
: T_CATCH catchlist T_DO
{ $$ = inner_context;
inner_context = context_catch;
catch_nesting++; }
stmtlist T_DONE
{
int i;
struct valist *p;
inner_context = $4;
catch_nesting--;
$$ = alloc_node(node_type_catch, &@1);
$$->v.catch.exmask = exmask_create();
$$->v.catch.context = outer_context;/*??*/
$$->v.catch.exmask->all = $2.all;
if (!$2.all) {
for (i = 0, p = $2.valist; p; p = p->next, i++) {
if (p->value.type != dtype_number) {
parse_error_locus(&@1,
_("expected numeric value, but found `%s'"),
p->value.v.literal->text);
continue;
}
bitmask_set(&$$->v.catch.exmask->bm,
p->value.v.number);
}
}
$$->v.catch.node = $5.head;
}
;
catchlist : '*'
{
$$.all = 1;
}
| valist
{
$$.all = 0;
$$.valist = $1.head;
}
;
throw : T_THROW value expr
{
$$ = alloc_node(node_type_throw, &@1);
if ($2.type != dtype_number)
parse_error_locus(&@2,
_("exception code not a number"));
else if ($2.v.number > exception_count)
parse_error_locus(&@2,
_("invalid exception number: %lu"),
$2.v.number);
$$->v.throw.code = $2.v.number;
$$->v.throw.expr = cast_to(dtype_string, $3);
}
;
return : T_RETURN
{
if (!func)
parse_error_locus(&@1,
_("`return' outside of a function"));
else if (func->rettype != dtype_unspecified)
parse_error_locus(&@1,
_("`return' with no value, in function "
"returning non-void"));
$$ = alloc_node(node_type_return, &@1);
$$->v.node = NULL;
}
| T_RETURN expr
{
if (!func)
parse_error_locus(&@1,
_("`return' outside of a function"));
else {
$$ = alloc_node(node_type_return, &@1);
if (func->rettype == dtype_unspecified) {
parse_error_locus(&@1,
_("`return' with a value, in function "
"returning void"));
$$->v.node = NULL;
}
else
$$->v.node = cast_to(func->rettype, $2);
}
}
;
/* *************************** */
/* ON statement */
/* *************************** */
on_cond : on pollstmt do branches T_DONE
{
NODE *sel, *np;
NODE *head = NULL, *tail;
struct function *fp;
fp = function_lookup($2.client_addr ?
"strictpoll" : "stdpoll");
if (!fp) {
parse_error_locus(&@1,
_("`on poll' used without prior `require poll'"));
YYERROR;
}
/* Build argument list */
if ($2.client_addr) {
head = tail = $2.client_addr;
tail = $2.email;
if (!tail) {
parse_error_locus(&@2,
_("recipient address not specified "
"in `on poll' construct"));
YYERROR;
}
tail->next = NULL;
head->next = tail;
} else
head = tail = $2.email;
if ($2.ehlo)
np = $2.ehlo;
else {
/* FIXME: Pass NULL? */
np = alloc_node(node_type_variable, &@2);
np->v.var_ref.variable =
variable_lookup("ehlo_domain");
np->v.var_ref.nframes = 0;
}
tail->next = np;
tail = np;
if ($2.mailfrom)
np = $2.mailfrom;
else {
/* FIXME: Pass NULL? */
np = alloc_node(node_type_variable, &@2);
np->v.var_ref.variable =
variable_lookup("mailfrom_address");
np->v.var_ref.nframes = 0;
}
tail->next = np;
tail = np;
sel = function_call(fp, nodelistlength(head), head);
$$ = alloc_node(node_type_switch, &@1);
$$->v.switch_stmt.node = sel;
$$->v.switch_stmt.cases = $4.head;
}
| on funcall do branches T_DONE
{
$$ = alloc_node(node_type_switch, &@1);
$$->v.switch_stmt.node = $2;
$$->v.switch_stmt.cases = $4.head;
}
;
on : T_ON
{
tie_in_onblock(1);
}
;
do : T_DO
{
tie_in_onblock(0);
}
;
pollstmt : T_POLL expr
{
struct pollarg arg;
arg.kw = T_FOR;
arg.expr = $2;
memset(&$$, 0, sizeof $$);
set_poll_arg(&$$, arg.kw, arg.expr);
}
| T_POLL expr pollarglist
{
struct pollarg arg;
arg.kw = T_FOR;
arg.expr = $2;
set_poll_arg(&$3, arg.kw, arg.expr);
$$ = $3;
}
| T_POLL pollarglist
{
$$ = $2;
}
;
pollarglist: pollarg
{
memset(&$$, 0, sizeof $$);
set_poll_arg(&$$, $1.kw, $1.expr);
}
| pollarglist pollarg
{
set_poll_arg(&$1, $2.kw, $2.expr);
$$ = $1;
}
;
pollarg : T_FOR expr
{
$$.kw = T_FOR;
$$.expr = $2;
}
| T_HOST expr
{
$$.kw = T_HOST;
$$.expr = $2;
}
| T_AS expr
{
$$.kw = T_AS;
$$.expr = $2;
}
| T_FROM expr
{
$$.kw = T_FROM;
$$.expr = $2;
}
;
branches : branch
{
$$.head = $$.tail = $1;
}
| branches branch
{
$1.tail->next = $2;
$1.tail = $2;
$$ = $1;
}
;
branch : T_WHEN valist ':' stmtlist
{
struct valist *p;
for (p = $2.head; p; p = p->next) {
if (p->value.type == dtype_string) {
parse_error_locus(&@2,
_("invalid data type, "
"expected number"));
/* Try to continue */
p->value.type = dtype_number;
p->value.v.number = 0;
}
}
$$ = mu_alloc(sizeof *$$);
$$->next = NULL;
mu_locus_range_init (&$$->locus);
mu_locus_range_copy (&$$->locus, &@1);
$$->valist = $2.head;
$$->node = $4.head;
}
;
%%
int
yyerror(char const *s)
{
parse_error("%s", s);
return 0;
}
struct stream_state
{
int mode;
struct mu_locus_range loc;
int sevmask;
};
void
stream_state_save(mu_stream_t str, struct stream_state *st)
{
mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_MODE, &st->mode);
mu_locus_range_init(&st->loc);
mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_LOCUS_RANGE, &st->loc);
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_SEVERITY_MASK,
&st->sevmask);
}
void
stream_state_restore(mu_stream_t str, struct stream_state *st)
{
mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &st->mode);
mu_stream_ioctl(mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS_RANGE, &st->loc);
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_SEVERITY_MASK,
&st->sevmask);
mu_locus_range_deinit(&st->loc);
}
int
parse_program(char *name, int ydebug)
{
int rc;
struct stream_state st;
int mode;
stream_state_save(mu_strerr, &st);
mode = st.mode | MU_LOGMODE_LOCUS | MU_LOGMODE_SEVERITY;
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
mode = MU_DEBUG_LEVEL_MASK (MU_DIAG_ERROR);
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_SEVERITY_MASK,
&mode);
yydebug = ydebug;
if (lex_new_source(name, 0))
return -1;
outer_context = inner_context = context_none;
catch_nesting = 0;
code_immediate(NULL, ptr); /* Reserve 0 slot */
rc = yyparse() + error_count;
stream_state_restore(mu_strerr, &st);
return rc;
}
static void
alloc_locus(struct mu_locus_range const *locus)
{
struct literal *lit;
if (locus->beg.mu_file) {
lit = string_alloc(locus->beg.mu_file,
strlen(locus->beg.mu_file));
lit->flags |= SYM_REFERENCED;
}
if (!mu_locus_point_same_file(&locus->beg, &locus->end)) {
lit = string_alloc(locus->end.mu_file,
strlen(locus->end.mu_file));
lit->flags |= SYM_REFERENCED;
}
}
NODE *
alloc_node(enum node_type type, struct mu_locus_range const *locus)
{
NODE *node = malloc(sizeof(*node));
if (!node) {
yyerror("Not enough memory");
abort();
}
node->type = type;
mu_locus_range_init (&node->locus);
mu_locus_range_copy (&node->locus, locus);
alloc_locus(locus);
node->next = NULL;
return node;
}
void
free_node(NODE *node)
{
mu_locus_range_deinit(&node->locus);
free(node);
}
void
copy_node(NODE *dest, NODE *src)
{
dest->type = src->type;
mu_locus_range_copy (&dest->locus, &src->locus);
dest->v = src->v;
}
void
free_subtree(NODE *node)
{
/*FIXME*/
}
void
free_parser_data()
{
/*FIXME*/
}
/* Print parse tree */
static void print_node_list(NODE *node, int indent);
static void print_node_list_reverse(NODE *node, int level);
static void print_node(NODE *node, int indent);
void print_stat(sfsistat stat);
static int dbg_setreply(void *data, char *code, char *xcode, char *message);
static void dbg_msgmod(void *data, struct msgmod_closure *clos);
static void
print_level(int level)
{
level *= 2;
printf("%*.*s", level, level, "");
}
static void
print_bin_op(enum bin_opcode opcode)
{
char *p;
switch (opcode) {
case bin_and:
p = "AND";
break;
case bin_or:
p = "OR";
break;
case bin_eq:
p = "EQ";
break;
case bin_ne:
p = "NE";
break;
case bin_lt:
p = "LT";
break;
case bin_le:
p = "LE";
break;
case bin_gt:
p = "GT";
break;
case bin_ge:
p = "GE";
break;
case bin_match:
p = "MATCH";
break;
case bin_fnmatch:
p = "FNMATCH";
break;
case bin_add:
p = "ADD";
break;
case bin_sub:
p = "SUB";
break;
case bin_mul:
p = "MUL";
break;
case bin_div:
p = "DIV";
break;
case bin_mod:
p = "MOD";
break;
case bin_logand:
p = "LOGAND";
break;
case bin_logor:
p = "LOGOR";
break;
case bin_logxor:
p = "LOGXOR";
break;
case bin_shl:
p = "SHL";
break;
case bin_shr:
p = "SHR";
break;
default:
p = "UNKNOWN_OP";
}
printf("%s", p);
}
static void
print_quoted_string(const char *str)
{
for (; *str; str++) {
if (mu_isprint(*str))
putchar(*str);
else {
putchar('\\');
switch (*str) {
case '\a':
putchar('a');
break;
case '\b':
putchar('b');
break;
case '\f':
putchar('f');
break;
case '\n':
putchar('n');
break;
case '\r':
putchar('r');
break;
case '\t':
putchar('t');
break;
default:
printf("%03o", *str);
}
}
}
}
struct node_drv {
void (*print) (NODE *, int);
void (*mark) (NODE *);
void (*code) (NODE *, struct mu_locus_range const **);
void (*optimize) (NODE *);
};
static void traverse_tree(NODE *node);
static void code_node(NODE *node);
static void code_node(NODE *node);
static void optimize_node(NODE *node);
static void optimize(NODE *node);
static void record_switch(struct switch_stmt *sw);
#include "drivers.c"
#include "node-tab.c"
struct node_drv *
find_node_drv(enum node_type type)
{
if (type >= NELEMS(nodetab)) {
parse_error(_("INTERNAL ERROR at %s:%d, "
"unexpected node type %d"),
__FILE__, __LINE__,
type);
abort();
}
return nodetab + type;
}
static void
print_node(NODE *node, int level)
{
struct node_drv *nd = find_node_drv(node->type);
if (nd->print)
nd->print(node, level);
}
static void
print_node_list(NODE *node, int level)
{
for (; node; node = node->next)
print_node(node, level);
}
static void
print_node_list_reverse(NODE *node, int level)
{
if (node) {
print_node_list_reverse(node->next, level);
print_node(node, level);
}
}
int
function_enumerator(void *sym, void *data)
{
int i;
struct function *fsym = sym;
struct function *f =
(struct function *)symbol_resolve_alias(&fsym->sym);
struct module *mod = data;
if (f->sym.module != mod)
return 0;
printf("function %s (", f->sym.name);
for (i = 0; i < f->parmcount; i++) {
printf("%s", type_to_string(f->parmtype[i]));
if (i < f->parmcount-1)
putchar(',');
}
putchar(')');
if (f->rettype != dtype_unspecified)
printf(" returns %s", type_to_string(f->rettype));
printf(":\n");
print_node_list(f->node, 0);
printf("END function %s\n", f->sym.name);
return 0;
}
void
print_syntax_tree()
{
struct module **modv;
size_t i, modc;
enum smtp_state tag;
printf("State handlers:\n");
printf("---------------\n");
for (tag = smtp_state_first; tag < smtp_state_count; tag++) {
if (root_node[tag]) {
printf("%s:\n", state_to_string(tag));
print_node_list(root_node[tag], 0);
putchar('\n');
}
}
printf("User functions:\n");
printf("---------------\n");
collect_modules(&modv, &modc);
for (i = 0; i < modc; i++) {
size_t n;
n = printf("Module %s (%s)\n", modv[i]->name,
modv[i]->file);
while (n--)
putchar('-');
putchar('\n');
symtab_enumerate(MODULE_SYMTAB(modv[i], namespace_function),
function_enumerator, modv[i]);
putchar('\n');
}
free(modv);
}
/* Cross-reference support */
struct collect_data {
mu_opool_t pool;
size_t count;
};
static int
variable_enumerator(void *item, void *data)
{
struct variable *var = item;
struct collect_data *p = data;
if (var->sym.flags & (SYM_VOLATILE | SYM_REFERENCED)) {
mu_opool_append(p->pool, &var, sizeof var);
p->count++;
}
return 0;
}
int
print_locus(void *item, void *data)
{
struct mu_locus_range *loc = item;
struct mu_locus_range **prev = data;
int c;
if (!*prev) {
*prev = loc;
printf("%s", loc->beg.mu_file);
c = ':';
} else if (mu_locus_point_same_file (&(*prev)->beg, &loc->beg)) {
*prev = loc;
printf(", %s", loc->beg.mu_file);
c = ':';
} else
c = ',';
printf("%c%u", c, loc->beg.mu_line);
return 0;
}
void
print_xref_var(struct variable *var)
{
struct mu_locus_range *prev = NULL;
var = (struct variable *)symbol_resolve_alias(&var->sym);
printf("%-32.32s %6s %lu ",
var->sym.name, type_to_string(var->type),
(unsigned long)var->off);
mu_list_foreach(var->xref, print_locus, &prev);
printf("\n");
}
int
vp_comp(const void *a, const void *b)
{
struct variable * const *va = a, * const *vb = b;
return strcmp((*va)->sym.name, (*vb)->sym.name);
}
void
print_xref()
{
struct collect_data cd;
struct variable **vp;
size_t i;
mu_opool_create(&cd.pool, MU_OPOOL_ENOMEMABRT);
cd.count = 0;
symtab_enumerate(TOP_MODULE_SYMTAB(namespace_variable),
variable_enumerator, &cd);
printf("Cross-references:\n");
printf("-----------------\n");
vp = mu_opool_finish(cd.pool, NULL);
qsort(vp, cd.count, sizeof *vp, vp_comp);
for (i = 0; i < cd.count; i++, vp++)
print_xref_var(*vp);
mu_opool_destroy(&cd.pool);
}
static mu_list_t smtp_macro_list[gacopyz_stage_max];
static enum gacopyz_stage
smtp_to_gacopyz_stage(enum smtp_state tag)
{
switch (tag) {
case smtp_state_connect:
return gacopyz_stage_conn;
case smtp_state_helo:
return gacopyz_stage_helo;
case smtp_state_envfrom:
return gacopyz_stage_mail;
case smtp_state_envrcpt:
return gacopyz_stage_rcpt;
case smtp_state_data:
case smtp_state_header:
case smtp_state_body:
return gacopyz_stage_data;
case smtp_state_eoh:
return gacopyz_stage_eoh;
case smtp_state_eom:
return gacopyz_stage_eom;
default:
break;
}
return gacopyz_stage_none;
}
static int
compare_macro_names (const void *item, const void *data)
{
const char *elt;
size_t elen;
const char *arg;
size_t alen;
elt = item;
elen = strlen (elt);
if (elt[0] == '{') {
elt++;
elen -= 2;
}
arg = data;
alen = strlen (arg);
if (arg[0] == '{') {
arg++;
alen -= 2;
}
if (alen != elen)
return 1;
return memcmp (elt, arg, alen);
}
void
register_macro(enum smtp_state state, const char *macro)
{
enum gacopyz_stage ind = smtp_to_gacopyz_stage(state);
if (ind == gacopyz_stage_none)
return;
if (!smtp_macro_list[ind]) {
mu_list_create(&smtp_macro_list[ind]);
mu_list_set_comparator(smtp_macro_list[ind],
compare_macro_names);
}
/* FIXME: MU: 2nd arg should be const? */
if (mu_list_locate(smtp_macro_list[ind], (void*) macro, NULL)) {
char *cmacro;
if (macro[1] == 0 || macro[0] == '{')
cmacro = mu_strdup (macro);
else {
size_t mlen = strlen (macro);
cmacro = mu_alloc (mlen + 3);
cmacro[0] = '{';
memcpy (cmacro + 1, macro, mlen);
cmacro[mlen + 1] = '}';
cmacro[mlen + 2] = 0;
}
mu_list_append(smtp_macro_list[ind], cmacro);
}
}
static int
print_macro(void *item, void *data)
{
int *p = data;
if (*p) {
printf(" ");
*p = 0;
} else
printf(", ");
printf("%s", (char*) item);
return 0;
}
void
print_used_macros()
{
enum gacopyz_stage i;
for (i = 0; i < gacopyz_stage_max; i++) {
if (smtp_macro_list[i]) {
int n = 1;
printf("%s", gacopyz_stage_name[i]);
mu_list_foreach(smtp_macro_list[i], print_macro, &n);
printf("\n");
}
}
}
struct macro_acc {
size_t size;
char *buf;
};
static int
add_macro_size(void *item, void *data)
{
char *macro = (char*) item;
struct macro_acc *mp = data;
mp->size += strlen(macro) + 1;
return 0;
}
static int
concat_macro(void *item, void *data)
{
char *macro = (char*) item;
struct macro_acc *mp = data;
size_t len = strlen(macro);
char *pbuf = mp->buf + mp->size;
memcpy(pbuf, macro, len);
pbuf[len++] = ' ';
mp->size += len;
return 0;
}
char *
get_stage_macro_string(enum gacopyz_stage i)
{
struct macro_acc acc;
size_t size;
mu_list_t list = smtp_macro_list[i];
if (!list)
return NULL;
acc.size = 0;
mu_list_foreach(list, add_macro_size, &acc);
if (!acc.size)
return NULL;
size = acc.size;
acc.size = 0;
acc.buf = mu_alloc (size);
mu_list_foreach(list, concat_macro, &acc);
acc.buf[size-1] = 0;
return acc.buf;
}
/* Code generation */
static void
code_node(NODE *node)
{
if (!node)
error_count++;
else {
static struct mu_locus_range const *old_locus;
struct node_drv *nd = find_node_drv(node->type);
if (nd->code)
nd->code(node, &old_locus);
}
}
static void
traverse_tree(NODE *node)
{
for (; node; node = node->next)
code_node(node);
}
static void
optimize_node(NODE *node)
{
if (!node)
error_count++;
else {
struct node_drv *nd = find_node_drv(node->type);
if (nd->optimize)
nd->optimize(node);
}
}
static void
optimize(NODE *node)
{
for (; node; node = node->next)
optimize_node(node);
}
static int
optimize_tree(NODE *node)
{
if (optimization_level)
optimize(node);
return error_count;
}
static struct switch_stmt *switch_root;
static void
record_switch(struct switch_stmt *sw)
{
sw->next = switch_root;
switch_root = sw;
}
static struct exmask *exmask_root;
struct exmask *
exmask_create()
{
struct exmask *p = mu_alloc(sizeof(*p));
p->next = exmask_root;
p->off = 0;
p->all = 0;
bitmask_init(&p->bm);
exmask_root = p;
return p;
}
static void
mark_node(NODE *node)
{
if (!node)
error_count++;
else {
struct node_drv *nd = find_node_drv(node->type);
if (nd->mark)
nd->mark(node);
}
}
static void
mark(NODE *node)
{
for (; node; node = node->next)
mark_node(node);
}
static int
codegen(prog_counter_t *pc, NODE *node, struct exmask *exmask,
int finalize, size_t nautos)
{
int save_mask;
if (error_count)
return 1;
*pc = code_get_counter();
jump_pc = 0;
if (nautos) {
code_op(opcode_stkalloc);
code_immediate(nautos, uint);
}
save_mask = exmask && bitmask_nset(&exmask->bm);
if (save_mask) {
code_op(opcode_saveex);
code_exmask(exmask);
}
traverse_tree(node);
jump_fixup(jump_pc, code_get_counter());
if (save_mask)
code_op(opcode_restex);
if (finalize)
code_op(opcode_nil);
else
code_op(opcode_return);
return 0;
}
static void
compile_tree(NODE *node)
{
struct node_drv *nd;
struct mu_locus_range const *plocus;
for (; node; node = node->next) {
switch (node->type) {
case node_type_progdecl:
case node_type_funcdecl:
nd = find_node_drv(node->type);
if (!nd->code)
abort();
nd->code(node, &plocus);
break;
default:
parse_error_locus(&node->locus,
_("INTERNAL ERROR at %s:%d, "
"unexpected node type %d"),
__FILE__, __LINE__,
node->type);
break;
}
}
}
enum regex_mode { regex_enable, regex_disable, regex_set };
static mf_stack_t regex_stack;
void
regex_push()
{
if (!regex_stack)
regex_stack = mf_stack_create(sizeof regex_flags, 0);
mf_stack_push(regex_stack, ®ex_flags);
}
void
regex_pop()
{
if (!regex_stack || mf_stack_pop(regex_stack, ®ex_flags))
parse_error(_("nothing to pop"));
}
static void
pragma_regex(int argc, char **argv, const char *text)
{
enum regex_mode mode = regex_set;
int i = 1;
if (strcmp(argv[i], "push") == 0) {
regex_push();
i++;
} else if (strcmp(argv[i], "pop") == 0) {
regex_pop();
i++;
}
for (; i < argc; i++) {
int bit;
char *p = argv[i];
switch (p[0]) {
case '+':
mode = regex_enable;
p++;
break;
case '-':
mode = regex_disable;
p++;
break;
case '=':
mode = regex_set;
p++;
break;
}
if (strcmp (p, REG_EXTENDED_NAME) == 0)
bit = REG_EXTENDED;
else if (strcmp (p, REG_ICASE_NAME) == 0)
bit = REG_ICASE;
else if (strcmp (p, REG_NEWLINE_NAME) == 0)
bit = REG_NEWLINE;
else {
parse_error(_("unknown regexp flag: %s"), p);
return;
}
switch (mode) {
case regex_disable:
regex_flags &= ~bit;
break;
case regex_enable:
regex_flags |= bit;
break;
case regex_set:
regex_flags = bit;
break;
}
}
}
static int
strtosize(const char *text, size_t *psize)
{
unsigned long n;
size_t size;
char *p;
n = strtoul(text, &p, 0);
size = n;
switch (*p) {
case 't':
case 'T':
size *= 1024;
case 'g':
case 'G':
size *= 1024;
case 'm':
case 'M':
size *= 1024;
case 'k':
case 'K':
size *= 1024;
p++;
if (*p && (*p == 'b' || *p == 'B'))
p++;
break;
case 0:
break;
default:
parse_error(_("invalid size suffix (near %s)"), p);
return 1;
}
if (size < n) {
parse_error(_("invalid size: numeric overflow occurred"));
return 2;
}
*psize = size;
return 0;
}
static void
pragma_stacksize(int argc, char **argv, const char *text)
{
size_t size, incr = stack_expand_incr, max_size = stack_max_size;
enum stack_expand_policy policy = stack_expand_policy;
switch (argc) {
case 4:
if (strtosize(argv[3], &max_size))
return;
case 3:
if (strcmp(argv[2], "twice") == 0)
policy = stack_expand_twice;
else {
policy = stack_expand_add;
if (strtosize(argv[2], &incr))
return;
}
case 2:
strtosize(argv[1], &size);
}
stack_size = size;
stack_expand_incr = incr;
stack_expand_policy = policy;
stack_max_size = max_size;
}
void
pragma_setup()
{
install_pragma("regex", 2, 0, pragma_regex);
install_pragma("stacksize", 2, 4, pragma_stacksize);
}
/* Test run */
struct sfsistat_tab {
char *name;
sfsistat stat;
} sfsistat_tab[] = {
{ "accept", SMFIS_ACCEPT },
{ "continue", SMFIS_CONTINUE },
{ "discard", SMFIS_DISCARD },
{ "reject", SMFIS_REJECT },
{ "tempfail", SMFIS_TEMPFAIL },
{ NULL }
};
const char *
sfsistat_str(sfsistat stat)
{
struct sfsistat_tab *p;
for (p = sfsistat_tab; p->name; p++)
if (p->stat == stat)
return p->name;
return NULL;
}
void
print_stat(sfsistat stat)
{
struct sfsistat_tab *p;
for (p = sfsistat_tab; p->name; p++)
if (p->stat == stat) {
printf("%s", p->name);
return;
}
printf("%d", stat);
}
const char *
msgmod_opcode_str(enum msgmod_opcode opcode)
{
switch (opcode) {
case header_add:
return "ADD HEADER";
case header_replace:
return "REPLACE HEADER";
case header_delete:
return "DELETE HEADER";
case header_insert:
return "INSERT HEADER";
case rcpt_add:
return "ADD RECIPIENT";
case rcpt_delete:
return "DELETE RECIPIENT";
case quarantine:
return "QUARANTINE";
case body_repl:
return "REPLACE BODY";
case body_repl_fd:
return "REPLACE BODY FROM FILE";
case set_from:
return "SET FROM";
}
return "UNKNOWN HEADER COMMAND";
}
static int
dbg_setreply(void *data, char *code, char *xcode, char *message)
{
if (code) {
printf("SET REPLY %s", code);
if (xcode)
printf(" %s", xcode);
if (message)
printf(" %s", message);
printf("\n");
}
return 0;
}
static void
dbg_msgmod(void *data, struct msgmod_closure *clos)
{
if (!clos)
printf("clearing msgmod list\n");
else
printf("%s %s: %s %u\n", msgmod_opcode_str(clos->opcode),
SP(clos->name), SP(clos->value), clos->idx);
}
static const char *
dbg_dict_getsym (void *data, const char *str)
{
return dict_getsym ((mu_assoc_t)data, str);
}
void
mailfromd_test(int argc, char **argv)
{
int i;
mu_assoc_t dict = NULL;
eval_environ_t env;
char *p, *end;
long n;
sfsistat status;
char *args[9] = {0,0,0,0,0,0,0,0,0};
dict_init(&dict);
env = create_environment(NULL,
dbg_dict_getsym, dbg_setreply, dbg_msgmod,
dict);
env_init(env);
xeval(env, smtp_state_begin);
env_init(env);
for (i = 0; i < argc; i++) {
if (p = strchr(argv[i], '=')) {
char *ident = argv[i];
*p++ = 0;
if (mu_isdigit(*ident) && *ident != 0
&& ident[1] == 0)
args[*ident - '0' - 1] = p;
else
dict_install(dict, argv[i], p);
}
}
for (i = state_parms[test_state].cnt; i--; ) {
switch (state_parms[test_state].types[i]) {
case dtype_string:
env_push_string(env, args[i] ? args[i] : "");
break;
case dtype_number:
if (args[i]) {
n = strtol(args[i], &end, 0);
if (*end)
mu_error(_("$%d is not a number"),
i+1);
} else
n = 0;
env_push_number(env, n);
break;
default:
abort();
}
}
test_message_data_init(env);
env_make_frame(env);
xeval(env, test_state);
env_leave_frame(env, state_parms[test_state].cnt);
env_final_gc(env);
status = environment_get_status(env);
env_init(env);
xeval(env, smtp_state_end);
printf("State %s: ", state_to_string(test_state));
print_stat(status);
printf("\n");
destroy_environment(env);
}
void
mailfromd_run(prog_counter_t entry_point, int argc, char **argv)
{
int rc, i;
mu_assoc_t dict = NULL;
eval_environ_t env;
dict_init(&dict);
env = create_environment(NULL,
dbg_dict_getsym, dbg_setreply, dbg_msgmod,
dict);
env_init(env);
test_message_data_init(env);
env_push_number(env, 0);
for (i = argc - 1; i >= 0; i--)
env_push_string(env, argv[i]);
env_push_number(env, argc);
env_make_frame0(env);
rc = eval_environment(env, entry_point);
env_final_gc(env);
rc = mf_c_val(env_get_reg(env), int);
destroy_environment(env);
exit(rc);
}
static struct tagtable {
char *name;
enum smtp_state tag;
} tagtable[] = {
{ "none", smtp_state_none },
{ "begin", smtp_state_begin },
{ "connect", smtp_state_connect },
{ "helo", smtp_state_helo },
{ "envfrom", smtp_state_envfrom },
{ "envrcpt", smtp_state_envrcpt },
{ "data", smtp_state_data },
{ "header", smtp_state_header },
{ "eoh", smtp_state_eoh },
{ "body", smtp_state_body },
{ "eom", smtp_state_eom },
{ "end", smtp_state_end },
};
enum smtp_state
string_to_state(const char *name)
{
struct tagtable *p;
for (p = tagtable; p < tagtable + sizeof tagtable/sizeof tagtable[0];
p++)
if (strcasecmp (p->name, name) == 0)
return p->tag;
return smtp_state_none;
}
const char *
state_to_string(enum smtp_state state)
{
if (state < sizeof tagtable/sizeof tagtable[0])
return tagtable[state].name;
abort();
}
static NODE *
_reverse(NODE *list, NODE **root)
{
NODE *next;
if (list->next == NULL) {
*root = list;
return list;
}
next = _reverse(list->next, root);
next->next = list;
list->next = NULL;
return list;
}
NODE *
reverse(NODE *in)
{
NODE *root;
if (!in)
return in;
_reverse(in, &root);
return root;
}
size_t
nodelistlength(NODE *p)
{
size_t len = 0;
for (; p; p = p->next)
len++;
return len;
}
static NODE *
create_asgn_node(struct variable *var, NODE *expr,
struct mu_locus_range const *loc)
{
NODE *node;
data_type_t t = node_type(expr);
if (t == dtype_unspecified) {
parse_error(_("unspecified value not ignored as it should be"));
return NULL;
}
node = alloc_node(node_type_asgn, loc);
node->v.asgn.var = var;
node->v.asgn.nframes = catch_nesting;
node->v.asgn.node = cast_to(var->type, expr);
return node;
}
NODE *
function_call(struct function *function, size_t count, NODE *subtree)
{
NODE *np = NULL;
if (count < function->parmcount - function->optcount) {
parse_error(_("too few arguments in call to `%s'"),
function->sym.name);
} else if (count > function->parmcount && !function->varargs) {
parse_error(_("too many arguments in call to `%s'"),
function->sym.name);
} else {
np = alloc_node(node_type_call, &yylloc);
np->v.call.func = function;
np->v.call.args = reverse(cast_arg_list(subtree,
function->parmcount,
function->parmtype,
0));
}
return np;
}
data_type_t
node_type(NODE *node)
{
switch (node->type) {
case node_type_string:
case node_type_symbol:
return dtype_string;
case node_type_number:
return dtype_number;
case node_type_if:
return dtype_unspecified;
case node_type_bin:
return dtype_number;
case node_type_un:
return dtype_number;
case node_type_builtin:
return node->v.builtin.builtin->rettype;
case node_type_concat:
return dtype_string;
case node_type_variable:
return node->v.var_ref.variable->type;
case node_type_arg:
return node->v.arg.data_type;
case node_type_argx:
return dtype_string;
case node_type_call:
return node->v.call.func->rettype;
case node_type_return:
if (node->v.node)
return node_type(node->v.node);
break;
case node_type_backref:
return dtype_string;
case node_type_cast:
return node->v.cast.data_type;
case node_type_offset:
return dtype_number;
case node_type_vaptr:
return dtype_number;
case node_type_result:
case node_type_header:
case node_type_asgn:
case node_type_regex:
case node_type_regcomp:
case node_type_catch:
case node_type_try:
case node_type_throw:
case node_type_echo:
case node_type_switch:
case node_type_funcdecl:
case node_type_progdecl:
case node_type_noop:
case node_type_next:
case node_type_break:
case node_type_loop:
case max_node_type:
break;
}
return dtype_unspecified;
}
NODE *
cast_to(data_type_t type, NODE *node)
{
NODE *np;
data_type_t ntype = node_type(node);
switch (ntype) {
case dtype_string:
case dtype_number:
if (type == ntype)
return node;
break;
case dtype_pointer:
if (type == ntype)
return node;
break;
case dtype_unspecified:
parse_error(_("cannot convert %s to %s"), type_to_string(ntype),
type_to_string(type));
return NULL;
default:
abort();
}
np = alloc_node(node_type_cast, &yylloc);
np->v.cast.data_type = type;
np->v.cast.node = node;
node->next = NULL;
return np;
}
NODE *
cast_arg_list(NODE *args, size_t parmc, data_type_t *parmtype, int disable_prom)
{
NODE *head = NULL, *tail = NULL;
while (args) {
NODE *next = args->next;
NODE *p;
data_type_t type;
if (parmc) {
type = *parmtype++;
parmc--;
} else if (disable_prom)
type = node_type(args);
else
type = dtype_string;
p = cast_to(type, args);
if (head)
tail->next = p;
else
head = p;
tail = p;
args = next;
}
return head;
}
void
add_xref(struct variable *var, struct mu_locus_range const *locus)
{
if (script_dump_xref) {
/* FIXME: either change type to mu_locus_point, or
change print_locus above */
struct mu_locus_range *elt = mu_zalloc(sizeof *elt);
if (!var->xref)
mu_list_create(&var->xref);
mu_locus_range_copy(elt, locus);
mu_list_append(var->xref, elt);
}
}
struct variable *
vardecl(const char *name, data_type_t type, storage_class_t sc,
struct mu_locus_range const *loc)
{
struct variable *var;
const struct constant *cptr;
if (!loc)
loc = &yylloc;
if (type == dtype_unspecified) {
parse_error(_("cannot define variable of unspecified type"));
return NULL;
}
var = variable_install(name);
if (var->type == dtype_unspecified) {
/* the variable has just been added: go straight to
initializing it */;
} else if (sc != var->storage_class) {
struct variable *vp;
switch (sc) {
case storage_extern:
parse_error(_("INTERNAL ERROR at %s:%d, declaring %s %s"),
__FILE__, __LINE__,
storage_class_str(sc), name);
abort();
case storage_auto:
if (var->storage_class == storage_param) {
parse_warning_locus(loc,
_("automatic variable `%s' "
"is shadowing a parameter"),
var->sym.name);
} else
parse_warning_locus(loc,
_("automatic variable `%s' "
"is shadowing a global"),
var->sym.name);
unregister_auto(var);
break;
case storage_param:
parse_warning_locus(loc,
_("parameter `%s' is shadowing a "
"global"),
name);
}
/* Do the shadowing */
vp = variable_replace(var->sym.name, NULL);
vp->shadowed = var;
var = vp;
} else {
switch (sc) {
case storage_extern:
if (var->type != type) {
parse_error_locus(loc,
_("redeclaring `%s' as different "
"data type"),
name);
parse_error_locus(&var->sym.locus,
_("this is the location of the "
"previous definition"));
return NULL;
}
break;
case storage_auto:
if (var->type != type) {
parse_error_locus(loc,
_("redeclaring `%s' as different "
"data type"),
name);
parse_error_locus(&var->sym.locus,
_("this is the location of the "
"previous definition"));
return NULL;
} else {
parse_error_locus(loc,
_("duplicate variable: %s"),
name);
return NULL;
}
break;
case storage_param:
parse_error_locus(loc, _("duplicate parameter: %s"),
name);
return NULL;
}
}
/* FIXME: This is necessary because constants can be
referred to the same way as variables. */
if (cptr = constant_lookup(name)) {
parse_warning_locus(loc,
_("variable name `%s' clashes with a constant name"),
name);
parse_warning_locus(&cptr->sym.locus,
_("this is the location of the "
"previous definition"));
}
var->type = type;
var->storage_class = sc;
switch (sc) {
case storage_extern:
add_xref(var, loc);
break;
case storage_auto:
case storage_param:
register_auto(var);
}
mu_locus_range_copy(&var->sym.locus, loc);
return var;
}
static int
cast_value(data_type_t type, struct value *value)
{
if (type != value->type) {
char buf[NUMERIC_BUFSIZE_BOUND];
char *p;
switch (type) {
default:
abort();
case dtype_string:
snprintf(buf, sizeof buf, "%ld", value->v.number);
value->v.literal = string_alloc(buf, strlen(buf));
break;
case dtype_number:
value->v.number = strtol(value->v.literal->text,
&p, 10);
if (*p) {
parse_error(_("cannot convert `%s' to number"),
value->v.literal->text);
return 1;
}
break;
}
value->type = type;
}
return 0;
}
static struct variable *
externdecl(const char *name, struct value *value,
struct mu_locus_range const *loc)
{
struct variable *var = vardecl(name, value->type, storage_extern, loc);
if (!var)
return NULL;
if (initialize_variable(var, value, loc))
return NULL;
return var;
}
struct deferred_decl {
struct deferred_decl *next;
struct literal *name;
struct value value;
struct mu_locus_range locus;
};
struct deferred_decl *deferred_decl;
void
defer_initialize_variable(const char *arg, const char *val,
struct mu_locus_range const *ploc)
{
struct deferred_decl *p;
struct literal *name = string_alloc(arg, strlen(arg));
for (p = deferred_decl; p; p = p->next)
if (p->name == name) {
parse_warning_locus(NULL, _("redefining variable %s"),
name->text);
p->value.type = dtype_string;
p->value.v.literal = string_alloc(val, strlen(val));
mu_locus_range_copy (&p->locus, ploc);
return;
}
p = mu_alloc(sizeof *p);
p->name = name;
p->value.type = dtype_string;
p->value.v.literal = string_alloc(val, strlen(val));
mu_locus_range_init (&p->locus);
mu_locus_range_copy (&p->locus, ploc);
p->next = deferred_decl;
deferred_decl = p;
}
static void
apply_deferred_init()
{
struct deferred_decl *p;
for (p = deferred_decl; p; p = p->next) {
struct variable *var = variable_lookup(p->name->text);
if (!var) {
mu_error(_(": warning: "
"no such variable: %s"),
p->name->text);
continue;
}
if (initialize_variable(var, &p->value, &p->locus))
parse_error_locus(&p->locus,
_("error initialising variable %s: incompatible types"),
p->name->text);
}
}
struct declvar {
struct declvar *next;
struct mu_locus_range locus;
struct variable *var;
struct value val;
};
static struct declvar *declvar;
void
set_poll_arg(struct poll_data *poll, int kw, NODE *expr)
{
switch (kw) {
case T_FOR:
poll->email = expr;
break;
case T_HOST:
poll->client_addr = expr;
break;
case T_AS:
poll->mailfrom = expr;
break;
case T_FROM:
poll->ehlo = expr;
break;
default:
abort();
}
}
int
initialize_variable(struct variable *var, struct value *val,
struct mu_locus_range const *locus)
{
struct declvar *dv;
if (cast_value(var->type, val))
return 1;
for (dv = declvar; dv; dv = dv->next)
if (dv->var == var) {
if (dv->locus.beg.mu_file) {
parse_warning_locus(locus,
_("variable `%s' already initialized"),
var->sym.name);
parse_warning_locus(&dv->locus,
_("this is the location of the "
"previous initialization"));
}
if (locus)
mu_locus_range_copy (&dv->locus, locus);
else
mu_locus_range_deinit (&dv->locus);
dv->val = *val;
return 0;
}
dv = mu_alloc(sizeof *dv);
dv->next = declvar;
dv->var = var;
mu_locus_range_init (&dv->locus);
if (locus)
mu_locus_range_copy (&dv->locus, locus);
dv->val = *val;
declvar = dv;
var->sym.flags |= SYM_INITIALIZED;
return 0;
}
void
ensure_initialized_variable(const char *name, struct value *val)
{
struct declvar *dv;
struct variable *var = variable_lookup(name);
if (!var) {
mu_error(_("INTERNAL ERROR at %s:%d: variable to be "
"initialized is not declared"),
__FILE__, __LINE__);
abort();
}
if (var->type != val->type)
mu_error(_("INTERNAL ERROR at %s:%d: variable to be "
"initialized has wrong type"),
__FILE__, __LINE__);
for (dv = declvar; dv; dv = dv->next)
if (dv->var == var)
return;
dv = mu_alloc(sizeof *dv);
mu_locus_range_init (&dv->locus);
dv->var = var;
dv->val = *val;
dv->next = declvar;
declvar = dv;
}
static int
_ds_variable_count_fun(void *sym, void *data)
{
struct variable *var = sym;
if ((var->sym.flags & (SYM_VOLATILE | SYM_REFERENCED))
&& !(var->sym.flags & SYM_PASSTOGGLE)) {
var->sym.flags |= SYM_PASSTOGGLE;
variable_count++;
if (var->type == dtype_string)
dataseg_reloc_count++;
if (var->sym.flags & SYM_PRECIOUS)
precious_count++;
}
return 0;
}
static int
_ds_variable_fill_fun(void *sym, void *data)
{
struct variable *var = sym;
if (var->sym.flags & SYM_PASSTOGGLE) {
var->sym.flags &= ~SYM_PASSTOGGLE;
struct variable ***vtabptr = data;
**vtabptr = var;
++*vtabptr;
}
return 0;
}
static int
_ds_reloc_fun(void *sym, void *data)
{
struct variable *var = sym;
size_t *pi = data;
if ((var->sym.flags & (SYM_VOLATILE | SYM_REFERENCED))
&& !(var->sym.flags & SYM_PASSTOGGLE)
&& var->type == dtype_string) {
var->sym.flags |= SYM_PASSTOGGLE;
dataseg_reloc[(*pi)++] = var->off;
}
return 0;
}
static int
_ds_literal_count_fun(void *sym, void *data)
{
struct literal *lit = sym;
size_t *offset = data;
if (!(lit->flags & SYM_VOLATILE) && (lit->flags & SYM_REFERENCED)) {
lit->off = *offset;
*offset += B2STACK(strlen(lit->text) + 1);
}
return 0;
}
static int
_ds_literal_copy_fun(void *sym, void *data)
{
struct literal *lit = sym;
if (!(lit->flags & SYM_VOLATILE) && (lit->flags & SYM_REFERENCED))
strcpy((char*)(dataseg + lit->off), lit->text);
return 0;
}
static int
vtab_comp(const void *a, const void *b)
{
const struct variable *vp1 = *(const struct variable **)a;
const struct variable *vp2 = *(const struct variable **)b;
if ((vp1->sym.flags & SYM_PRECIOUS)
&& !(vp2->sym.flags & SYM_PRECIOUS))
return 1;
else if ((vp2->sym.flags & SYM_PRECIOUS)
&& !(vp1->sym.flags & SYM_PRECIOUS))
return -1;
return 0;
}
static int
place_exc(const struct constant *cp, const struct literal *lit, void *data)
{
STKVAL *tab = data;
tab[cp->value.v.number] = (STKVAL) lit->off;
return 0;
}
static void
dataseg_layout()
{
struct declvar *dv;
size_t i;
struct switch_stmt *sw;
struct variable **vtab, **pvtab;
struct exmask *exmask;
/* Count used variables and estimate the number of relocations
needed */
dataseg_reloc_count = 0;
module_symtab_enumerate(namespace_variable,
_ds_variable_count_fun,
NULL);
/* Fill variable pointer array and make sure precious variables
occupy its bottom part */
vtab = mu_calloc(variable_count, sizeof(vtab[0]));
pvtab = vtab;
module_symtab_enumerate(namespace_variable,
_ds_variable_fill_fun,
&pvtab);
qsort(vtab, variable_count, sizeof(vtab[0]), vtab_comp);
/* Compute variable offsets. Offset 0 is reserved for NULL symbol */
for (i = 0; i < variable_count; i++) {
vtab[i]->off = i + 1;
if (vtab[i]->addrptr)
*vtab[i]->addrptr = vtab[i]->off;
}
/* Free the array */
free(vtab);
/* Mark literals used to initialize variables as referenced */
for (dv = declvar; dv; dv = dv->next) {
if ((dv->var->sym.flags & (SYM_VOLATILE | SYM_REFERENCED))
&& dv->var->type == dtype_string) {
dv->val.v.literal->flags |= SYM_REFERENCED;
}
}
datasize = variable_count + 1;
dvarsize = datasize - precious_count;
/* Count referenced literals and adjust the data size */
symtab_enumerate(stab_literal, _ds_literal_count_fun, &datasize);
/* Account for switch translation tables */
for (sw = switch_root; sw; sw = sw->next) {
sw->off = datasize;
datasize += sw->tabsize;
}
/* Account for exception masks */
for (exmask = exmask_root; exmask; exmask = exmask->next) {
exmask->off = datasize;
if (exmask->all) {
size_t i;
for (i = 0; i < exception_count; i++)
bitmask_set(&exmask->bm, i);
}
datasize += exmask->bm.bm_size + 1;
}
/* Account for exception name table */
datasize += exception_count;
/* Allocate data segment and relocation table */
dataseg = mu_calloc(datasize, sizeof(STKVAL));
dataseg_reloc = mu_calloc(dataseg_reloc_count, sizeof *dataseg_reloc);
/* Fill relocation table */
i = 0;
module_symtab_enumerate(namespace_variable, _ds_reloc_fun, &i);
/* Initialize variables */
for (dv = declvar; dv; dv = dv->next) {
if (dv->var->sym.flags & (SYM_VOLATILE | SYM_REFERENCED)) {
switch (dv->var->type) {
case dtype_string:
dataseg[dv->var->off] =
(STKVAL) dv->val.v.literal->off;
break;
case dtype_number:
dataseg[dv->var->off] =
(STKVAL) dv->val.v.number;
break;
default:
abort();
}
}
}
/* Place literals */
symtab_enumerate(stab_literal, _ds_literal_copy_fun, NULL);
/* Initialize exception masks */
for (exmask = exmask_root; exmask; exmask = exmask->next) {
size_t i, off = exmask->off;
dataseg[off++] = (STKVAL) exmask->bm.bm_size;
for (i = 0; i < exmask->bm.bm_size; i++)
dataseg[off++] = (STKVAL) exmask->bm.bm_bits[i++];
}
/* Initialize exception name table */
enumerate_exceptions(place_exc, dataseg + EXTABIND);
}
static int
_regex_compile_fun(void *sym, void *data)
{
struct literal *lit = sym;
if (lit->regex) {
struct sym_regex *rp;
for (rp = lit->regex; rp; rp = rp->next)
register_regex(rp);
}
return 0;
}
void
regex_layout()
{
symtab_enumerate(stab_literal, _regex_compile_fun, NULL);
finalize_regex();
}
static struct variable *auto_list;
static void
register_auto(struct variable *var)
{
var->next = auto_list;
auto_list = var;
}
static void
unregister_auto(struct variable *var)
{
struct variable *p = auto_list, *prev = NULL;
while (p) {
struct variable *next = p->next;
if (p == var) {
if (prev)
prev->next = next;
else
auto_list = next;
p->next = NULL;
return;
}
prev = p;
p = next;
}
}
/* FIXME: Redo shadowing via a separate table? */
static size_t
forget_autos(size_t nparam, size_t auto_count, size_t hidden_arg)
{
size_t param_count = 0;
struct variable *var = auto_list;
while (var) {
struct variable *next = var->next;
switch (var->storage_class) {
case storage_auto:
var->off = auto_count++;
break;
case storage_param:
var->off = nparam - param_count++;
var->ord = var->off - (hidden_arg ? 1 : 0) - 1;
break;
default:
abort();
}
while (var->storage_class != storage_extern) {
struct variable *shadowed = var->shadowed;
if (!shadowed) {
symtab_remove(TOP_MODULE_SYMTAB(namespace_variable),
var->sym.name);
break;
}
var->shadowed = NULL;
var = variable_replace(var->sym.name, shadowed);
}
var = next;
}
auto_list = NULL;
return auto_count;
}
const char *
storage_class_str(storage_class_t sc)
{
switch (sc) {
case storage_extern:
return "extern";
case storage_auto:
return "auto";
case storage_param:
return "param";
}
return "unknown?";
}
const char *
function_name()
{
switch (outer_context) {
case context_function:
return func->sym.name;
case context_handler:
return state_to_string(state_tag);
default:
return "";
}
}
NODE *
declare_function(struct function *func, struct mu_locus_range const *loc,
size_t nautos)
{
NODE *node = alloc_node(node_type_funcdecl, loc);
node->v.funcdecl.func = func;
node->v.funcdecl.auto_count = nautos;
node->v.funcdecl.tree = func->node;
return node;
}
NODE *
create_node_variable(struct variable *var, struct mu_locus_range const *locus)
{
NODE *node = alloc_node(node_type_variable, locus);
node->v.var_ref.variable = var;
node->v.var_ref.nframes = catch_nesting;
return node;
}
NODE *
create_node_argcount(struct mu_locus_range const *locus)
{
NODE *node;
if (outer_context == context_function) {
if (func->optcount || func->varargs) {
node = alloc_node(node_type_arg, locus);
node->v.arg.data_type = dtype_number;
node->v.arg.number = 1;
} else {
node = alloc_node(node_type_number, locus);
node->v.number = parminfo[outer_context].parmcount();
}
} else {
node = alloc_node(node_type_number, locus);
node->v.number = parminfo[outer_context].parmcount();
}
return node;
}
NODE *
create_node_arg(long num, struct mu_locus_range const *locus)
{
NODE *node;
if (inner_context == context_function && func->varargs)
;
else if (num > PARMCOUNT())
parse_error(_("argument number too high"));
node = alloc_node(node_type_arg, locus);
node->v.arg.data_type = PARMTYPE(num);
node->v.arg.number = num;
if (inner_context == context_function)
node->v.arg.number += FUNC_HIDDEN_ARGS(func);
return node;
}
NODE *
create_node_symbol(struct literal *lit, struct mu_locus_range const *locus)
{
NODE *node;
register_macro(state_tag, lit->text);
node = alloc_node(node_type_symbol, locus);
node->v.literal = lit;
return node;
}
NODE *
create_node_backref(long num, struct mu_locus_range const *locus)
{
NODE *node = alloc_node(node_type_backref, locus);
node->v.number = num;
return node;
}