aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-04-22 21:54:39 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-04-22 21:54:39 +0000
commitc112aa5a76f7800297677fa372de65ad721a4666 (patch)
tree4b7eee0d91462fffe2c4814e8d162242f5084052
parent44b3537859dd32e2730119ce3363f032fc39cb27 (diff)
downloadmailfromd-c112aa5a76f7800297677fa372de65ad721a4666.tar.gz
mailfromd-c112aa5a76f7800297677fa372de65ad721a4666.tar.bz2
Introduce the module system
git-svn-id: file:///svnroot/mailfromd/trunk@1373 7a8a7f39-df28-0410-adc6-e0d955640f24
-rw-r--r--NEWS18
-rw-r--r--doc/mailfromd.texi538
-rw-r--r--etc/mailfromd.rc2
-rw-r--r--mflib/heloarg_test.mf4
-rw-r--r--mflib/match_dnsbl.mf4
-rw-r--r--mflib/match_rhsbl.mf4
-rw-r--r--mflib/mx.mf4
-rw-r--r--mflib/spf.mf2
-rw-r--r--mflib/valid_domain.mf2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/bi_other.m427
-rw-r--r--src/debug.cin72
-rw-r--r--src/debugdef.m44
-rw-r--r--src/lex.l120
-rw-r--r--src/main.c10
-rw-r--r--tests/etc/catch.rc2
-rw-r--r--tests/etc/catch01.rc2
-rw-r--r--tests/etc/cidr.rc2
-rw-r--r--tests/etc/poll-1.rc2
19 files changed, 679 insertions, 142 deletions
diff --git a/NEWS b/NEWS
index 8398d6fd..c842f888 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Mailfromd NEWS -- history of user-visible changes. 2007-04-20
+Mailfromd NEWS -- history of user-visible changes. 2007-04-23
Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -51,6 +51,8 @@ do
...
done [while <stmt>]
+See the documentation, section `Loop Statements'.
+
* break and next
The `break' statement exits from the enclosing loop.
@@ -243,12 +245,20 @@ fails.
These are implemented in MFL and work exactly as their predecessors in
3.1.x branch.
-To use the traditional calls, include file "dns.mf".
+To use the traditional calls, add the following statement at the
+beginning of your script file:
+
+ #require dns
+
+(see the documentatio, section `Modules' for the description of
+#require statement)
* Function `match_cidr'
-This function has been reimplemented in MFL. To use it, include
-"match_cidr.mf".
+This function has been reimplemented in MFL. To use it, add
+`#require match_cidr' at the top of your script source (see the
+documentatio, section `Modules' for the description of
+#require statement)
* Catch arguments
diff --git a/doc/mailfromd.texi b/doc/mailfromd.texi
index b539d3a0..5deb4b63 100644
--- a/doc/mailfromd.texi
+++ b/doc/mailfromd.texi
@@ -112,6 +112,7 @@ Tutorial
* Start Up::
* Simplest Configurations::
* Conditional Execution::
+* Functions and Modules::
* Domain Name System::
* Checking Sender Address::
* SMTP Timeouts::
@@ -148,8 +149,10 @@ Mail Filtering Language
* Expressions:: Expressions.
* Statements::
* Conditionals:: Conditional Statements.
+* Loops:: Loop Statements.
* Exceptions:: Exceptional Conditions and their Handling.
* Polling:: Sender Verification Tests.
+* Modules:: Modules are Collections of Useful Functions.
* Filter Script Example:: A Working Filter Script Explained.
* Reserved Words:: A Reference List of Reserved Words.
@@ -179,6 +182,8 @@ Built-in and Library Functions
* Special test functions::
* Mail Sending Functions::
* Debugging Functions::
+* Blacklisting Functions::
+* SPF Functions::
User-Defined Functions
@@ -628,7 +633,7 @@ Expiration settings can be changed at run time using
@samp{#pragma database} statement in the filter script file
(@pxref{database}).
-@cindex --enable-syslog-async, @command{configure} option
+@cindex enable-syslog-async, @option{--enable-syslog-async}, @command{configure} option
@cindex syslog, non-blocking
@item Select @command{syslog} implementation to use.
@@ -684,7 +689,7 @@ Mailfromd configured with the following settings:
DBM version............................... Berkeley DB v. 3
Default user.............................. mail
State directory........................... $(localstatedir)/@/$(PACKAGE)
-Socket.................................... unix:$(DEFAULT_STATE_DIR)/@/mailfrom
+Socket.................................... unix:@//$(DEFAULT_STATE_DIR)/@/mailfrom
Expiration interval....................... 86400
Negative DNS answer expiration interval... 3600
Rates expire interval..................... 300
@@ -791,7 +796,7 @@ use the following @command{sed} expression: @samp{s/&\([a-z]\)/\1/g}.
to the top of your script:
@smallexample
-#include_once <dns.mf>
+#require dns
@end smallexample
@FIXME-xref{}
@@ -804,7 +809,7 @@ to the top of your script:
to the top of your script:
@smallexample
-#include_once <match_cidr.mf>
+#require match_cidr
@end smallexample
@FIXME-xref{}
@@ -841,7 +846,7 @@ will change in the future releases.
If a variable is declared implicitly within a function, it is
created as automatic. This differs from the previous versions, where
all variables were global. It is a common practice to use global
-variables to pass additional information between handlers (@pxref{HELO
+variables to pass additional information between handlers (@xref{HELO
Domain}, for an example of this approach). If your filter uses it,
make sure the variable is declared as global. For example, this code:
@@ -972,6 +977,7 @@ interaction with the Mail Transport Agent.
* Start Up::
* Simplest Configurations::
* Conditional Execution::
+* Functions and Modules::
* Domain Name System::
* Checking Sender Address::
* SMTP Timeouts::
@@ -1252,12 +1258,94 @@ done
@xref{switch}, for more elaborate forms of conditional branching.
+@node Functions and Modules
+@section Functions and Modules
+
+@cindex function, defined
+ As any programming language, @acronym{MFL} supports a concept of
+@dfn{function}, i.e. a body of code that is assigned a unique name and
+can be invoked elsewhere as many times as needed.
+
+ All functions have a @dfn{definition} that introduces types and
+names of the formal parameters and the result type, if the function is
+to return a meaningful value (function definitions in @acronym{MFL}
+are discussed in detail in @pxref{User-defined, User-Defined Functions}).
+
+@cindex function calls
+ A function is invoked using a special construct, @dfn{function
+call}:
+
+@smallexample
+ @var{name} (@var{arg-list})
+@end smallexample
+
+@noindent
+ where @var{name} is the function name, and @var{arg-list} is a
+comma-separated list of expressions. Each expression in
+@var{arg-list} is evaluated, and its type is compared with that of the
+corresponding formal argument. If the types differ, the expression is
+converted to the formal argument type. Finally, a copy of its value
+is passed to the function as a corresponding argument. The order in
+which the expressions are evaluated is not defined. The compiler
+checks that the number of elements in @var{arg-list} matches the
+number of mandatory arguments for function @var{name}.
+
+ For compatibility with previous versions, if @var{arg-list} consists
+of a single expression, the surrounding parentheses can be omitted, i.e.
+the following forms are equivalent:
+
+@smallexample
+hostname($client_addr)
+hostname $client_addr
+@end smallexample
+
+@noindent
+However, such syntax creates several ambiguities, so use it sparingly
+if at all. We recommend to always use parentheses when calling a
+function. @FIXME{explain why.}
+
+ When a function does not deliver a result, it should only be called
+as a statement.
+
+ Functions may be recursive, even mutually recursive.
+
+@cindex built-in and library functions, introduced
+@cindex library and built-in functions, introduced
+@cindex module, defined
+@cindex #require, introduced
+ @command{Mailfromd} comes with a rich set of predefined functions
+for various purposes. There are two basic function classes:
+@dfn{built-in} functions, that are implemented by the @acronym{MFL}
+runtime environment in @command{mailfromd}, and @dfn{library}
+functions, that are implemented in @acronym{MFL}. The built-in
+functions are always available, no preparatory work is needed before
+calling them. In contrast, the library functions are defined in
+@dfn{modules}, special @acronym{MFL} source files grouping functions
+designed for a particular task. In order to access a library
+function, you must first @dfn{require} a module it is defined it.
+This is done using @code{#require} statement. For example, the
+function @code{hostname} looks up in the DNS the name corresponding to
+the IP address specified as its argument. This function is defined in
+module @file{dns.mf}, so before calling it you must require this
+module:
+
+@smallexample
+#require dns
+@end smallexample
+
+@noindent
+The @code{#require} statement takes a single argument: the name of the
+requested module (without the @samp{.mf} suffix). It looks up the
+module on disk and loads it if it is available.
+
+ For more information about the module system @xref{Modules}.
+
@node Domain Name System
@section Domain Name System
Site administrators often do not wish to accept mail from hosts that
do not have a proper reverse delegation in the Domain Name System.
-The @command{mailfromd} language offers a special library function
+In the previous section we introduced the library function
@code{hostname}, that looks up in the DNS the name corresponding to
the IP address specified as its argument. If there is no
corresponding name, the function returns its argument unchanged. This
@@ -1266,29 +1354,23 @@ example below:
@smallexample
@group
+#require dns
+
prog envfrom
do
- if hostname $client_addr = $client_addr
+ if hostname($client_addr) = $client_addr
reject
fi
done
@end group
@end smallexample
- This example illustrates an important concept about
-@command{mailfromd} functions: for functions taking one argument, the
-argument can be given without parentheses. @FIXME{This feature
-creates lots of ambiguities, describe them.} For
-consistency with other programming languages, @command{mailfromd}
-language allows to give the argument within parentheses, like this:
-@code{hostname($client_addr)}, but this is not
-required@footnote{In fact, parentheses in function calls are required
-only if the function takes several arguments, see
-@ref{Functions}.}.
+ The @code{#require dns} statement loads the module @file{dns.mf},
+after which the definition of @code{hostname} becomes available.
- An orthogonal function, which resolves the symbolic name to the
-corresponding IP address is provided as well. Its name is
-@code{resolve}.
+ An orthogonal function, @code{resolve}, which resolves the symbolic
+name to the corresponding IP address is provided in the same
+@file{dns.mf} module.
@node Checking Sender Address
@section Checking Sender Address
@@ -1446,7 +1528,7 @@ practice). The default @code{initial-response-timeout} value should
be enough to cope with most of them. If you encounter some server
that delays more than 30 seconds, you can raise the
@code{initial-response-timeout} value. However, in this case, I'd
-recommend rather informing its admin that his server settings are not
+rather recommend informing its admin that his server settings are not
tolerable.
@node Avoiding Verification Loops
@@ -1488,6 +1570,7 @@ server@footnote{class @samp{R}}.
@smallexample
@group
+#require dns
#pragma option relay "/etc/mail/local-host-names"
#pragma option relay "/etc/mail/relay-domains"
@@ -1495,7 +1578,7 @@ prog envfrom
do
if $f == ""
accept
- elif relayed hostname $@{client_addr@}
+ elif relayed hostname($@{client_addr@}
accept
else
on poll $f do
@@ -2037,6 +2120,7 @@ user: mail
statedir: /var/run/mailfromd
socket: unix:/var/run/mailfromd/mailfrom
pidfile: /var/run/mailfromd/mailfromd.pid
+syslog: blocking
database format: Berkeley DB 2.x
dns database: /var/run/mailfromd/dns.db
dns negative expiration: 3600
@@ -2455,6 +2539,8 @@ these defaults, two command line options are provided:
@cindex syslog facility, selecting
@cindex selecting syslog facility
+@cindex syslog facility, default
+@cindex default syslog facility
The default syslog facility is @samp{mail}, and it can be changed
by @option{--log-facility} option. Argument to this option is a valid
syslog facility name, i.e. one of: @samp{user}, @samp{daemon},
@@ -2463,11 +2549,6 @@ supported}, @samp{mail}, and @samp{local0} through @samp{local7}.
The argument can be given in upper on mixed cases as well, and it can
be prefixed by @samp{log_}
-@cindex syslog facility, default
-@cindex default syslog facility
- Default syslog facility is @samp{mail}, although that depends on the
-defaults @command{mailutils} has been compiled with.
-
@cindex syslog tag
@xopindex{log-tag, introduced}
Another syslog-related parameter that can be configured is the
@@ -2481,7 +2562,7 @@ or something similar. A set of command line options is provided that
controls the output verbosity.
@xopindex{trace, introduced}
- First of all, the @option{--trace} option enables tracing actions
+ The @option{--trace} option enables tracing actions
executed during the message verification. When this option is given,
any @code{accept}, @code{discard}, @code{continue}, etc. triggered
during the execution of your filter program will leave their traces in
@@ -2505,7 +2586,7 @@ script @file{/etc/mailfromd.rc} at line 45.
@cindex Message-ID, exporting
The appearance of the message ID in the log deserves a special
notice. The program will always identify its log messages with
-the @samp{Message-Id}, when it is available. Your responsibility as
+the @samp{Message-Id}, when it is available. Your responsibility as an
administrator is to make sure it is available by configuring
@code{Sendmail} to export the macro @samp{i} to @command{mailfromd}.
The rule of thumb is: make @samp{i} available to the very first
@@ -2533,10 +2614,11 @@ impractically verbose output, suitable only for developers of
that allows to set debugging levels individually for a set of
@dfn{source modules}. In this form, the argument to the option
consists of a comma-separated list of @dfn{debug specifications}, each
-of which has the following form: @code{@var{module}[=@var{level}]}.
-Here, @var{module} is the module name, without the suffix, and
-optional @var{level} is the debugging level (an integer between 0 and
-100), that you wish to assign to this module. The default is 100.
+of which has the following form: @code{@var{source}[=@var{level}]}.
+Here, @var{source} is the @command{mailfromd} source name, without the
+suffix, and optional @var{level} is the debugging level (an integer
+between 0 and 100), that you wish to assign to this module. The
+default level is 100.
For example, the following invocation sets the global debugging level to
1, the level for functions from @file{prog.c} to 10, and for
@@ -2608,6 +2690,7 @@ amount of white-space characters (i.e. spaces, tabulations or newlines).
* Loops:: Loop Statements.
* Exceptions:: Exceptional Conditions and their Handling.
* Polling:: Sender Verification Tests.
+* Modules:: Modules are Collections of Useful Functions.
* Filter Script Example:: A Working Filter Script Explained.
* Reserved Words:: A Reference List of Reserved Words.
@end menu
@@ -2628,8 +2711,8 @@ character and extending up to the end of line:
@end group
@end smallexample
- There are, however, two special cases, where @samp{#} is not treated as a
-comment.
+ There are, however, several special cases, where the characters
+following @samp{#} are not ignored.
@anchor{include}
@cindex #include statement
@@ -2646,7 +2729,8 @@ of the specified file, as in C. There are two forms of the
@end enumerate
The quotes around @var{file} in the second form quotes are optional.
-
+
+@anchor{include search path}
@cindex include search path, introduced
Both forms are equivalent if @var{file} is an absolute file name.
Otherwise, the first form will look for @var{file} in the include
@@ -2686,6 +2770,7 @@ creates the following include search path
@end enumerate
@anchor{include_once}
+@cindex include_once
Along with @code{#include} there is also a special form
@code{#include_once}, that has the same syntax:
@@ -2694,14 +2779,41 @@ creates the following include search path
#include_once "@var{file}"
@end smallexample
- This form works exactly as @code{#include}, except that if the code
-from a file has already been included, it will not be included
+ This form works exactly as @code{#include}, except that if the
+@var{file} has already been included, it will not be included
again. As the name suggests, it will be included only once.
This form should be used to prevent re-inclusions of a code, which
can cause problems due to function redefinitions, variable
reassignements etc.
+@anchor{require}
+@kwindex require
+@cindex #require statement
+ Another special construct is @code{#require}. It instructs the
+compiler, that the code requires functions from @dfn{module} given as
+an argument to this statement:
+
+@smallexample
+#require @var{module}
+@end smallexample
+
+ Modules are @acronym{MFL} source files that contain a collection of
+code and data serving a special purpose. They are named
+@file{@var{modname}.mf} and placed in one of the directories
+comprising the include path (@pxref{include search path}). The
+@code{#require} statement looks up the named module in the path and
+attempts to compile it. If the module does not exist, a syntax error
+is reported.
+
+ It is not an error to require the same module several times. After
+the first successful load, subsequent @code{#require} statements with
+the same @var{modname} will be ignored. In this regard, this
+statement is similar to @code{#include_once}.
+
+ You will find more information regarding @acronym{MFL} modules in
+@xref{Modules}.
+
@anchor{Pragmatic comments}
@cindex Pragmatic comments
@cindex #pragma statement
@@ -2932,6 +3044,15 @@ includes source information into the debugging output. Use this if
you are debugging @command{mailfromd}.
@end deffn
+@deffn {pragma option} stack-trace @var{boolean}
+@xprindex{stack-trace}
+ Enables dumping stack traces on runtime errors. This feature is
+useful for locating the source of the error, especially in complex
+scripts.
+
+@FIXME-xref{Debugging runtime errors}.
+@end deffn
+
These options control program privileges after startup:
@deffn {pragma option} user @var{string}
@@ -4350,23 +4471,29 @@ Returns at most @var{n} last components of the domain name @var{domain}.
If @var{n} is 0 the function is equivalent to @code{domainpart}.
@flindex strip_domain_part.mf
- This function becomes available after inclusion of the file
-@file{strip_domain_part.mf}.
+ This function is defined in @file{strip_domain_part.mf}
+module (@pxref{Modules}).
Examples:
@smallexample
+#require strip_domain_part
strip_domain_part("puszcza.gnu.org.ua", 2) @result{} "org.ua"
strip_domain_part("puszcza.gnu.org.ua", 0) @result{} "gnu.org.ua"
@end smallexample
@end deftypefn
@deftypefn {Library Function} boolean is_ip (string @var{str})
+@flindex is_ip.mf
- Returns @samp{true} if @var{str} is a valid IPv4 address. For
-example:
+ Returns @samp{true} if @var{str} is a valid IPv4 address. This
+function is defined in @file{is_ip.mf} module (@pxref{Modules}).
+
+For example:
@smallexample
+#require is_ip
+
is_ip("1.2.3.4") @result{} 1
is_ip("1.2.3.x") @result{} 0
is_ip("blah") @result{} 0
@@ -4374,8 +4501,6 @@ is_ip("255.255.255.255") @result{} 1
is_ip("0.0.0.0") @result{} 1
@end smallexample
-@flindex is_ip.mf
- This function becomes available after inclusion of the file @file{is_ip.mf}.
@end deftypefn
@anchor{revip}
@@ -4639,7 +4764,8 @@ netmask_to_len(inet_aton("254.0.0.0")) @result{} 7
string @var{cidr})
@flindex match_cidr.mf
- This function becomes available after including @file{match_cidr.mf}.
+ This function is defined in @file{match_cidr.mf}
+module (@pxref{Modules}).
It returns @code{true} if the IP address @var{ip} pertains to the
IP range @var{cidr}. The first argument, @var{ip}, is a string
@@ -4677,7 +4803,11 @@ without throwing exceptions.
@flindex dns.mf
The built-in layer is always available. The library calls become
-available after inclusion of @file{dns.mf}.
+available after requesting @file{dns.mf} module (@pxref{Modules}):
+
+@smallexample
+#require dns
+@end smallexample
@deftypefn {Built-in Function} string getmx (string @var{domain} @
[, number @var{resolve}])
@@ -5374,7 +5504,13 @@ corresponding A record or if it has any MX records, that is if it can
be possible to send mail to it.
@flindex valid_domain.mf
-To use this function, include @file{valid_domain.mf} file.
+To use this function, require include @file{valid_domain.mf} module
+(@pxref{Modules}):
+
+@smallexample
+#require valid_domain
+@end smallexample
+
@end deftypefn
@anchor{heloarg_test}
@@ -5383,7 +5519,8 @@ To use this function, include @file{valid_domain.mf} file.
@flindex heloarg_test.mf
Verify if an argument of @samp{HELO} (@samp{EHLO}) command is
-valid. To use this function, include @file{heloarg_test.mf}
+valid. To use this function, load @file{heloarg_test.mf} module
+(@pxref{Modules}).
Arguments:
@@ -5625,7 +5762,8 @@ It is intended as a replacement for the Sendmail macros @samp{dnsbl} and
@samp{enhdnsbl}.
@flindex match_dnsbl.mf
-To use @code{match_dnsbl}, include the file @file{match_dnsbl.mf}.
+To use @code{match_dnsbl}, require @file{match_dnsbl.mf} module
+(@pxref{Modules}).
Arguments:
@@ -5661,8 +5799,7 @@ It is intended as a replacement for the Sendmail macro @samp{rhsbl}
by Derek J. Balling.
@flindex match_rhsbl.mf
-This function becomes available after including the file
-@file{match_rhsbl.mf}.
+To use this function, require @file{match_rhsbl.mf} module (@pxref{Modules}).
Arguments:
@@ -5804,7 +5941,7 @@ verification in @code{envfrom} handler:
@smallexample
#include_once <status.mfh>
-#include_once <spf.mf>
+#require spf
prog envfrom
do
@@ -5842,7 +5979,8 @@ layers: a built-in layer that provides basic support, and a library
layer that implements result caching.
@flindex spf.mf
- The library layer is implemented in @file{spf.mf}.
+ The library layer is implemented in @file{spf.mf} module
+(@pxref{Modules}).
The rest of this node describes available @acronym{SPF} functions
and variables.
@@ -6051,23 +6189,85 @@ Enable debugging. @var{spec} sets the debugging level the format,
described in @ref{debugging level specification}.
@end deftypefn
-@deftypefn {Built-in Function} void cancel_debug (string @var{spec})
-Disable debugging level @var{spec}.
+@deftypefn {Built-in Function} number debug_level ([string @var{srcname}])
+This function returns the current debugging level for the source
+module @var{srcname}, or the global debugging level, if called without
+argument.
+
+For example, if the program was started with
+@option{--debug=20,@/engine=8} option, then:
+
+@smallexample
+debug_level() @result{} 20
+debug_level("engine") @result{} 8
+debug_level("db") @result{} 0
+@end smallexample
@end deftypefn
- It is recommended to use both functions as brackets around the piece
-of code you wish to debug, for example:
+@deftypefn {Built-in Function} string debug_spec ([string @var{srcnames}])
+Returns the current debugging level specification, as given by
+@option{--debug} command line option or by @code{#pragma option debug}
+statement.
+
+If the argument @var{srcnames} is specified, it is treated as a
+comma-separated list of source names for which the debugging
+specification is to be returned.
+
+For example, if @command{mailfromd} was started with
+@option{--debug=20,@/spf=50,@/engine=8,@/db=30} option, then:
+
+@smallexample
+debug_spec("all,engine") @result{} "20,engine=8"
+debug_spec("engine,db") @result{} "db=30,engine=8"
+@end smallexample
+
+@noindent
+When called without argument, @code{debug_spec} will return full
+debugging level specification, as shown in the example below:
+
+@smallexample
+debug_spec() @result{} "20,@/debug=0,@/cache=0,@/dnscache=0,@/db=30,@/dns=0,@/dnsbase=0,@/engine=8,@/gram=0,@/lex=0,@/main=0,@/mf-status=0,@/mu_dbm=0,@/optab=0,@/prog=0,@/spf=50,@/stack=0,@/symtab=0,@/rate=0,@/bi_db=0,@/bi_dns=98,@/bi_io=0,@/bi_ipaddr=0,@/bi_mail=0,@/bi_poll=0,@/bi_sa=0,@/bi_spf=0,@/bi_string=7,@/bi_system=0,@/bi_other=0,@/bi_vars=0"
+@end smallexample
+@end deftypefn
+
+ These three functions are intended to complement each other. The
+calls to @code{debug} can be placed around some piece of code you wish
+to debug, to enable specific debugging information for this code
+fragment only. For example:
@smallexample
@group
- debug("io=80")
+ /* @r{Save debugging level for @file{dns.c} source} */
+ set dlev debug_level("dns")
+ /* @r{Set new debugging level} */
+ debug("dns=80")
.
.
.
- cancel_debug("io=80")
+ /* @r{Restore previous level} */
+ debug("dns=%dlev")
@end group
@end smallexample
-
+
+ You can also change debugging levels for several modules
+simultaneously:
+
+@smallexample
+@group
+ /* @r{Save debugging specifications for the sources we are
+ * interested in}
+ */
+ set dspec debug_spec("dns,dnsbase,db")
+ /* @r{Set new debugging spec} */
+ debug("dns=80,dnsbase=100,db=1")
+ .
+ .
+ .
+ /* @r{Restore previous debug specification} */
+ debug("%dspec")
+@end group
+@end smallexample
+
@deftypefn {Built-in Function} void program_trace (string @var{module})
Enable tracing for a set of modules given in @var{module} argument.
@xref{--trace-program}, for the description of its format.
@@ -6260,12 +6460,13 @@ done
To illustrate the concept of user-defined functions, this subsection
shows definitions of some of the library functions shipped with
-@command{mailfromd}. @FIXME{Until @command{mailfromd} has a library
-facility, to use these functions you must include appropriate source
-file in your script}.
+@command{mailfromd}. These functions are contained in modules
+installed along with the @command{mailfromd} binary. To use any of
+them in your code, require the appropriate module as described in
+@ref{require, #require statement}, e.g. to use the @code{revip}
+function, do @code{#require revip}.
-@FIXME{The functions are located in @file{mflib/}. Instruct how to use
-them}.
+ Functions and their definitions:
@enumerate 1
@cindex revip, definition of
@@ -6327,7 +6528,7 @@ definition follows:
@smallexample
@group
-#include_once <dns.mf>
+#require dns
func valid_domain(string domain) returns number
do
@@ -6342,8 +6543,8 @@ The function @code{match_dnsbl} (@pxref{match_dnsbl}) is defined as
follows:
@smallexample
-#include_once <dns.mf>
-#include_once <match_cidr.mf>
+#require dns
+#require match_cidr
#pragma regex push +extended
func match_dnsbl(string address, string zone, string range)
@@ -7076,13 +7277,165 @@ equality is used.
@node Loops
@section Loop Statements
-@UNREVISED{}
-@FIXME{
+@kwindex loop
+@kwindex while
+@cindex loop statement
+ The loop statement allows for repeated execution of a block of code,
+controlled by some conditional expression. It has the following form:
+
@smallexample
+@group
+loop [@var{label}]
+ [for @var{stmt1},] [while @var{expr1},] [@var{stmt2}]
+do
+ @var{stmt3}
+done [while @var{expr2}]
+@end group
+@end smallexample
+
+@noindent
+where @var{stmt1}, @var{stmt2}, and @var{stmt3} are statement lists,
+@var{expr1} and @var{expr2} are expressions.
+
+ The control flow is as follows:
+
+@enumerate 1
+@item
+ If @var{stmt1} is specified, execute it.
+
+@item
+ Evaluate @var{expr1}. If it is zero, go to 6. Otherwise, continue.
+
+@item
+ Execute @var{stmt3}.
+
+@item
+ If @var{stmt2} is supplied, execute it.
+
+@item
+ If @var{expr2} is given, evaluate it. If it is zero, go to 6.
+Otherwise, go to 2.
+
+@item
+ End.
+@end enumerate
+
+ Thus, @var{stmt3} is executed until either @var{expr1} or
+@var{expr2} yield a zero value.
+
+@cindex loop body
+ The @dfn{loop body} -- @var{stmt3} -- can contain special
+statements:
+
+@table @code
+@kwindex break
+@cindex break statement
+@item break [@var{label}]
+ Terminates the loop immediately. Control passes to @samp{6} (End)
+in the formal definition above. If @var{label} is supplied, the
+statement terminates the loop statement marked with that label. This
+allows to break from nested loops.
+
+ It is similar to @code{break} statement is C or shell.
+
+@item next [@var{label}]
+ Initiates next iteration of the loop. Control passes to @samp{2} in
+the formal definition above. If @var{label} is supplied, the
+statement starts next iteration of the loop statement marked with that
+label. This allows to request next iteration of an upper-level
+loop from a nested loop statement.
+@end table
+
+ The @code{loop} statement can be used to create iterative statements
+of arbitrary complexity. Let's illustrate it in comparison with C.
+
+@cindex infinite loop
+@cindex loop, infinite
+The statement:
+
+@smallexample
+@group
+loop
+do
+ @var{stmt-list}
+done
+@end group
+@end smallexample
+
+@noindent
+creates an infinite loop. The only way to exit from such a loop is to
+call @code{break} (or @code{return}, if used within a function),
+somewhere in @var{stmt-list}.
+
+@cindex loop, while-style
+@cindex while loop
+ The following statement is equivalent to @code{while (@var{expr1})
+@var{stmt-list}} in C:
+
+@smallexample
+@group
+loop while @var{expr}
+do
+ @var{stmt-list}
+done
+@end group
+@end smallexample
+
+@cindex loop, for-style
+@cindex for loop
+ The C construct @code{for (@var{expr1}; @var{expr2}; @var{expr3})}
+is written in @acronym{MFL} as follows:
+
+@smallexample
+@group
+loop for @var{stmt1}, while @var{expr2}, @var{stmt2}
+do
+ @var{stmt3}
+done
+@end group
+@end smallexample
+
+ For example, to repeat @var{stmt3} 10 times:
+
+@smallexample
+@group
+ loop for set i 0, while %i < 10, set i %i + 1
+ do
+ @var{stmt3}
+ done
+@end group
+@end smallexample
+
+@cindex loop, do-style
+@cindex do loop
+ Finally, the C @samp{do} loop is implemented as follows:
+
+@smallexample
+@group
+loop
+do
+ @var{stmt-list}
+done while @var{expr}
+@end group
+@end smallexample
+
+ As a real-life example of a loop statement, let's consider the
+implementation of function @code{ptr_validate}, which takes a single
+argument @var{ipstr}, and checks its validity using the following alorithm:
+
+Perform a DNS reverse-mapping for @var{ipstr}, looking up the
+corresponding PTR record in @samp{in-addr.arpa}. For each record
+returned, look up its IP addresses (A records). If @var{ipstr} is
+among the returned IP addresses, return 1 (@code{true}), otherwise
+return 0 (@code{false}).
+
+ The implementation of this function in @acronym{MFL} is:
+
+@smallexample
#pragma regex push +extended
-func a