23.4 Use $( and $) in Rules

The information in database maps is accessed in the RHS of rules. This is the basic syntax:

$(name key $)

The key is looked up in the database map whose symbolic name (declared with the K configuration command, Section 23.2) is name. If the key is found, the entire expression, including the $( and $), is normally replaced with the value returned for that key.[4] Any suffix, as specified with the -a switch (-a) in the K configuration declaration for name, is appended to the data. If the key is not found, the entire expression is replaced with key. If the $) is omitted, all tokens up to but excluding the tab and comment, or end-of-line if there is no comment, are taken as the key. To illustrate one use for $( and $), see the following rule:

[4] Note that the -m switch (-m) prevents the found value from replacing the $( and $) enclosed expression.

R$- . uucp      $: $(uucp $1.uucp $)

and the following K command:

Kuucp hash /etc/mail/uucp

This associates the symbolic name uucp with a hash-type file called /etc/mail/uucp. If the uucp database contained entries such as these:

lady.uucp    lady.localuucp
sonya.uucp   sonya.localuucp

a workspace of lady.uucp would match the LHS, so the RHS would look up $1.uucp (thus, lady.uucp) in the uucp.db database. Because lady.uucp is found, the entire $( to $) RHS expression is replaced with lady.localuucp from the database. Any UUCP hosts other than lady or sonya would not be found in the database, so the RHS expression would become the original workspace, unchanged.

Note that the entire RHS is prefixed with a $:. This prevents sendmail from retesting with the LHS after the RHS rewrite. If this prefix were omitted, endless looping could occur.

Also note that the -a switch of the K command can be used to simplify the writing of this rule. For example:

Kuucp hash -a.localuucp /etc/mail/uucp

The -a switch tells sendmail to append the text .localuucp to all successful lookups. Thus, the preceding database can be simplified to look like this:

lady.uucp    lady
sonya.uucp   sonya

But the preceding rule remains the same:

R$- . uucp      $: $(uucp $1.uucp $)

Beyond the simple macros and positional operators we have shown, the key part can use other operators and forms of macros. For example, delayed expansion macros can be useful:

R$&s       $: $( uucp $&s $)

Here, the sender's host is looked up to see whether it is a UUCP host. The $& prefix (Section 21.5.3) prevents the s macro from being expanded as the configuration file is read. Instead, its value will change with each piece of mail that is processed.

Additional examples of database lookups are given with the individual type descriptions at the end of this chapter.

23.4.1 Specify a Default with $:

The $: operator can be used as an alternative to the -a switch (or in conjunction with it). The $: operator, when it stands between the $( and $), specifies a default to use instead of the key, should a lookup fail:

R$- . uucp      $: $(uucp $1 $: $1.uucp $)

Here, the $- part of the LHS is looked up in the uucp database. If it is found, the $( to the $) in the RHS expression is replaced by the data from that database. If it is not found, the $: causes the expression to be replaced with the $- LHS part and a .uucp suffix ($1.uucp).

This version of our rule further simplifies the contents of the database file. With this rule, the database file would contain information such as the following:

lady    lady
sonya   sonya

The -a is still used as before to append a .localuucp to each successful match:

Kuucp hash -a.localuucp /etc/mail/uucp

In the RHS expression the $: must follow the key or it loses its special meaning:

$(name key $:  default  $)

If the $:default wrongly precedes the key, it is used as the key, lookups fail, and replacements are not as expected. If the $: is present but the default is missing, a failed lookup returns an empty workspace.

23.4.2 Specify Numbered Substitution with $@

For more complex substitutions, V8 sendmail offers use of the $@ operator in the RHS in conjunction with the $( and $) expressions in database maps.[5] There can be multiple $@-prefixed texts between the key and the $: (if present) or the $):

[5] Note that this substitution technique does not work for most internal database-map types. For example, it does not work with arith or dequote, but it does work with regex.

$(name key $@ text1  $@ text2  $: default  $)

Each $@text expression is numbered by position (from left to right):

$(name key $@ text1  $@ text2  $: default  $)
               1               2 

In this numbering scheme the key is always number 0, even if no $@s are listed.

These numbers correspond to literal % digit expressions in the data portion of a database map. For example:

lady   %0!%1@%2

When a lookup of the key in the RHS of the rule is successful, the returned value is examined for %digit expressions. Each such expression is replaced by its corresponding $@text from the rule. In the case of the preceding database map, %0 would be replaced with lady (the key), %1 with text1, and %2 with text2.

To illustrate, consider the earlier database entry and the following rule:

R$- @ $-.uucp   $: $(uucp $2 $@ $1 $@ mailhost $: $1@$2.uucp $)

If the workspace contains the address joe@lady.uucp, the LHS matches. The RHS rewrites only once because it is prefixed with the $: operator. The expression between the $( and $) causes the second $- from the LHS (the $2, the key) to be looked up in the database whose symbolic name is uucp. Because $2 references lady from the workspace, lady is found and the data (%0!%1@%2) is used to rewrite. The %0 is replaced by lady (the key via $2). The text for the first $@ ($1 or joe) then replaces the %1. Then the second text for the second $@ (mailhost) replaces the %2. Thus, the address joe@lady.uucp is rewritten to become lady!joe@mailhost.

If a host other than lady appeared in the workspace, this RHS would use the $:default part. Thus, the address joe@foo.uucp would become (via the $:$1@$2.uucp) joe@foo.uucp. That is, any address that is not found in the database would remain unchanged.

If there are more $@text expressions in the RHS than there are numbers in the value, the excess $@text parts are ignored. If a %digit in the data references a nonexistent $@text, it is simply removed during the rewrite.

All $@text expressions must lie between the key and the $:default (if present). If any follow the $:, they become part of the default and cease to reference any %digits.

23.4.3 $[ and $]: A Special Case

The special database-map type called host can be declared to modify name-server lookups with $[ and $]. The special symbolic name and type pair, host and host, is declared with the $( and $) operators like this:

Khost host -a.

The -a switch was discussed earlier in this chapter. Here, it is sufficient to note how it is used in resolving fully qualified domain names with the $[ and $] operators in the RHS of rules. Under V8 sendmail, $[ and $] are a special case of the following database lookup:

$(host lookuphost $)

A successful match will ordinarily append a dot to a successfully resolved hostname.

When a host type is declared with the K command, any suffix of the -a replaces the dot as the character or characters added.[6] For example:

[6] This happens only for V2 and higher configuration files. Below that level, the dot is not appended unless it is specifically added by the -a of the K command.

$[ lookuphost $]  found, so rewritten as lookuphost.domain.

Khost host -a
$[ lookuphost $]  found, so rewritten as lookuphost.domain

Khost host -a.yes
$[ lookuphost $]  found, so rewritten as lookuphost.domain.yes

The first line shows the default action of the $[ and $] operators in the RHS of the rules. If lookuphost can be fully qualified, its fully qualified name becomes the rewritten value of the RHS and has a dot appended. The next two lines show the -a with no suffix (note that with no suffix the -a is optional). In this configuration file, the fully qualified name has nothing (not even a dot) appended. The last two lines show a configuration file with a .yes as the suffix. This time, the fully qualified name has a .yes appended instead of the dot.

    Part I: Build and Install
    Part II: Administration
    Part III: The Configuration File
    Chapter 21. The D (Define a Macro) Configuration Command
    Chapter 24. The O (Options) Configuration Command