Writing Macros (The GNU Troff Manual)

### 5.24 Writing Macros

A macro is a stored collection of text and control lines that can be interpolated multiple times. Use macros to define common operations. Macros are called in the same way that requests are invoked. While requests exist for the purpose of creating macros, simply calling an undefined macro, or interpolating it as a string, will cause it to be defined as empty. See Identifiers.

Request: `.de` name [end] ¶

  Define a macro name, replacing the definition of any existing request, macro, string, or diversion called name. If name already exists as an alias, the target of the alias is redefined; recall Strings. GNU `troff` enters copy mode,97 storing subsequent input lines as the macro definition. If the optional second argument is not specified, the definition ends with the control line ‘`..`’ (two dots). Alternatively, end identifies a macro whose call syntax at the start of a control line ends the definition of name; end is then called normally. A macro definition must end in the same conditional block (if any) in which it began (see Conditional Blocks). Spaces or tabs are permitted after the control character in the line containing this ending token (either ‘`.`’ or ‘`end`’), but a tab immediately after the token prevents its recognition as the end of a macro definition. The macro end can be called with arguments.98

  Here is a small example macro called ‘`P`’ that causes a break and inserts some vertical space. It could be used to separate paragraphs.

      .de P
      .  br
      .  sp .8v
      ..

  We can define one macro within another. Attempting to nest ‘`..`’ naïvely will end the outer definition because the inner definition isn’t interpreted as such until the outer macro is later interpolated. We can use an end macro instead. Each level of nesting should use a unique end macro.

  An end macro need not be defined until it is called. This fact enables a nested macro definition to begin inside one macro and end inside another. Consider the following example.99

      .de m1
      .  de m2 m3
      you
      ..
      .de m3
      Hello,
      Joe.
      ..
      .de m4
      do
      ..
      .m1
      know?
      .  m3
      What
      .m4
      .m2
          ⇒ Hello, Joe.  What do you know?

  A nested macro definition can be terminated with ‘`..`’ and nested macros can reuse end macros, but these control lines must be escaped multiple times for each level of nesting. The necessity of this escaping and the utility of nested macro definitions will become clearer when we employ macro parameters and consider the behavior of copy mode in detail.

`de` defines a macro that inherits the compatibility mode enablement status of its context (see Implementation Differences). Often it is desirable to make a macro that uses `groff` features callable from contexts where compatibility mode is on; for instance, when writing extensions to a historical macro package. To achieve this, compatibility mode needs to be switched off while such a macro is interpreted—without disturbing that state when it is finished.

Request: `.de1` name [end] ¶

  The `de1` request defines a macro to be interpreted with compatibility mode disabled. When name is called, compatibility mode enablement status is saved; it is restored when the call completes. Observe the extra backlash before the interpolation of register ‘`xxx`’; we’ll explore this subject in Copy Mode.

      .nr xxx 12345
      .de aa
      The value of xxx is \\n[xxx].
      .  br
      ..
      .de1 bb
      The value of xxx is \\n[xxx].
      ..
      .cp 1
      .aa
          error→ warning: register '[' not defined
          ⇒ The value of xxx is 0xxx].
      .bb
          ⇒ The value of xxx is 12345.

Request: `.dei` name [end] ¶
Request: `.dei1` name [end] ¶

  The `dei` request defines a macro with its name and end macro indirected through strings. That is, it interpolates strings named name and end before performing the definition.

  The following examples are equivalent.

      .ds xx aa
      .ds yy bb
      .dei xx yy

      .de aa bb

  The `dei1` request bears the same relationship to `dei` as `de1` does to `de`; it temporarily turns compatibility mode off when name is called.

Request: `.am` name [end] ¶
Request: `.am1` name [end] ¶
Request: `.ami` name [end] ¶
Request: `.ami1` name [end] ¶

  `am` appends subsequent input lines to macro name, extending its definition, and otherwise working as `de` does.

  To make the previously defined ‘`P`’ macro set indented instead of block paragraphs, add the necessary code to the existing macro.

      .am P
      .ti +5n
      ..

  The other requests are analogous to their ‘`de`’ counterparts. The `am1` request turns off compatibility mode during interpretation of the appendment. The `ami` request appends indirectly, meaning that strings name and end are interpolated with the resulting names used before appending. The `ami1` request is similar to `ami`, disabling compatibility mode during interpretation of the appended lines.

Using `trace.tmac`, you can trace calls to `de`, `de1`, `am`, and `am1`. You can also use the `backtrace` request at any point desired to troubleshoot tricky spots (see Debugging).

See Strings, for the `als`, `rm`, and `rn` requests to create an alias of, remove, and rename a macro, respectively.

Macro identifiers share their name space with requests, strings, and diversions; see Identifiers. The `am`, `as`, `da`, `de`, `di`, and `ds` requests (together with their variants) create a new object only if the name of the macro, diversion, or string is currently undefined or if it is defined as a request; normally, they modify the value of an existing object. See the description of the `als` request, for pitfalls when redefining a macro that is aliased.

Request: `.return` [anything] ¶

  Exit a macro, immediately returning to the caller. If called with an argument anything, exit twice—the current macro and the macro one level higher. This is used to define a wrapper macro for `return` in `trace.tmac`.