diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-04-01 15:02:37 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-04-01 15:02:37 +0000 |
commit | f7c9cc679520834d36acc7a1fc666f2a052fce69 (patch) | |
tree | e112e12dd7db2620b5fcfa761c4d37977877eec6 | |
parent | 02cc930297b1c3b375e35032e47c1e66f4e9e712 (diff) | |
download | mailfromd-f7c9cc679520834d36acc7a1fc666f2a052fce69.tar.gz mailfromd-f7c9cc679520834d36acc7a1fc666f2a052fce69.tar.bz2 |
Implement loop statement
git-svn-id: file:///svnroot/mailfromd/trunk@1337 7a8a7f39-df28-0410-adc6-e0d955640f24
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | NEWS | 50 | ||||
-rw-r--r-- | doc/mailfromd.texi | 45 | ||||
-rw-r--r-- | etc/mailfromd.rc | 8 | ||||
-rw-r--r-- | src/drivers.c | 244 | ||||
-rw-r--r-- | src/gram.y | 161 | ||||
-rw-r--r-- | src/lex.l | 28 | ||||
-rw-r--r-- | src/mailfromd.h | 20 | ||||
-rw-r--r-- | src/prog.h | 5 | ||||
-rw-r--r-- | src/stack.c | 46 |
10 files changed, 573 insertions, 39 deletions
@@ -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. @@ -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) @@ -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); @@ -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" @@ -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; } - |