Next: , Previous: Conditionals and Loops, Up: gtroff Reference


5.21 Writing Macros

A macro is a collection of text and embedded commands which can be invoked multiple times. Use macros to define common operations. See Strings, for a (limited) alternative syntax to call macros.

Although the following requests can be used to create macros, simply using an undefined macro will cause it to be defined as empty. See Identifiers.

— Request: .de name [end]
— Request: .de1 name [end]
— Request: .dei name [end]
— Request: .dei1 name [end]

Define a new macro named name. gtroff copies subsequent lines (starting with the next one) into an internal buffer until it encounters the line ‘..’ (two dots). If the optional second argument to de is present it is used as the macro closure request instead of ‘..’.

There can be whitespace after the first dot in the line containing the ending token (either ‘.’ or macro ‘end’). Don't insert a tab character immediately after the ‘..’, otherwise it isn't recognized as the end-of-macro symbol.1

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

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

The following example defines a macro within another. Remember that expansion must be protected twice; once for reading the macro and once for executing.

          
          \# a dummy macro to avoid a warning
          .de end
          ..
          .
          .de foo
          .  de bar end
          .    nop \f[B]Hallo \\\\$1!\f[]
          .  end
          ..
          .
          .foo
          .bar Joe
              ⇒ Hallo Joe!

Since \f has no expansion, it isn't necessary to protect its backslash. Had we defined another macro within bar which takes a parameter, eight backslashes would be necessary before ‘$1’.

The de1 request turns off compatibility mode while executing the macro. On entry, the current compatibility mode is saved and restored at exit.

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

The dei request defines a macro indirectly. That is, it expands strings whose names are name or end before performing the append.

This:

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

is equivalent to:

          
          .de aa bb

The dei1 request is similar to dei but with compatibility mode switched off during execution of the defined macro.

If compatibility mode is on, de (and dei) behave similar to de1 (and dei1): A `compatibility save' token is inserted at the beginning, and a `compatibility restore' token at the end, with compatibility mode switched on during execution. See Gtroff Internals, for more information on switching compatibility mode on and off in a single document.

Using trace.tmac, you can trace calls to de and de1.

Note that macro identifiers are shared with identifiers for strings and diversions.

See the description of the als request, for possible pitfalls if redefining a macro which has been aliased.

— Request: .am name [end]
— Request: .am1 name [end]
— Request: .ami name [end]
— Request: .ami1 name [end]

Works similarly to de except it appends onto the macro named name. So, to make the previously defined ‘P’ macro actually do indented instead of block paragraphs, add the necessary code to the existing macro like this:

          
          .am P
          .ti +5n
          ..

The am1 request turns off compatibility mode while executing the appended macro piece. To be more precise, a compatibility save input token is inserted at the beginning of the appended code, and a compatibility restore input token at the end.

The ami request appends indirectly, meaning that gtroff expands strings whose names are name or end before performing the append.

The ami1 request is similar to ami but compatibility mode is switched off during execution of the defined macro.

Using trace.tmac, you can trace calls to am and am1.

See Strings, for the als and rn request to create an alias and rename a macro, respectively.

The de, am, di, da, ds, and as requests (together with its variants) only create a new object if the name of the macro, diversion or string diversion is currently undefined or if it is defined to be a request; normally they modify the value of an existing object.

— Request: .return [anything]

Exit a macro, immediately returning to the caller.

If called with an argument, exit twice, namely the current macro and the macro one level higher. This is used to define a wrapper macro for return in trace.tmac.


Footnotes

[1] While it is possible to define and call a macro ‘.’ with

     
     .de .
     .  tm foo
     ..
     .
     ..    \" This calls macro `.'!

you can't use this as the end-of-macro macro: during a macro definition, ‘..’ is never handled as a call to ‘.’, even if you say ‘.de foo .’ explicitly.