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:
D
Xtext
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:
R$+@xxx.yyy.zzz $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.