General-Purpose Mail Filter
Mailfromd provides a special option that allows to run arbitrary MFL scripts. This is an experimental feature, intended for future use of MFL as a scripting language.
When given the --run command line option,
mailfromd loads the script given in its command line and
executes a function called ‘main’.
The function main must be declared as:
func main(...) returns number
Mailfromd passes all command line arguments that follow the script
name as arguments to that function. When the function returns, its
return value is used by
mailfromd as exit code.
As an example, suppose the file script.mf contains the following:
func main (...) returns number do loop for number i 1, while i <= $#, set i i + 1 do echo "arg %i=" . $(i) done done
This function prints all its arguments (See variadic functions, for a detailed description of functions with variable number of arguments). Now running:
$ mailfromd --run script.mf 1 file dest
displays the following:
arg 1=1 arg 2=file arg 3=dest
Note, that MFL does not have a direct equivalent of
$0 argument. If your function needs to know the name
of the script that is being executed, use
constant instead (see __file__.
You may name your start function with any name other than the default
‘main’. In this case, give its name as an argument to the
--run option. This argument is optional, therefore it must
be separated from the option by an equals sign (with no whitespace
from either side). For example, given the command line below,
mailfromd loads the file script.mf and execute the
function named ‘start’:
$ mailfromd --run=start script.mf
The --run option makes it possible to use
scripts as standalone programs. The traditional way to do so was to
set the executable bit on the script file and to begin the script with
the interpreter selector, i.e. the characters ‘#!’ followed by
the name of the
mailfromd executable, e.g.:
#! /usr/sbin/mailfromd --run
This would cause the shell to invoke
mailfromd with the
command line constructed from the --run option, the name
of the invoked script file itself, and any actual arguments from
the invocation. Once invoked,
mailfromd would treat the
initial ‘#!’ line as a usual single-line comment
However, the interpretation of the ‘#!’ by shells has various
deficiencies, which depend on the actual shell being used. For
example, some shells pass any characters following the whitespace
after the interpreter name as a single argument, some others silently
truncate the command line after some number of characters, etc. This
often make it impossible to pass additional arguments to
mailfromd. For example, a script which begins with the
following line would most probably fail to be executed properly:
#! /usr/sbin/mailfromd --no-config --run
To compensate for these deficiencies and to allow for more complex
mailfromd handles initial ‘#’
in a special way. If the first line of a source file begins with
‘#!/’ or ‘#! /’ (with a single space between ‘!’ and
‘/’), it is treated as a start of a multi-line comment, which is
closed by the two characters ‘!#’ on a line by themselves.
Thus, the correct way to begin a
mailfromd script is:
#! /usr/sbin/mailfromd --run !#
Using this feature, you can start the
arbitrary shell code, provided it ends with an
invoking the interpreter itself. For example:
#!/bin/sh exec /usr/sbin/mailfromd --no-config --run $0 $@ !# func main(...) returns number do /* actual mfl code goes here */ done
Note the use of ‘$0’ and ‘$@’ to pass the actual script file
name and command line arguments to
A special function is provided to break (parse) options in command lines, and to check for legal options. It uses the GNU getopt routines (see getopt in The GNU C Library Reference Manual).
getopt function parses the command line arguments, as
supplied by argc and argv. The argc argument is the
argument count, and argv is an opaque data structure,
representing the array of arguments8. The operator
vaptr (see vaptr) is
provided to initialize this argument.
An argument that starts with ‘-’ (and is not exactly ‘-’ or ‘--’), is an option element. An argument that starts with a ‘-’ is called short or traditional option. The characters of this element, except for the initial ‘-’ are option characters. Each option character represents a separate option. An argument that starts with ‘--’ is called long or GNU option. The characters of this element, except for the initial ‘--’ form the option name.
Options may have arguments. The argument to a short option is supplied immediately after the option character, or as the next word in command line. E.g., if option -f takes a mandatory argument, then it may be given either as -farg or as -f arg. The argument to a long option is either given immediately after it and separated from the option name by an equals sign (as --file=arg), or is given as the next word in the command line (e.g. --file arg).
If the option argument is optional, i.e. it may not necessarily be given, then only the first form is allowed (i.e. either -farg or --file=arg.
The ‘--’ command line argument ends the option list. Any arguments following it are not considered options, even if they begin with a dash.
getopt is called repeatedly, it returns successively each of
the option characters from each of the option elements (for short
options) and each option name (for long options). In this case, the
actual arguments are supplied only to the first invocation.
Subsequent calls must be given two nulls as arguments. Such
getopt to use the values saved on the
When the function finds another option, it returns its character or name
updating the external variable
optind (see below) so that the
next call to
getopt can resume the scan with the following
When there are no more options left, or a ‘--’ argument is
getopt returns an empty string. Then
optind gives the index in argv of the first element that
is not an option.
The legitimate options and their characteristics are supplied in
additional arguments to
getopt. Each such argument is a string
consisting of two parts, separated by a vertical bar (‘|’). Any
one of these parts is optional, but at least one of them must be
present. The first part specifies short option character. If it is
followed by a colon, this character takes mandatory argument. If it
is followed by two colons, this character takes an optional argument.
If only the first part is present, the ‘|’ separator may be
Short option -c.
Short option -f, taking a mandatory argument.
Short option -f, taking an optional argument.
If the vertical bar is present and is followed by any characters, these characters specify the name of a long option, synonymous to the short one, specified by the first part. Any mandatory or optional arguments to the short option remain mandatory or optional for the corresponding long option. Examples:
Short option -f, or long option --file, requiring an argument.
Short option -f, or long option --file, taking an optional argument.
In any of the above cases, if this option appears in the command line,
getopt returns its short option character.
To define a long option without a short equivalent, begin it with a bar, e.g.:
If this option is to take an argument, this is specified using the mechanism described above, except that the short option character is replaced with a minus sign. For example:
Long option --output, which takes a mandatory argument.
Long option --output, which takes an optional argument.
If an option is returned that has an argument in the command line,
getopt stores this argument in the variable
After each invocation,
getopt sets the variable
to the index of the next argv element to be parsed. Thus,
when the list of options is exhausted and the function returned an
optind contains the index of the the first
element that is not an option.
getopt encounters an option that is not described in its
arguments or if it detects a missing option argument it prints an
error message using
mailfromd logging facilities, stores
the offending option in the variable
optopt, and returns ‘?’.
If printing error message is not desired (e.g. the application is going
to take care of error messaging), it can be disabled by setting the
opterr to ‘0’.
The third argument to
getopt, called controlling argument,
may be used to control the behavior of the function. If it is a
colon, it disables printing the error message for unrecognized options
and missing option arguments (as setting
opterr to ‘0’
does). In this case
getopt returns ‘:’, instead of
‘?’ to indicate missing option argument.
If the controlling argument is a plus sign, or the environment
POSIXLY_CORRECT is set, then option processing stops as
soon as a non-option argument is encountered. By default, if options
and non optional arguments are intermixed in argv,
permutes them so that the options go first, followed by non-optional
If the controlling argument is ‘-’, then each non-option element in argv is handled as if it were the argument of an option with character code 1 (‘"\001"’, in MFL notation. This can used by programs that are written to expect options and other argv-elements in any order and that care about the ordering of the two.
Any other value of the controlling argument is handled as an option definition.
A special language construct is provided to supply the second
argument (argv) to
getopt and similar functions:
where param is a positional parameter, from which to start the array of argv. For example:
func main(...) returns number do set rc getopt($#, vaptr($1), "|help") ...
vaptr($1) constructs the argv array from all the
arguments, supplied to the function
To illustrate the use of
getopt function, let’s suppose you
write a script that takes the following options:
Then, the corresponding
getopt invocation will be:
func main(...) returns number do loop for string rc getopt($#, vaptr($1), "f:|file", "-::|output", "h|help"), while rc != "", set rc getopt(0, 0) do switch rc do case "f": set file optarg case "output" set output 1 set output_dir optarg case "h" help() default: return 1 done ...
When MFL has array data type, the second argument will change to array of strings.
This document was generated on January 3, 2019 using makeinfo.Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.