Mailfromd Manual (split by section):   Section:   Chapter:FastBack: Tutorial   Up: Tutorial   FastForward: MFL   Contents: Table of ContentsIndex: Concept Index

3.17 Run Mode

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 shell’s $0 argument. If your function needs to know the name of the script that is being executed, use __file__ built-in 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

3.17.1 The Top of a Script File

The --run option makes it possible to use mailfromd 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 (see Comments).

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 invocation sequences, 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 mailfromd with arbitrary shell code, provided it ends with an exec statement 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 mailfromd.

3.17.2 Parsing Command Line Arguments

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).

Built-in Function: string getopt (number argc, pointer argv, ...)

The 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.

If 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 invocation instructs getopt to use the values saved on the previous invocation.

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 option.

When there are no more options left, or a ‘--’ argument is encountered, 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 omitted. Examples:

"c"
"c|"

Short option -c.

"f:"
"f:|"

Short option -f, taking a mandatory argument.

"f::"
"f::|"

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:

"f:|file"

Short option -f, or long option --file, requiring an argument.

"f::|file"

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.:

"|help"

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:

"-:|output"

Long option --output, which takes a mandatory argument.

"-::|output"

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 optarg.

After each invocation, getopt sets the variable optind to the index of the next argv element to be parsed. Thus, when the list of options is exhausted and the function returned an empty string, optind contains the index of the the first element that is not an option.

When 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 variable 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 variable 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, getopt permutes them so that the options go first, followed by non-optional arguments.

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:

vaptr(param)

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")
  ...

Here, vaptr($1) constructs the argv array from all the arguments, supplied to the function main.

To illustrate the use of getopt function, let’s suppose you write a script that takes the following options:

-f file
--file=file
--output[=dir]
--help

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
    ...

Footnotes

(8)

When MFL has array data type, the second argument will change to array of strings.

Mailfromd Manual (split by section):   Section:   Chapter:FastBack: Tutorial   Up: Run Mode   FastForward: MFL   Contents: Table of ContentsIndex: Concept Index