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 RecursiveWhen 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.[9]
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. |