diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-04-22 21:54:39 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-04-22 21:54:39 +0000 |
commit | c112aa5a76f7800297677fa372de65ad721a4666 (patch) | |
tree | 4b7eee0d91462fffe2c4814e8d162242f5084052 | |
parent | 44b3537859dd32e2730119ce3363f032fc39cb27 (diff) | |
download | mailfromd-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-- | NEWS | 18 | ||||
-rw-r--r-- | doc/mailfromd.texi | 538 | ||||
-rw-r--r-- | etc/mailfromd.rc | 2 | ||||
-rw-r--r-- | mflib/heloarg_test.mf | 4 | ||||
-rw-r--r-- | mflib/match_dnsbl.mf | 4 | ||||
-rw-r--r-- | mflib/match_rhsbl.mf | 4 | ||||
-rw-r--r-- | mflib/mx.mf | 4 | ||||
-rw-r--r-- | mflib/spf.mf | 2 | ||||
-rw-r--r-- | mflib/valid_domain.mf | 2 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/bi_other.m4 | 27 | ||||
-rw-r--r-- | src/debug.cin | 72 | ||||
-rw-r--r-- | src/debugdef.m4 | 4 | ||||
-rw-r--r-- | src/lex.l | 120 | ||||
-rw-r--r-- | src/main.c | 10 | ||||
-rw-r--r-- | tests/etc/catch.rc | 2 | ||||
-rw-r--r-- | tests/etc/catch01.rc | 2 | ||||
-rw-r--r-- | tests/etc/cidr.rc | 2 | ||||
-rw-r--r-- | tests/etc/poll-1.rc | 2 |
19 files changed, 679 insertions, 142 deletions
@@ -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 |