aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--NEWS50
-rw-r--r--doc/mailfromd.texi45
-rw-r--r--etc/mailfromd.rc8
-rw-r--r--src/drivers.c244
-rw-r--r--src/gram.y161
-rw-r--r--src/lex.l28
-rw-r--r--src/mailfromd.h20
-rw-r--r--src/prog.h5
-rw-r--r--src/stack.c46
10 files changed, 573 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index 25634f6d..ddba3002 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
2007-04-01 Sergey Poznyakoff <gray@gnu.org.ua>
+ * src/lex.l, src/stack.c, src/gram.y, src/drivers.c,
+ src/mailfromd.h, src/prog.h, doc/mailfromd.texi, NEWS: Implement
+ loop statement.
+ * etc/mailfromd.rc: Replace `next' with `pass'
+
* src/prog.c (s_off_cmp): Revert sorting order to match that of
heap growth.
diff --git a/NEWS b/NEWS
index c6205d06..abebdfc8 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,56 @@ Please send mailfromd bug reports to <bug-mailfromd@gnu.org.ua>
Version 3.1.91, SVN
+* next and pass
+
+Use `pass' instead of `next'.
+
+The `next' keyword has changed its semantics: it is now used to
+resume the next iteration of the enclosing loop statement (see
+below).
+
+For compatibility with the previous versions, its use outside of a
+loop statement is still allowed, but a warning is issued. You are
+enouraged to replace all occurrances of `next' in your confifuration
+scripts with `pass'.
+
+* Loop
+
+The loop statement is implemented. Its syntax is:
+
+loop [name]
+ [for <stmt>,] [while <stmt>,] [<stmt>]
+do
+ ...
+done [while <stmt>]
+
+* break and next
+
+The `break' statement exits from the enclosing loop.
+
+The `next' statement resumes the next iteration of the enclosing loop
+statement.
+
+Both statements take an optional argument specifying the identifier
+(name) of the loop to break from (or continue), this allows to build
+complex iterations consisting of nested loops. For example, in this
+code (line numbers added for clarity):
+
+ 1 loop outer for set i 1, while %i < %N
+ 2 do
+ 3 ...
+ 4 loop for set j 1, while %j < %i
+ 5 do
+ 6 if foo(%j)
+ 7 break outer
+ 8 fi
+ 9 done
+10 done
+11 accept
+
+if the call to `foo' in line 6 returns true, the control is immediately passed
+to `accept' in line 11.
+
* Resizable stack
The runtime stack of the MFL grows automatically as the need arises.
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 0f97f1f5..f4a90b6f 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -200,7 +200,7 @@ Statements
* Actions:: Actions control the handling of the mail.
* Assignments::
-* Next::
+* Pass::
* Echo::
@c Return statement::
@c Conditionals
@@ -443,7 +443,7 @@ user. Depending on this value a decision can be made whether to
accept or to refuse the message.
For this feature to work, the package must be configured with
-@acronym{DBM} support (see next chapter).
+@acronym{DBM} support (see the next chapter).
@node Building, Tutorial, Intro, Top
@chapter Building the Package
@@ -756,6 +756,9 @@ to the top of your script:
@FIXME-xref{}
@item
+ Replace all occurrences of @code{next} with @code{pass}.
+
+@item
If your code uses function @code{match_cidr}, add the following line
to the top of your script:
@@ -2561,6 +2564,7 @@ amount of white-space characters (i.e. spaces, tabulations or newlines).
* Expressions:: Expressions.
* Statements::
* Conditionals:: Conditional Statements.
+* Loops:: Loop Statements.
* Exceptions:: Exceptional Conditions and their Handling.
* Polling:: Sender Verification Tests.
* Filter Script Example:: A Working Filter Script Explained.
@@ -6320,7 +6324,7 @@ program.
@menu
* Actions:: Actions control the handling of the mail.
* Assignments::
-* Next::
+* Pass::
* Echo::
@c Return statement::
@c Conditionals
@@ -6468,17 +6472,17 @@ done
@end group
@end smallexample
-@node Next
-@subsection The @code{next} statement
-@kwindex next
- The @code{next} statement has no effect. It should be used when
+@node Pass
+@subsection The @code{pass} statement
+@kwindex pass
+ The @code{pass} statement has no effect. It should be used when
no statement is needed but the language syntax requires one:
@smallexample
@group
on poll $f do
when success:
- next
+ pass
when not_found or failure:
reject 550
done
@@ -6636,6 +6640,11 @@ type of comparisons used in @code{case} branches: for numeric types,
numeric equality will be used, whereas for string types, string
equality is used.
+@node Loops
+@section Loop Statements
+@UNREVISED{}
+
+
@node Exceptions
@section Exceptional Conditions
@cindex exceptions, defined
@@ -7271,7 +7280,7 @@ something similar to an IP are subject to strict checking:
".*[0-9]@{1,3@}[-.][0-9]@{1,3@}[-.][0-9]@{1,3@}[-.][0-9]@{1,3@}.*"
on poll host $client_addr for $f do
when success:
- next
+ pass
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
@@ -7288,7 +7297,7 @@ will greylist this message in @code{envrcpt} handler:
@group
elif $f mx fnmatches "*.yahoo.com"
or $f mx fnmatches "*.namaeserver.com"
- next
+ pass
@end group
@end smallexample
@@ -7300,7 +7309,7 @@ it is verified by the standard procedure:
else
on poll $f do
when success:
- next
+ pass
when not_found or failure:
reject 550 5.1.0 "Sender validity not confirmed"
when temp_failure:
@@ -7368,7 +7377,8 @@ words:
@item __version__
@item accept
@item add
-@item and
+@item and
+@item break
@item case
@item catch
@item const
@@ -7383,14 +7393,17 @@ words:
@item else
@item fi
@item fnmatches
+@item for
@item func
-@item if
+@item if
+@item loop
@item matches
@item next
@item not
@item number
@item on
-@item or
+@item or
+@item pass
@item prog
@item reject
@item replace
@@ -7401,7 +7414,8 @@ words:
@item switch
@item tempfail
@item throw
-@item when
+@item when
+@item while
@end itemize
Several keywords are context-dependent: @code{mx} is a keyword if it
@@ -7410,7 +7424,6 @@ are keywords in @code{on} context:
@itemize
@item as
-@item for
@item from
@item host
@item poll
diff --git a/etc/mailfromd.rc b/etc/mailfromd.rc
index 045b0582..e1323f14 100644
--- a/etc/mailfromd.rc
+++ b/etc/mailfromd.rc
@@ -47,27 +47,27 @@ done
prog envfrom
do
if $f = ""
- next
+ pass
elif hostname ${client_addr} matches ".*(adsl|sdsl|hdsl|ldsl|dialin|dialup|ppp|dhcp|dynamic).*"
reject 550 5.7.1 "Use your SMTP relay"
elif hostname ${client_addr} matches ".*-[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}.*"
on poll host ${client_addr} for $f do
when success:
- next
+ pass
when not_found or failure:
reject 550 5.1.0 cachestr() "Sender validity not confirmed"
when temp_failure:
tempfail 450 4.1.0 "Try again later"
done
elif relayed hostname ${client_addr}
- next
+ pass
elif $f mx fnmatches "*.yahoo.com"
or $f mx fnmatches "*.namaeserver.com"
set need_greylist 1
else
on poll $f do
when success:
- next
+ pass
when not_found or failure:
reject 550 5.1.0 cachestr() "Sender validity not confirmed"
when temp_failure:
diff --git a/src/drivers.c b/src/drivers.c
index 440626da..0868f110 100644
--- a/src/drivers.c
+++ b/src/drivers.c
@@ -1819,6 +1819,250 @@ code_type_switch(NODE *node, struct locus **old_locus)
}
+/* Loop nesting stack */
+struct loop_stack {
+ struct literal *ident;
+ prog_counter_t *begjmp;
+ prog_counter_t *endjmp;
+};
+
+static mf_stack_t loop_stack;
+
+static int
+_find_loop(void *item, void *data)
+{
+ struct loop_stack *ent = item;
+ struct loop_stack *s = data;
+ if (ent->ident == s->ident) {
+ *s = *ent;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+find_loop(struct literal *lit, struct loop_stack *pret)
+{
+ if (!loop_stack)
+ return 0;
+ else if (!lit)
+ return mf_stack_peek(loop_stack, 0, pret) == 0;
+ else {
+ int rc;
+ struct loop_stack ent;
+ if (!pret)
+ pret = &ent;
+ pret->ident = lit;
+ return mf_stack_enumerate_desc(loop_stack, _find_loop, pret);
+ }
+}
+
+int
+within_loop(struct literal *lit)
+{
+ return find_loop(lit, NULL);
+}
+
+void
+enter_loop(struct literal *lit, prog_counter_t *begptr, prog_counter_t *endptr)
+{
+ struct loop_stack ent;
+ if (!loop_stack)
+ loop_stack = mf_stack_create(sizeof(struct loop_stack), 0);
+ ent.begjmp = begptr;
+ ent.endjmp = endptr;
+ ent.ident = lit;
+ mf_stack_push(loop_stack, &ent);
+}
+
+void
+leave_loop()
+{
+ mf_stack_pop(loop_stack, NULL);
+}
+
+
+/* type next */
+void
+print_type_next(NODE *node, int level)
+{
+ print_level(level);
+ printf("NEXT");
+ if (node->v.literal)
+ printf(" %s", node->v.literal->text);
+ printf("\n");
+}
+
+void
+code_type_next(NODE *node, struct locus **old_locus)
+{
+ struct loop_stack ent;
+
+ MARK_LOCUS();
+
+ if (find_loop(node->v.literal, &ent) == 0) {
+ parse_error_locus(&node->locus,
+ "INTERNAL ERROR at %s:%d: cannot find loop",
+ __FILE__, __LINE__);
+ abort();
+ }
+
+ code_op(opcode_jmp);
+ *ent.begjmp = code_immediate((void*) *ent.begjmp);
+}
+
+
+/* type break */
+void
+print_type_break(NODE *node, int level)
+{
+ print_level(level);
+ printf("BREAK");
+ if (node->v.literal)
+ printf(" %s", node->v.literal->text);
+ printf("\n");
+}
+
+void
+code_type_break(NODE *node, struct locus **old_locus)
+{
+ struct loop_stack ent;
+
+ MARK_LOCUS();
+
+ if (find_loop(node->v.literal, &ent) == 0) {
+ parse_error_locus(&node->locus,
+ "INTERNAL ERROR at %s:%d: cannot find loop",
+ __FILE__, __LINE__);
+ abort();
+ }
+
+ code_op(opcode_jmp);
+ *ent.endjmp = code_immediate((void*) *ent.endjmp);
+}
+
+
+/* type loop */
+void
+print_type_loop(NODE *node, int level)
+{
+ print_level(level);
+ printf("LOOP");
+ if (node->v.loop.ident)
+ printf(" %s", node->v.loop.ident->text);
+ printf(":\n");
+ if (node->v.loop.for_stmt) {
+ print_level(level);
+ printf("FOR ");
+ print_node_list(node->v.loop.for_stmt, level + 1);
+ }
+ if (node->v.loop.beg_while) {
+ print_level(level);
+ printf("BEG_WHILE ");
+ print_node_list(node->v.loop.beg_while, level + 1);
+ }
+ if (node->v.loop.end_while) {
+ print_level(level);
+ printf("BEG_WHILE ");
+ print_node_list(node->v.loop.end_while, level + 1);
+ }
+ if (node->v.loop.stmt) {
+ print_level(level);
+ printf("BY ");
+ print_node_list(node->v.loop.stmt, level + 1);
+ }
+
+ print_level(level);
+ print_node_list(node->v.loop.body, level + 1);
+}
+
+void
+mark_type_loop(NODE *node)
+{
+ mark(node->v.loop.beg_while);
+ mark(node->v.loop.stmt);
+ mark(node->v.loop.for_stmt);
+ mark(node->v.loop.end_while);
+ mark(node->v.loop.body);
+}
+
+void
+optimize_type_loop(NODE *node)
+{
+ optimize(node->v.loop.beg_while);
+ if (node->v.loop.beg_while) {
+ if (node->v.loop.beg_while->type == node_type_number) {
+ if (node->v.loop.beg_while->v.number)
+ node->v.loop.beg_while = NULL;
+ else
+ node->type = node_type_noop;
+ /* FIXME: free subtrees?? */
+ return;
+ } else if (node->v.loop.beg_while->type == node_type_string) {
+ if (node->v.loop.beg_while->v.literal->text[0])
+ node->v.loop.beg_while = NULL;
+ else
+ node->type = node_type_noop;
+ return;
+ }
+ }
+
+ optimize(node->v.loop.stmt);
+ optimize(node->v.loop.for_stmt);
+ optimize(node->v.loop.end_while);
+ optimize(node->v.loop.body);
+}
+
+void
+code_type_loop(NODE *node, struct locus **old_locus)
+{
+ /* FIXME */
+ /*
+ .
+ . <for_stmt>
+ .
+ L_begin:
+ [<beg_while>
+ bz L_end]
+ <body>
+ [<end_while>
+ bz L_end]
+ [<stmt>]
+ jmp L_begin
+ L_end:
+ */
+ prog_counter_t begin, end, begjmp = 0, endjmp = 0;
+
+ MARK_LOCUS();
+
+ enter_loop(node->v.loop.ident, &begjmp, &endjmp);
+
+ traverse_tree(node->v.loop.for_stmt);
+ begin = code_get_counter();
+ if (node->v.loop.beg_while) {
+ code_node(node->v.loop.beg_while);
+ code_op(opcode_bz);
+ endjmp = code_immediate((void*)endjmp);
+ }
+
+ traverse_tree(node->v.loop.body);
+
+ if (node->v.loop.end_while) {
+ code_node(node->v.loop.end_while);
+ code_op(opcode_bz);
+ endjmp = code_immediate((void*)endjmp);
+ }
+
+ traverse_tree(node->v.loop.stmt);
+ code_op(opcode_jmp);
+ code_immediate((void*)(begin - code_get_counter() - 1));
+ end = code_get_counter();
+
+ jump_fixup(begjmp, begin);
+ jump_fixup(endjmp, end);
+}
+
+
/* type backref */
void
print_type_backref(NODE *node, int level)
diff --git a/src/gram.y b/src/gram.y
index e677e9f3..0ab3ed30 100644
--- a/src/gram.y
+++ b/src/gram.y
@@ -182,6 +182,11 @@ struct parminfo {
#define PARMCOUNT() parminfo[inner_context].parmcount()
#define PARMTYPE(n) parminfo[inner_context].parmtype(n)
+
+/* Loop stack */
+
+
+
data_type_t
string_to_type(const char *s)
{
@@ -310,15 +315,17 @@ static void register_macro(enum smtp_state tag, const char *macro);
struct case_stmt *head, *tail;
} case_list ;
struct case_stmt *case_stmt;
+ struct loop_node loop;
};
%token <locus> ACT_ACCEPT ACT_REJECT ACT_TEMPFAIL ACT_CONTINUE ACT_DISCARD
%token <locus> ADD REPLACE DELETE
%token <locus> PROG IF FI ELSE ELIF
-%token <locus> ON HOST FOR FROM AS DO DONE POLL MATCHES FNMATCHES
+%token <locus> ON HOST FROM AS DO DONE POLL MATCHES FNMATCHES
%token <locus> MXMATCHES MXFNMATCHES
-%token <locus> WHEN NEXT SET CATCH THROW KW_ECHO RETURNS RETURN FUNC
+%token <locus> WHEN PASS SET CATCH THROW KW_ECHO RETURNS RETURN FUNC
%token <locus> SWITCH CASE DEFAULT CONST
+%token <locus> FOR LOOP WHILE BREAK NEXT
%token <literal> STRING CODE XCODE
%token <literal> SYMBOL VARIABLE IDENTIFIER
%token <number> ARG NUMBER BACKREF
@@ -344,14 +351,16 @@ static void register_macro(enum smtp_state tag, const char *macro);
%type <node> decl stmt condition action if_cond else_cond on_cond atom
funcall proccall expr common_expr simp_expr atom_expr
asgn catch throw return case_cond autodcl constdecl
+ loopstmt opt_while jumpstmt
%type <stmtlist> stmtlist decllist
%type <ret> triplet maybe_triplet
%type <poll> pollstmt pollarglist
-%type <pollarg> pollarg
+%type <pollarg> pollarg loop_parm
+%type <loop> opt_loop_parms loop_parm_list
%type <number> number
%type <arglist> arglist
%type <var> variable
-%type <literal> string
+%type <literal> string opt_ident loop_ident
%type <state> state_ident
%type <matchtype> matches fnmatches
%type <type> retdecl
@@ -362,7 +371,7 @@ static void register_macro(enum smtp_state tag, const char *macro);
%type <valist_list> valist catchlist
%type <case_list> cond_branches branches
%type <case_stmt> cond_branch branch
-%type <locus> on
+%type <locus> on
%%
@@ -652,6 +661,8 @@ stmt : condition
| return
| proccall
| constdecl
+ | loopstmt
+ | jumpstmt
;
asgn : SET IDENTIFIER expr
@@ -735,7 +746,7 @@ action : ACT_ACCEPT maybe_triplet
memset(&$$->v.ret, 0, sizeof $$->v.ret);
$$->v.ret.stat = SMFIS_DISCARD;
}
- | NEXT
+ | PASS
{
$$ = alloc_node(node_type_noop, &$1);
}
@@ -994,6 +1005,144 @@ number : NUMBER
}
;
+
+/* Loop statements */
+
+loopstmt : LOOP loop_ident opt_loop_parms DO stmtlist 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;
+ }
+ | IDENTIFIER
+ ;
+
+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 FOR:
+ $$.for_stmt = $1.expr;
+ break;
+
+ case WHILE:
+ $$.beg_while = $1.expr;
+ break;
+
+ default:
+ abort();
+ }
+ }
+ | loop_parm_list ',' loop_parm
+ {
+ switch ($3.kw) {
+ case 0:
+ if ($$.stmt)
+ parse_error("loop increment duplicated");
+ $$.stmt = $3.expr;
+ break;
+
+ case FOR:
+ if ($$.for_stmt)
+ parse_error("for statement duplicated");
+ $$.for_stmt = $3.expr;
+ break;
+
+ case WHILE:
+ if ($$.beg_while)
+ parse_error("while statement duplicated");
+ $$.beg_while = $3.expr;
+ break;
+
+ default:
+ abort();
+ }
+ }
+ ;
+
+loop_parm : stmt
+ {
+ $$.kw = 0;
+ $$.expr = $1;
+ }
+ | FOR stmt
+ {
+ $$.kw = FOR;
+ $$.expr = $2;
+ }
+ | WHILE expr
+ {
+ $$.kw = WHILE;
+ $$.expr = $2;
+ }
+ ;
+
+opt_while : /* empty */
+ {
+ $$ = NULL;
+ }
+ | WHILE expr
+ {
+ $$ = $2;
+ }
+ ;
+
+jumpstmt : BREAK opt_ident
+ {
+ if (!within_loop($2)) {
+ if ($2)
+ parse_error("no such loop: %s", $2->text);
+ parse_error("`break' used outside `loop'");
+ YYERROR;
+ }
+ $$ = alloc_node(node_type_break, &$1);
+ $$->v.literal = $2;
+ }
+ | NEXT opt_ident
+ {
+ if (!within_loop($2)) {
+ if ($2) {
+ parse_error("no such loop: %s", $2->text);
+ parse_error("`next' used outside `loop'");
+ YYERROR;
+ } else {
+ parse_warning("`next' is used outside `loop'; did you mean `pass'?");
+ $$ = alloc_node(node_type_noop, &$1);
+ }
+ }
+ $$ = alloc_node(node_type_next, &$1);
+ $$->v.literal = $2;
+ }
+ ;
+
+
+/* Expressions */
expr : NOT expr
{
$$ = alloc_node(node_type_un, &$1);
diff --git a/src/lex.l b/src/lex.l
index aef5ac68..7acaab9c 100644
--- a/src/lex.l
+++ b/src/lex.l
@@ -287,7 +287,6 @@ pop_source()
}
/* Restore previous context */
locus = context_stack->locus;
- locus.line++; /* #include rule did not increment it */
if (yy_flex_debug)
fprintf(stderr, "Resuming file `%s' at line %lu\n",
locus.file, (unsigned long) locus.line);
@@ -305,18 +304,21 @@ parse_include(int once)
{
int argc;
char **argv;
+ char *tmp = NULL;
+ char *p = NULL;
if (mu_argcv_get(yytext, "", NULL, &argc, &argv))
parse_error("cannot parse include line");
else if (argc != 2)
parse_error("invalid include statement");
else {
- char *tmp = NULL;
- char *p = argv[1];
- size_t len = strlen(p);
+ size_t len;
int allow_cwd;
static char *cwd = ".";
+ p = argv[1];
+ len = strlen(p);
+
if (p[0] == '<' && p[len - 1] == '>') {
allow_cwd = 0;
p[len - 1] = 0;
@@ -356,11 +358,12 @@ parse_include(int once)
p = tmp = fd.buf;
}
- if (p)
- push_source(p, once);
-
- free(tmp);
}
+
+ locus.line++;
+ if (p)
+ push_source(p, once);
+ free(tmp);
mu_argcv_free(argc, argv);
}
@@ -428,7 +431,7 @@ MACRO {LOCUS}|{VMACRO}|__statedir__
^[ \t]*#[ \t]*pragma[ \t].*/\n parse_pragma(yytext);
^[ \t]*#[ \t]*include_once[ \t].*\n parse_include(1);
^[ \t]*#[ \t]*include[ \t].*\n parse_include(0);
-^[ \t]*#[ \t]*error[ \t].*\n parse_error("%s", yytext);
+^[ \t]*#[ \t]*error[ \t].*\n { parse_error("%s", yytext); locus.line++; }
/* End-of-line comments */
#.*\n { locus.line++; }
#.* /* end-of-file comment */;
@@ -471,10 +474,15 @@ string { yylval.type = dtype_string; return TYPE; }
number { yylval.type = dtype_number; return TYPE; }
const return keyword(CONST);
throw return keyword(THROW);
+loop return keyword(LOOP);
+while return keyword(WHILE);
+for return keyword(FOR);
+break return keyword(BREAK);
+pass return keyword(PASS);
+
{MACRO} { return builtin_macro(yytext); }
<ONBLOCK>poll return keyword(POLL);
<ONBLOCK>host return keyword(HOST);
-<ONBLOCK>for return keyword(FOR);
<ONBLOCK>as return keyword(AS);
<ONBLOCK>from return keyword(FROM);
/* Variables */
diff --git a/src/mailfromd.h b/src/mailfromd.h
index 11af559f..171ed254 100644
--- a/src/mailfromd.h
+++ b/src/mailfromd.h
@@ -483,6 +483,15 @@ struct regcomp_data {
size_t regind;
};
+struct loop_node {
+ struct literal *ident;
+ NODE *stmt;
+ NODE *for_stmt;
+ NODE *beg_while;
+ NODE *end_while;
+ NODE *body;
+};
+
#include "node-type.h"
/* Parse tree node */
@@ -515,6 +524,7 @@ struct node {
struct funcdecl funcdecl;
struct regcomp_data regcomp_data;
struct sym_regex *regex;
+ struct loop_node loop;
} v;
};
@@ -820,7 +830,17 @@ mf_stack_t mf_stack_create(size_t elsize, size_t count);
void mf_stack_destroy(mf_stack_t *pstk);
void mf_stack_push(mf_stack_t stk, void *item);
int mf_stack_pop(mf_stack_t stk, void *ptr);
+int mf_stack_peek(mf_stack_t stk, size_t n, void *ptr);
+
+size_t mf_stack_count(mf_stack_t stk);
+typedef int (*mf_stack_enumerator) (void *item, void *data);
+
+int mf_stack_enumerate_desc(mf_stack_t stk, mf_stack_enumerator fun,
+ void *data);
+int mf_stack_enumerate_asc(mf_stack_t stk, mf_stack_enumerator fun,
+ void *data);
+
/* DBM support */
#include "mu_dbm.h"
diff --git a/src/prog.h b/src/prog.h
index e16c35ed..6fbf9486 100644
--- a/src/prog.h
+++ b/src/prog.h
@@ -60,6 +60,11 @@ void scan_code(prog_counter_t start, prog_counter_t end,
void (*fun)(prog_counter_t pc, struct optab *op, void *d),
void *data);
+int within_loop(struct literal *lit);
+void enter_loop(struct literal *lit,
+ prog_counter_t *begptr, prog_counter_t *endptr);
+void leave_loop(void);
+
extern instr_t *prog;
extern unsigned error_count;
extern unsigned long prog_trace_option;
diff --git a/src/stack.c b/src/stack.c
index 8a4a508a..1a05e48c 100644
--- a/src/stack.c
+++ b/src/stack.c
@@ -31,7 +31,7 @@ struct mf_stack {
size_t elcount;
};
-#define STACK_PTR(s, n) (void*)((s)->base + n * (s)->elsize)
+#define STACK_PTR(s, n) (void*)((s)->base + (n) * (s)->elsize)
mf_stack_t
mf_stack_create(size_t elsize, size_t count)
@@ -74,8 +74,48 @@ mf_stack_pop(mf_stack_t stk, void *ptr)
if (stk->tos == 0)
return 1;
--stk->tos;
- memcpy(ptr, STACK_PTR(stk, stk->tos), stk->elsize);
+ if (ptr)
+ memcpy(ptr, STACK_PTR(stk, stk->tos), stk->elsize);
+ return 0;
+}
+
+int
+mf_stack_peek(mf_stack_t stk, size_t n, void *ptr)
+{
+ if (stk->tos == 0 || n + 1 > stk->tos)
+ return 1;
+ if (ptr)
+ memcpy(ptr, STACK_PTR(stk, stk->tos - 1 - n), stk->elsize);
+ return 0;
+}
+
+size_t
+mf_stack_count(mf_stack_t stk)
+{
+ return stk->tos;
+}
+
+int
+mf_stack_enumerate_desc(mf_stack_t stk, mf_stack_enumerator fun, void *data)
+{
+ size_t i;
+ for (i = stk->tos; i > 0; i--) {
+ int rc = fun(STACK_PTR(stk, i - 1), data);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+int
+mf_stack_enumerate_asc(mf_stack_t stk, mf_stack_enumerator fun, void *data)
+{
+ size_t i;
+ for (i = 0; i < stk->tos; i++) {
+ int rc = fun(STACK_PTR(stk, i), data);
+ if (rc)
+ return rc;
+ }
return 0;
}
-

Return to:

Send suggestions and report system problems to the System administrator.