[Chapter 31] 31.5 Macro Expansion: $ and $&

sendmail

sendmailSearch this book
Previous: 31.4 Macro NamesChapter 31
Defined Macros
Next: 31.6 Macro Conditionals: $?, $|, and $.
 

31.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 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}

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

31.5.2 When Is a Macro Expanded?

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

31.5.3 Use Value as Is with $&

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.


Previous: 31.4 Macro NamessendmailNext: 31.6 Macro Conditionals: $?, $|, and $.
31.4 Macro NamesBook Index31.6 Macro Conditionals: $?, $|, and $.