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 may 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}
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 expression
$D
appears in a line in the configuration file, 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.
In rules, when
sendmail
expands a macro, it also tokenizes it. For example, placing the above
$D
in the following rule's LHS:
R$+@$D $1
causes the LHS to contain seven tokens, rather than three:
[email protected] $1
A macro can either be expanded 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 (see
Section 29.1.4, "Macros in Rule-Set Names"
) and in maps declared with the
K
configuration command (see
Section 33.3, "The K Configuration Command"
). 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 be expanded until the headers of a mail message are processed:
H?x?Full-Name: $x
Here,
$x
(see
Section 31.10.42, $x
) may 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 like
$x
should never be used in rules, because the configuration file is read long before mail is processed:
R$x ($x)
Rules like 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 (see
Section 28.6.1, "Copy by Position: $digit"
) in the RHS may not 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:
replacement $1 out of bounds
For those situations in which a macro should not be recursively expanded, 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.
This could be useful, because it appears to offer a way to delay expansion of macros in rules until after the configuration file is read. Unfortunately such is not always the case, because the expanded text returned by the
$&
prefix is always a single token. That is, because the above is tokenized before each token is evaluated, it appears in the workspace as
lady . $m
Here, the
$m
will expand to its current value, say,
our.domain
, but that expansion will remain a single token:
lady . our.domain
When tokens are compared during rule-set processing, they are compared token by token. Consequently, the single token above will not match the individual tokens of a real address, as shown on the left:
our does not match our.domain . does not match our.domain domain does not match our.domain
The
$&
prefix is intended to provide a way to access macros that are given values after the configuration file is read. Therefore the failure of
$&
to
recursively
expand is the result of an implementation designed to meet the limited goal of accessing those runtime macros. (See
Section 33.8.4, dequote
for ways to use the
dequote
database class to circumvent this restriction.)
To illustrate one application of
$&
, consider the client/hub setup described in the tutorial. In that 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 forth 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 Rsmtp @ $H <$+> $#local $: $1 Local delivery breaks a loop R$* <$+> $#smtp $@ $H $: $2 Punt to hub
These rules appear in 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 above matches the workspace and rewrites it to be the sending protocol (
$&r
; see
Section 31.10.31, $r
), an
@
, the sending host (
$&s
, see
Section 31.10.33, $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, then 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.