21.5 Macro Expansion: $ and $&
The
value of a macro can be used by putting a $
character in front of the macro's name. For example,
consider the following definition:
DXtext
Here, the macro named X is given
text as its value.
If you later prefix a macro name with a $
character, you can use that value. This is called
expanding a macro:
$X
Here, the expression $X tells
sendmail to use the value stored in
X (the text) rather
than its name (X).
For multicharacter names, the process is the same, but the name is
surrounded with curly braces:
D{Xxx}text declare {Xxx}
${Xxx} use {Xxx}
21.5.1 Macro Expansion Is Recursive
When
text contains other macros, those other
macros are also expanded. This process is recursive and continues
until all macros have been expanded. For example, consider the
following:
DAxxx
DByyy
DC$A.$B
DD$C.zzz
Here, the text for the macro
D is $C.zzz. When the
D macro is defined, it is recursively expanded
like this:
$D becomes $C.zzz
$C.zzz becomes $A.$B.zzz
$A.$B.zzz becomes xxx.$B.zzz
xxx.$B.zzz becomes xxx.yyy.zzz
Notice that when sendmail recursively expands a
macro, it does so one macro at a time, always expanding the leftmost
macro first.
In rules, when sendmail expands a macro, it also
tokenizes it. For example, placing the earlier $D
in the following rule's LHS:
R$+ @ $D $1
causes the LHS to contain seven tokens rather than three:
R$+ @ xxx . yyy . zzz $1
Note that the largest a recursive expansion can grow is defined at
compile time with the MACBUFSIZE compile-time macro (MAX...), which defaults to 4096 characters.
21.5.2 When Is a Macro Expanded?
A sendmail macro can be expanded either
immediately or at runtime, depending on where the expansion takes
place in the configuration file.
Macros are expanded in rule sets as the configuration file is read
and parsed by sendmail, and (beginning with
V8.7) so are macros in rule set names (Section 19.1.4)
and in database maps declared with the K
configuration command (Section 23.2). In other
configuration lines, expansion is deferred until
sendmail actually needs to use that value. In
yet others, macros are neither recognized nor expanded.
To illustrate, macros used in header commands are not expanded until
the headers of a mail message are processed:
H?x?Full-Name: $x
Here, $x ($x) can change
as sendmail is running. It contains as its value
the full name of the sender. Clearly, this macro should not be
expanded until that full name is known.
On the other hand, macros in rules are always expanded when
the configuration file is read. Therefore, macros such as
$x should never be used in rules because the
configuration file is read long before mail is processed:
R$x ($x)
Rules such as this won't work because
$x lacks a value when the configuration file is
read. This rule will be expanded to become meaningless:
R ( )
Note that the $digit
positional operator (Section 18.7.1) in the RHS cannot
be used to reference defined macros in the LHS. Consider this
example, in which {HOST} has the value
myhost:
R${HOST} <$1>
The ${HOST} is expanded when the configuration
file is read and is transformed into:
Rmyhost <$1> error
Here, the $1 has no wildcard operator in the LHS
to reference and so will produce this error:
configfile: line num: replacement $1 out of bounds
21.5.3 Use Value As Is with $&
For situations in which a macro should
not be recursively expanded when the configuration file is read, but
rather should be used in rules as is, V8
sendmail offers the $&
prefix. For example, consider the following RHS of a rule:
R... $w.$&m
When sendmail encounters this RHS in the
configuration file, it recursively expands $w into
its final text value (where that text value is your hostname, such as
lady). But because the m
macro is prefixed with $&, it is not expanded
until the rule is later evaluated at runtime.
To illustrate one application of $&, consider
a client/hub setup. In such a setup, all mail sent from a client
machine is forwarded to the hub for eventual delivery. If the client
were to run a sendmail daemon to receive mail
for local delivery, a mail loop could (in the absence of an MX
record) develop where a message would bounce back and fourth between
the client and the hub, eventually failing.
To break such a loop, a rule must be devised that recognizes that a
received message is from the hub:
R $+ $: $&r @ $&s <$1> Get protocol and host
R smtp @ $H <$+> $#local $: $1 Local delivery breaks a loop
R $* <$+> $#smtp $@ $H $: $2 Punt to hub
These rules appear in the parse rule set 0. By the
time they are reached, other rules have forwarded any nonlocal mail
to the hub. What is left in the workspace is a lone username. The
first rule in the preceding example matches the workspace and
rewrites it to be the sending protocol ($&r;
see $r), an @, the
sending host ($&s; see $s), and the username in angle brackets:
user becomes smtp@hub<user >
The second rule checks to make sure the message was received with the
SMTP protocol from the hub. If it was, the local
delivery agent is used to deliver the message on the local machine.
If it was received from any other host or by any other protocol, the
second rule fails and the third forwards the lone user address to the
hub.
|