Next: , Previous: , Up: MFL   [Contents][Index]


4.16 Functions

A function is a named mailfromd subroutine, which takes zero or more parameters and optionally returns a certain value. Depending on the return value, functions can be subdivided into string functions and number functions. A function may have mandatory and optional parameters. When invoked, the function must be supplied exactly as many actual arguments as the number of its mandatory parameters.

Functions are invoked using the following syntax:

  name (args)

where name is the function name and args is a comma-separated list of expressions. For example, the following are valid function calls:

  foo(10)
  interval("1 hour")
  greylist("/var/my.db", 180)

The number of parameters a function takes and their data types compose the function signature. When actual arguments are passed to the function, they are converted to types of the corresponding formal parameters.

There are two major groups of functions: built-in functions, that are implemented in the mailfromd binary, and user-defined functions, that are written in MFL. The invocation syntax is the same for both groups.

Mailfromd is shipped with a rich set of library functions. These are described in Library. In addition to these you can define your own functions.

Function definitions can appear anywhere between the handler declarations in a filter program, the only requirement being that the function definition occur before the place where the function is invoked.

The syntax of a function definition is:

[qualifier] func name (param-decl)
  [returns data-type]
do
  function-body
done

where name is the name of the function to define, param-decl is a comma-separated list of parameter declarations. The syntax of the latter is the same as that of variable declarations (see Variable declarations), i.e.:

type name

declares the parameter name having the type type. The type is string or number.

Optional qualifier declares the scope of visibility for that function (see scope of visibility). It is similar to that of variables, except that functions cannot be local (i.e. you cannot declare function within another function).

The public qualifier declares a function that may be referred to from any module, whereas the static qualifier declares a function that may be called only from the current module (see Modules). The default scope is ‘public’, unless specified otherwise in the module declaration (see module structure).

For example, the following declares a function ‘sum’, that takes two numeric arguments and returns a numeric value:

func sum(number x, number y) returns number

Similarly, the following is a declaration of a static function:

static func sum(number x, number y) returns number

Parameters are referenced in the function-body by their name, the same way as other variables. Similarly, the value of a parameter can be altered using set statement.

A function can be declared to take a certain number of optional arguments. In a function declaration, optional abstract arguments must be placed after the mandatory ones, and must be separated from them with a semicolon. The following example is a definition of function foo, which takes two mandatory and two optional arguments:

func foo(string msg, string email; number x, string pfx)

Mandatory parameters are: msg and email. Optional parameters are: x and pfx. The actual number of arguments supplied to the function is returned by a special construct $#. In addition, the special construct @arg evaluates to the ordinal number of variable arg in the list of formal parameters (the first argument has number ‘0’). These two constructs can be used to verify whether an argument is supplied to the function.

When an actual argument for parameter n is supplied, the number of actual arguments ($#) is greater than the ordinal number of that parameter in the declaration list (@n). Thus, the following construct can be used to check if an optional argument arg is actually supplied:

func foo(string msg, string email; number x, string arg)
do
  if $# > @arg
    …
  fi

The default mailfromd installation provides a special macro for this purpose: see defined. Using it, the example above could be rewritten as:

func foo(string msg, string email; number x, string arg)
do
  if defined(arg)
    …
  fi

Within a function body, optional arguments are referenced exactly the same way as the mandatory ones. Attempt to dereference an optional argument for which no actual parameter was supplied, results in an undefined value, so be sure to check whether a parameter is passed before dereferencing it.

A function can also take variable number of arguments (such functions are called variadic). This is indicated by ellipsis in place of the last abstract parameter name. The statement below defines a function foo taking one mandatory, one optional and any number of additional arguments:

func foo (string a ; string b, string ...)

The data type before the ellipsis indicates the type to promote all actual arguments to. If it is omitted, string is assumed, so the above declaration can also be written as:

func foo (string a ; string b, ...)

To refer to the actual arguments in the function body, the following construct is used:

$(expr)

where expr is any valid MFL expression, evaluating to a number n. This construct refers to the value of nth actual parameter from the variable argument list. Parameters are numbered from ‘1’, so the first variable parameter is $(1), and the last one is $($# - Nm - No), where Nm and No are numbers of mandatory and optional parameters to the function.

The construct ‘$(n)’ where 1 <= n <= 9 can also be written as ‘$n’.

For example, the function below prints all its arguments:

func pargs (string text, ...)
do
  echo "text=%text"
  loop for number i 1,
       while i < $# - @text,
       set i i + 1
  do
    echo "arg %i=" . $(i)
  done
done

Note how the ordinal number operator is used to compute the upper limit.

As another example, the function below computes the sum of its arguments.

func sum(number ...)
do
  number s 0
  loop for number i 1,
       while i <= $#,
       set i i + 1
  do
    set s s + $(i)
  done
  return s
done

Sometimes it is necessary to pass all variable arguments passed to a variadic function on to another variadic function. To do so, use the $@ operator. For example:

func y(string x, number ...)
do
  echo "x is " . sum($@)
done

Suppose y is called as y("test", 1, 3, 5). Then, it will call sum as: sum(1, 3, 5).

The $@ can be used with a numeric argument, which indicates number of arguments to remove from the resulted argument list. This is similar to shift statement in other languages. Thus, if y in the above example were written as:

func y(string x, ...)
do
  x($@(2))
done

then y("test", "a", "b", "c"), it will call x as follows: x("c").

Notice the following important points. First, $@ can be used only as the last argument in the argument list. Secondly, it cannot be used to pass mandatory and optional arguments to a function. In other words, arguments passed via $@ must correspond to ellipsis in the function declaration. Finally, passing shift count greater than the actual number of variable arguments results in a runtime error.

The function-body is any list of valid mailfromd statements. In addition to the statements discussed below (see Statements) it can also contain the return statement, which is used to return a value from the function. The syntax of the return statement is

  return value

As an example of this, consider the following code snippet that defines the function ‘sum’ to return a sum of its two arguments:

func sum(number x, number y) returns number
do
        return x + y
done

The returns part in the function declaration is optional. A declaration lacking it defines a procedure, or void function, i.e. a function that is not supposed to return any value. Such functions cannot be used in expressions, instead they are used as statements (see Statements). The following example shows a function that emits a customized temporary failure notice:

func stdtf()
do
  tempfail 451 4.3.5 "Try again later"
done

A function may have several names. An alternative name (or alias) can be assigned to a function by using alias keyword, placed after param-decl part, for example:

func foo()
alias bar
returns string
do
  …
done

After this declaration, both foo() and bar() will refer to the same function.

The number of function aliases is unlimited. The following fragment declares a function having three names:

func foo()
alias bar
alias baz
returns string
do
  …
done

Although this feature is rarely needed, there are sometimes cases when it may be necessary.

A variable declared within a function becomes a local variable to this function. Its lexical scope ends with the terminating done statement.

Parameters, local variables and global variables are using separate namespaces, so a parameter name can coincide with the name of a global, in which case a parameter is said to shadow the global. All references to its name will refer to the parameter, until the end of its scope is reached, where the global one becomes visible again. Consider the following example:

number x

func foo(string x)
do
  echo "foo: %x"
done

prog envfrom
do
  set x "Global"
  foo("Local")
  echo x
done

Running mailfromd --test with this configuration will display:

foo: Local
Global

Next: , Previous: , Up: MFL   [Contents][Index]