summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2009-05-11 18:48:14 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2009-05-11 18:48:14 (GMT)
commitf8323650568b0031f0ec4c50cde417cb10a48c56 (patch) (unidiff)
treeb0007c30366860dc079a7bf8828d254b7486630f
parentffd2cf189d926abe00de0a79f292f1ea69d02aac (diff)
downloadmailfromd-f8323650568b0031f0ec4c50cde417cb10a48c56.tar.gz
mailfromd-f8323650568b0031f0ec4c50cde417cb10a48c56.tar.bz2
Implement functional notation for reply actions
* NEWS, doc/mailfromd.texi: Update. * mfd/drivers.c (print_type_result, mark_type_result) (optimize_type_result, code_type_result): Rewrite. * mfd/gram.y: Rewrite action rules. * mfd/lex.l (CODE,XCODE): Remove. * mfd/opcodes (RESULT): Takes one immediate parameter. (instr_result): Get arguments from stack.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--NEWS19
-rw-r--r--doc/mailfromd.texi86
-rw-r--r--mfd/drivers.c115
-rw-r--r--mfd/gram.y131
-rw-r--r--mfd/lex.l4
-rw-r--r--mfd/mailfromd.h8
-rw-r--r--mfd/opcodes2
-rw-r--r--mfd/prog.c10
8 files changed, 276 insertions, 99 deletions
diff --git a/NEWS b/NEWS
index b50d87a..90b7a88 100644
--- a/NEWS
+++ b/NEWS
@@ -1,2 +1,2 @@
1Mailfromd NEWS -- history of user-visible changes. 2009-05-10 1Mailfromd NEWS -- history of user-visible changes. 2009-05-11
2Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff 2Copyright (C) 2005, 2006, 2007, 2008, 2009 Sergey Poznyakoff
@@ -26,2 +26,19 @@ each stage is limited (Mailfromd manual, section 11.1.2).
26 26
27* Reject and tempfail actions: Functional notation
28
29The reply actions `reject' and `tempfail' allow functional notation,
30i.e. their arguments can be supplied as to a function:
31
32 reject(550, 5.7.7, "IP address does not resolve")
33
34An important feature of this notation is that all three arguments are
35MFL expressions, which means that you can now compute the reply codes
36at run time:
37
38 reject(550 + %n, "5.7." %x, "Transaction rejected")
39
40An argument can be omitted, in which case the default value is used, e.g.:
41
42 reject(550 + %n, , "Transaction rejected")
43
27* New functions 44* New functions
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index 3d2e43c..090222e 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -1357,7 +1357,12 @@ code: @code{continue}, @code{accept}, @code{reject}, @code{discard},
1357and @code{tempfail}. Among these, @code{reject} and @code{discard} 1357and @code{tempfail}. Among these, @code{reject} and @code{discard}
1358can optionally take one to three arguments. The first 1358can optionally take one to three arguments. There are two ways of
1359argument is a three-digit @acronym{RFC} 2821 reply code. It must begin with 1359supplying the arguments.
1360@samp{5} for @code{reject} and with @samp{4} for @code{tempfail}. If 1360
1361two arguments are supplied, the second argument must be either an 1361In the first form, called @dfn{literal} or @dfn{traditional} notation,
1362@dfn{extended reply code} (@acronym{RFC} 1893/2034) or a textual string to be 1362the arguments are supplied as additional words after the action name,
1363separated by whitespace. The first argument is a three-digit
1364@acronym{RFC} 2821 reply code. It must begin with @samp{5} for
1365@code{reject} and with @samp{4} for @code{tempfail}. If two arguments
1366are supplied, the second argument must be either an @dfn{extended
1367reply code} (@acronym{RFC} 1893/2034) or a textual string to be
1363returned along with the @acronym{SMTP} reply. Finally, if all three 1368returned along with the @acronym{SMTP} reply. Finally, if all three
@@ -1366,3 +1371,3 @@ code and the third one must supply the textual string. The following
1366examples illustrate all possible ways of using the @code{reject} 1371examples illustrate all possible ways of using the @code{reject}
1367statement: 1372statement in literal notation:
1368 1373
@@ -1381,2 +1386,21 @@ Please note the quotes around the textual string.
1381 1386
1387Another form for these action is called @dfn{functional} notation,
1388because it resembles the function syntax. When used in this form, the
1389action word is followed by a parenthesized group of exactly three
1390arguments, separated by commas. The meaning and ordering of the
1391argument is the same as in literal form. Any of three arguments may
1392be absent, in which case it will be replaced by the default value. To
1393illustrate this, here are the statements from the previous example,
1394written in functional notation:
1395
1396@smallexample
1397@group
1398reject(,,)
1399reject(503,,)
1400reject(503, 5.0.0)
1401reject(503,, "Need HELO command")
1402reject(503, 5.0.0, "Need HELO command")
1403@end group
1404@end smallexample
1405
1382@node Conditional Execution 1406@node Conditional Execution
@@ -10255,3 +10279,4 @@ two kinds of actions: return actions and header manipulation actions.
10255 10279
10256 Return actions tell @command{Sendmail} to return given response code 10280@subsubheading Reply Actions
10281Reply actions tell @command{Sendmail} to return given response code
10257to the remote party. There are five such actions: 10282to the remote party. There are five such actions:
@@ -10265,3 +10290,4 @@ transmitting its message.
10265 10290
10266@item reject [@var{code}] [@var{excode}] [@var{message}] 10291@item reject @var{code} @var{excode} @var{message-expr}
10292@itemx reject (@var{code-expr}, @var{excode-expr}, @var{message-expr})
10267@cindex reject action, defined 10293@cindex reject action, defined
@@ -10272,3 +10298,4 @@ their usage is described below.
10272 10298
10273@item tempfail [@var{code}] [@var{excode}] [@var{message}] 10299@item tempfail @var{code} @var{excode} @var{message}
10300@itemx tempfail (@var{code-expr}, @var{excode-expr}, @var{message-expr})
10274@cindex tempfail action, defined 10301@cindex tempfail action, defined
@@ -10294,3 +10321,8 @@ continue processing of the message.
10294 Two actions, @code{reject} and @code{tempfail} can take up to three 10321 Two actions, @code{reject} and @code{tempfail} can take up to three
10295optional parameters. The first argument is a three-digit 10322optional parameters. There are two forms of supplying these
10323parameters.
10324
10325In the first form, called @dfn{literal} or @dfn{traditional} notation,
10326the arguments are supplied as additional words after the action name,
10327and are separated by whitespace. The first argument is a three-digit
10296@acronym{RFC} 2821 reply code. It must begin with @samp{5} for 10328@acronym{RFC} 2821 reply code. It must begin with @samp{5} for
@@ -10315,2 +10347,36 @@ reject 503 5.0.0 "Need HELO command"
10315 10347
10348The notion @dfn{textual string}, used above means either a literal
10349string or an @acronym{MFL} expression that evaluates to string.
10350However, both code and extended code must always be literal.
10351
10352The second form of supplying arguments is called @dfn{functional}
10353notation, because it resembles the function syntax. When used in this
10354form, the action word is followed by a parenthesized group of exactly
10355three arguments, separated by commas. Each argument is a
10356@acronym{MFL} expression. The meaning and ordering of the arguments is
10357the same as in literal form. Any or all of these three arguments may
10358be absent, in which case it will be replaced by the default value. To
10359illustrate this, here are the statements from the previous example,
10360written in functional notation:
10361
10362@smallexample
10363@group
10364reject(,,)
10365reject(503,,)
10366reject(503, 5.0.0)
10367reject(503, "Need HELO command",)
10368reject(503, 5.0.0, "Need HELO command")
10369@end group
10370@end smallexample
10371
10372 Notice that there is an important difference between the two
10373notations. The functional notation allows to compute both reply codes
10374at run time, e.g.:
10375
10376@smallexample
10377 reject(500 + %dig2*10 + %dig3, "5." %edig2 "." %edig2)
10378@end smallexample
10379
10380@subsubheading Header Actions
10381
10316@anchor{header manipulation} 10382@anchor{header manipulation}
diff --git a/mfd/drivers.c b/mfd/drivers.c
index d8dfb4b..ed8a288 100644
--- a/mfd/drivers.c
+++ b/mfd/drivers.c
@@ -891,25 +891,24 @@ print_type_result(NODE *node, int level)
891{ 891{
892 if (node->v.ret.code) { 892 NODE *code, *xcode;
893 const char *s = NULL; 893
894 int expr = 0; 894 code = node->v.ret.code;
895 895 xcode = node->v.ret.xcode;
896 print_level(level); 896
897 if (node->v.ret.message) {
898 if (node->v.ret.message->type == node_type_string)
899 s = node->v.ret.message->v.literal->text;
900 else {
901 expr = 1;
902 s = "(expression)";
903 }
904 }
905 dbg_setreply(NULL,
906 (char*) LITERAL_TEXT(node->v.ret.code),
907 (char*) LITERAL_TEXT(node->v.ret.xcode),
908 (char*) s);
909 if (expr)
910 print_node(node->v.ret.message, level+1);
911 }
912 print_level(level); 897 print_level(level);
898 printf("SET REPLY ");
913 print_stat(node->v.ret.stat); 899 print_stat(node->v.ret.stat);
914 printf("\n"); 900 printf("\n");
901 print_level(level);
902 printf("CODE:\n");
903 if (code)
904 print_node(code, level+1);
905 print_level(level);
906 printf("XCODE:\n");
907 if (xcode)
908 print_node(xcode, level+1);
909 print_level(level);
910 printf("MESSAGE:\n");
911 if (node->v.ret.message)
912 print_node(node->v.ret.message, level+1);
913 printf("\n");
915} 914}
@@ -919,6 +918,4 @@ mark_type_result(NODE *node)
919{ 918{
920 if (node->v.ret.code) 919 mark(node->v.ret.code);
921 node->v.ret.code->flags |= VAR_REFERENCED; 920 mark(node->v.ret.xcode);
922 if (node->v.ret.xcode)
923 node->v.ret.xcode->flags |= VAR_REFERENCED;
924 mark(node->v.ret.message); 921 mark(node->v.ret.message);
@@ -929,2 +926,4 @@ optimize_type_result(NODE *node)
929{ 926{
927 optimize(node->v.ret.code);
928 optimize(node->v.ret.xcode);
930 optimize(node->v.ret.message); 929 optimize(node->v.ret.message);
@@ -932,8 +931,7 @@ optimize_type_result(NODE *node)
932 931
933void 932static void
934code_type_result(NODE *node, struct locus **old_locus) 933code_result_arg(NODE *node)
935{ 934{
936 MARK_LOCUS(); 935 if (node)
937 if (node->v.ret.message) 936 code_node(node);
938 code_node(node->v.ret.message);
939 else { 937 else {
@@ -942,2 +940,51 @@ code_type_result(NODE *node, struct locus **old_locus)
942 } 940 }
941}
942
943static NODE *
944result_argptr(NODE *arg)
945{
946 if (arg && arg->type == node_type_string
947 && arg->v.literal->text[0] == 0)
948 arg = NULL;
949 return arg;
950}
951
952void
953code_type_result(NODE *node, struct locus **old_locus)
954{
955 NODE *code, *xcode;
956
957 code = result_argptr(node->v.ret.code);
958 xcode = result_argptr(node->v.ret.xcode);
959
960 switch (node->v.ret.stat) {
961 case SMFIS_REJECT:
962 if (code && code->type == node_type_string
963 && code->v.literal->text[0] != '5')
964 parse_error_locus(&node->locus,
965 _("Reject code should be 5xx"));
966 if (xcode && xcode->type == node_type_string
967 && xcode->v.literal->text[0] != '5')
968 parse_error_locus(&node->locus,
969 _("Reject extended code should be 5.x.x"));
970 break;
971
972 case SMFIS_TEMPFAIL:
973 if (code && code->type == node_type_string
974 && code->v.literal->text[0] != '4')
975 parse_error_locus(&node->locus,
976 _("Tempfail code should be 4xx"));
977 if (xcode && xcode->type == node_type_string
978 && xcode->v.literal->text[0] != '4')
979 parse_error_locus(&node->locus,
980 _("Tempfail extended code should be 4.x.x"));
981 break;
982 default:
983 break;
984 }
985
986 code_result_arg(node->v.ret.message);
987 code_result_arg(xcode);
988 code_result_arg(code);
989
943 MARK_LOCUS(); 990 MARK_LOCUS();
@@ -945,4 +992,2 @@ code_type_result(NODE *node, struct locus **old_locus)
945 code_immediate((void*)node->v.ret.stat); 992 code_immediate((void*)node->v.ret.stat);
946 code_immediate((void*)LITERAL_OFF(node->v.ret.code));
947 code_immediate((void*)LITERAL_OFF(node->v.ret.xcode));
948 code_op(opcode_nil); 993 code_op(opcode_nil);
@@ -1484,9 +1529,7 @@ code_type_catch(NODE *node, struct locus **old_locus)
1484 } else { 1529 } else {
1485 code_op(opcode_push); 1530 code_result_arg(NULL);
1486 code_immediate(NULL); 1531 code_result_arg(NULL);
1532 code_result_arg(NULL);
1487 code_op(opcode_result); 1533 code_op(opcode_result);
1488 code_immediate(SMFIS_CONTINUE); 1534 code_immediate(SMFIS_CONTINUE);
1489 code_immediate(NULL);
1490 code_immediate(NULL);
1491 code_immediate(NULL);
1492 } 1535 }
diff --git a/mfd/gram.y b/mfd/gram.y
index 332a748..15ee960 100644
--- a/mfd/gram.y
+++ b/mfd/gram.y
@@ -301,3 +301,3 @@ _create_alias(void *item, void *data)
301%error-verbose 301%error-verbose
302%expect 29 302%expect 28
303 303
@@ -362,3 +362,3 @@ _create_alias(void *item, void *data)
362%token <locus> FOR LOOP WHILE BREAK NEXT ARGCOUNT ALIAS DOTS ARGX VAPTR 362%token <locus> FOR LOOP WHILE BREAK NEXT ARGCOUNT ALIAS DOTS ARGX VAPTR
363%token <literal> STRING CODE XCODE 363%token <literal> STRING
364%token <literal> SYMBOL IDENTIFIER 364%token <literal> SYMBOL IDENTIFIER
@@ -388,3 +388,4 @@ _create_alias(void *item, void *data)
388 if_cond else_cond on_cond atom argref paren_argref 388 if_cond else_cond on_cond atom argref paren_argref
389 funcall proccall expr common_expr simp_expr atom_expr 389 funcall proccall expr maybe_expr maybe_xcode_expr
390 common_expr simp_expr atom_expr
390 asgn catch throw return case_cond autodcl constdecl 391 asgn catch throw return case_cond autodcl constdecl
@@ -396,6 +397,5 @@ _create_alias(void *item, void *data)
396%type <loop> opt_loop_parms loop_parm_list 397%type <loop> opt_loop_parms loop_parm_list
397%type <number> number
398%type <arglist> arglist 398%type <arglist> arglist
399%type <var> variable 399%type <var> variable
400%type <literal> string opt_ident loop_ident alias 400%type <literal> string opt_ident loop_ident alias code xcode
401%type <state> state_ident 401%type <state> state_ident
@@ -894,6 +894,2 @@ sendmail_action:
894 { 894 {
895 if ($2.code && $2.code->text[0] != '5')
896 parse_error(_("Reject code should be 5xx"));
897 if ($2.xcode && $2.xcode->text[0] != '5')
898 parse_error(_("Reject extended code should be 5.x.x"));
899 $$ = alloc_node(node_type_result, &$1); 895 $$ = alloc_node(node_type_result, &$1);
@@ -902,8 +898,12 @@ sendmail_action:
902 } 898 }
899 | ACT_REJECT '(' maybe_expr ',' maybe_xcode_expr ',' maybe_expr ')'
900 {
901 $$ = alloc_node(node_type_result, &$1);
902 $$->v.ret.stat = SMFIS_REJECT;
903 $$->v.ret.code = $3 ? cast_to(dtype_string, $3) : NULL;
904 $$->v.ret.xcode = $5 ? cast_to(dtype_string, $5) : NULL;
905 $$->v.ret.message = $7 ? cast_to(dtype_string, $7) : NULL;
906 }
903 | ACT_TEMPFAIL maybe_triplet 907 | ACT_TEMPFAIL maybe_triplet
904 { 908 {
905 if ($2.code && $2.code->text[0] != '4')
906 parse_error(_("Tempfail code should be 4xx"));
907 if ($2.xcode && $2.xcode->text[0] != '4')
908 parse_error(_("Tempfail extended code should be 4.x.x"));
909 $$ = alloc_node(node_type_result, &$1); 909 $$ = alloc_node(node_type_result, &$1);
@@ -912,2 +912,10 @@ sendmail_action:
912 } 912 }
913 | ACT_TEMPFAIL '(' maybe_expr ',' maybe_xcode_expr ',' maybe_expr ')'
914 {
915 $$ = alloc_node(node_type_result, &$1);
916 $$->v.ret.stat = SMFIS_TEMPFAIL;
917 $$->v.ret.code = $3 ? cast_to(dtype_string, $3) : NULL;
918 $$->v.ret.xcode = $5 ? cast_to(dtype_string, $5) : NULL;
919 $$->v.ret.message = $7 ? cast_to(dtype_string, $7) : NULL;
920 }
913 | ACT_CONTINUE 921 | ACT_CONTINUE
@@ -926,2 +934,11 @@ sendmail_action:
926 934
935maybe_xcode_expr: maybe_expr
936 | xcode
937 {
938 $$ = alloc_node(node_type_string, get_locus());
939 $$->v.literal = $1;
940 }
941 ;
942
943
927header_action: 944header_action:
@@ -957,5 +974,6 @@ maybe_triplet: /* empty */
957 974
958triplet : CODE 975triplet : code
959 { 976 {
960 $$.code = $1; 977 $$.code = alloc_node(node_type_string, get_locus());
978 $$.code->v.literal = $1;
961 $$.xcode = NULL; 979 $$.xcode = NULL;
@@ -963,17 +981,22 @@ triplet : CODE
963 } 981 }
964 | CODE XCODE 982 | code xcode
965 { 983 {
966 $$.code = $1; 984 $$.code = alloc_node(node_type_string, get_locus());
967 $$.xcode = $2; 985 $$.code->v.literal = $1;
986 $$.xcode = alloc_node(node_type_string, get_locus());
987 $$.xcode->v.literal = $2;
968 $$.message = NULL; 988 $$.message = NULL;
969 } 989 }
970 | CODE XCODE expr 990 | code xcode expr
971 { 991 {
972 $$.code = $1; 992 $$.code = alloc_node(node_type_string, get_locus());
973 $$.xcode = $2; 993 $$.code->v.literal = $1;
994 $$.xcode = alloc_node(node_type_string, get_locus());
995 $$.xcode->v.literal = $2;
974 $$.message = cast_to(dtype_string, $3); 996 $$.message = cast_to(dtype_string, $3);
975 } 997 }
976 | CODE expr 998 | code expr
977 { 999 {
978 $$.code = $1; 1000 $$.code = alloc_node(node_type_string, get_locus());
1001 $$.code->v.literal = $1;
979 $$.xcode = NULL; 1002 $$.xcode = NULL;
@@ -983,2 +1006,38 @@ triplet : CODE
983 1006
1007code : NUMBER
1008 {
1009 char buf[4];
1010
1011 if ($1 < 200 || $1 > 599) {
1012 yyerror(_("Invalid SMTP reply code"));
1013 buf[0] = 0;
1014 } else
1015 snprintf(buf, sizeof(buf), "%lu", $1);
1016 $$ = string_alloc(buf, strlen(buf));
1017 }
1018 ;
1019
1020xcode : NUMBER '.' NUMBER '.' NUMBER
1021 {
1022 char buf[sizeof("5.999.999")];
1023
1024 /* RFC 1893:
1025 The syntax of the new status codes is defined as:
1026
1027 status-code = class "." subject "." detail
1028 class = "2"/"4"/"5"
1029 subject = 1*3digit
1030 detail = 1*3digit
1031 */
1032 if (($1 != 2 && $1 != 4 && $1 !=5)
1033 || $3 > 999 || $5 > 999) {
1034 yyerror(_("Invalid extended reply code"));
1035 buf[0] = 0;
1036 } else
1037 snprintf(buf, sizeof(buf), "%lu.%lu.%lu",
1038 $1, $3, $5);
1039 $$ = string_alloc(buf, strlen(buf));
1040 }
1041 ;
1042
984condition : if_cond 1043condition : if_cond
@@ -1123,3 +1182,3 @@ value : STRING
1123 } 1182 }
1124 | number 1183 | NUMBER
1125 { 1184 {
@@ -1166,15 +1225,2 @@ fnmatches : FNMATCHES
1166 1225
1167number : NUMBER
1168 | CODE
1169 {
1170 char *p;
1171 $$ = strtol($1->text, &p, 10);
1172 if (*p) {
1173 /* should not happen */
1174 parse_error(_("Invalid number (near `%s')"), p);
1175 YYERROR;
1176 }
1177 }
1178 ;
1179
1180 1226
@@ -1404,2 +1450,9 @@ expr : NOT expr
1404 1450
1451maybe_expr : /* empty */
1452 {
1453 $$ = NULL;
1454 }
1455 | expr
1456 ;
1457
1405common_expr: simp_expr 1458common_expr: simp_expr
@@ -1952,3 +2005,3 @@ on : ON
1952 { 2005 {
1953 onblock(1); 2006 tie_in_onblock(1);
1954 } 2007 }
@@ -1958,3 +2011,3 @@ do : DO
1958 { 2011 {
1959 onblock(0); 2012 tie_in_onblock(0);
1960 } 2013 }
diff --git a/mfd/lex.l b/mfd/lex.l
index 3ceabfc..c1f9c3e 100644
--- a/mfd/lex.l
+++ b/mfd/lex.l
@@ -320,4 +320,2 @@ vaptr return keyword(VAPTR);
320 /* Numeric strings */ 320 /* Numeric strings */
321 {N}\.{N}\.{N} {string(yytext, yyleng); return XCODE; }
322[0-9]{3} { string(yytext, yyleng); return CODE; }
3230[xX]{X}{X}* { yylval.number = strtoul(yytext, NULL, 16); return NUMBER; }; 3210[xX]{X}{X}* { yylval.number = strtoul(yytext, NULL, 16); return NUMBER; };
@@ -793,3 +791,3 @@ isemptystr(char *text)
793void 791void
794onblock(int enable) 792tie_in_onblock(int enable)
795{ 793{
diff --git a/mfd/mailfromd.h b/mfd/mailfromd.h
index ac93cba..bdf09dc 100644
--- a/mfd/mailfromd.h
+++ b/mfd/mailfromd.h
@@ -309,5 +309,5 @@ struct return_node {
309 sfsistat stat; /* Return status */ 309 sfsistat stat; /* Return status */
310 struct literal *code; /* Code */ 310 NODE *code; /* Code */
311 struct literal *xcode; /* Extended code */ 311 NODE *xcode; /* Extended code */
312 NODE *message; /* Subtree producing the textual message */ 312 NODE *message; /* Textual message */
313}; 313};
@@ -699,3 +699,3 @@ int yyerror(char *s);
699void add_include_dir(const char *dir); 699void add_include_dir(const char *dir);
700void onblock(int enable); 700void tie_in_onblock(int enable);
701int parse_program(char *name, int ydebug); 701int parse_program(char *name, int ydebug);
diff --git a/mfd/opcodes b/mfd/opcodes
index cee68d4..dd883ea 100644
--- a/mfd/opcodes
+++ b/mfd/opcodes
@@ -92,3 +92,3 @@ FUNCALL dump_funcall 2
92NEXT NULL 0 92NEXT NULL 0
93RESULT dump_result 3 93RESULT dump_result 1
94HEADER dump_header 2 94HEADER dump_header 2
diff --git a/mfd/prog.c b/mfd/prog.c
index 90a426e..f8e58a9 100644
--- a/mfd/prog.c
+++ b/mfd/prog.c
@@ -1489,5 +1489,5 @@ instr_result(eval_environ_t env)
1489 1489
1490 get_string_arg(env, 0, &message); 1490 get_string_arg(env, 2, &message);
1491 get_literal(env, 1, (const char**)&code); 1491 get_string_arg(env, 1, &xcode);
1492 get_literal(env, 2, (const char**)&xcode); 1492 get_string_arg(env, 0, &code);
1493 1493
@@ -1515,4 +1515,4 @@ instr_result(eval_environ_t env)
1515 env->setreply(env->data, code, xcode, message); 1515 env->setreply(env->data, code, xcode, message);
1516 advance_pc(env, 3); 1516 advance_pc(env, 1);
1517 adjust_stack(env, 1); 1517 adjust_stack(env, 3);
1518} 1518}

Return to:

Send suggestions and report system problems to the System administrator.