4.1 The m4 Preprocessor
Creating a configuration file with m4(1) is
simplicity itself. The m4(1) program is a macro
preprocessor that produces a sendmail
configuration file by processing a file of m4
commands. Files of m4 commands traditionally
have names that end in the characters .m4 (the
same as files used for building the sendmail
binary). For building a configuration file, the convention is to name
a file of m4 commands with an ending of
.mc (for macro
configuration). The
m4 process reads that file and gathers
definitions of macros, then replaces those macros with their values
and outputs a sendmail configuration file.
With m4, macros are defined (given values) like
this:
define(macro, value)
Here, the macro is a symbolic name that
you will use later. Legal names must begin with an underscore or
letter and can contain letters, digits, and underscores. The
value can be any arbitrary text. A comma
separates the two, and that comma can be followed by optional
whitespace.
There must be no space between the define and the
left parenthesis. The definition ends with the right parenthesis.
To illustrate, consider this one-line m4 source
file named /tmp/x:
input text to be converted
define(A,B)A
the m4 definition
When m4 is run to process this file, the output
produced shows that A (the
input) is redefined to become
B:
% m4 /tmp/x
B
4.1.1 m4 Is Greedy
The
m4 program is greedy. That is, if a
macro is already defined, its value will replace
its name in the second declaration. Consider this input file:
define(A,B)
define(A,C)
A B
Here, the first line assigns the value B to the
macro named A. The second line notices that
A is a defined macro, so m4
replaces that A with B and then
defines B as having the value
C. The output of this file, after processing with
m4, will be:
C C
To prevent this kind of greedy behavior (and to prevent the confusion
it can create), you can quote an item to prevent
m4 from interpreting it.
You quote with
m4 by surrounding each item with left and right
single quotes:
define(A,B)
define(`A',C)
A B
Here, the first line defines A as
B like before. But the second line no longer sees
A as a macro. Instead, the single quotes allow
A to be redefined as C. So the
output is now:
C B
Although it is not strictly necessary, we recommend that all
macro and value pairs be
quoted. The preceding line should generally be expressed like this:
define(`A',`B')
define(`A',`C')
A B
This is the form we use when illustrating m4
throughout this book, including the previous two chapters.
4.1.2 m4 and dnl
Another problem with m4 is that it replaces its
commands with empty lines. The earlier define
commands, for example, will actually print like this:
a blank line
a blank line
C B
To suppress
this insertion of blank lines, you can use the special
m4 command dnl (for Delete
through New Line). That command looks like this:
define(`A',`B')dnl
define(`A',`C')dnl
A B
You can use dnl to remove blank lines where they
might prove inconvenient or unsightly in a configuration file.
The dnl command can also be used to put comments
into an mc file. Just be sure to put a blank
line after the last dnl because each
dnl gobbles both the text and the newline:
dnl This is a comment.
note the extra blank line
4.1.3 m4 and Arguments
When an m4
macro name is immediately followed by a right parenthesis, it is
treated like a function call. Arguments given to it in that role are
used to replace $digit
expressions in the original definition. For example, suppose the
m4 macro CONCAT is defined like this:
define(`CONCAT',`$1$2$3')dnl
and then later used like this:
CONCAT(`host', `.', `domain')
The result will be that host will replace
$1, the dot will replace $2,
and the domain will replace $3,
all jammed tightly together just as '$1$2$3' were:
host.domain
Macro arguments are used to create such techniques as FEATURE( ) and
OSTYPE( ), which are described later in this chapter.
4.1.4 The DOL m4 Macro
Ordinarily, the
$ character is interpreted by
m4 as a special character when found inside its
define expressions:
define(`A', `$2')
the $ makes $2 an m4 positional variable
There might be times, however, when you might want to put a literal
$ character into a definition—perhaps when
designing your own DOMAIN, FEATURE, or HACK files.
You place a literal $ into a definition with the
DOL m4 macro. For example:
define(`DOWN', `R DOL(*) < @ $1 > DOL(*) DOL(1) < @ $2 > DOL(2)')
Here, we define the m4 macro named DOWN, which
takes two arguments ($1 and
$2). Notice how the $ character
has meaning to m4. This newly created DOWN macro
can then be used in one of your .m4 files, perhaps
like this:
DOWN(badhost, outhost)
DOWN creates a rule by substituting the argument
(badhost for the $1 in
its definition, and the outhost) for the
corresponding $2. The substitution looks like
this:
R DOL(*) becomes -> R $*
< @ $1 > becomes -> < @ badhost >
DOL(*) becomes -> $*
DOL(1) becomes -> $1
< @ $2 > becomes -> < @ outhost >
DOL(2) becomes -> $2
After substitution, the following new rule is the result:
R $* < @ badhost > $* $1 < @ outhost > $2
The DOL m4 macro allowed the insertion of
$ characters (such as $*) and
protects you from having the literal use of $
characters being wrongly interpreted by m4.
Needless to say, you should never redefine the
DOL m4 macro.
|