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:
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.