Paul DuBois
dubois@primate.wisc.edu
Wisconsin Regional Primate Research Center
Revision date: 11 April 1997
When troffcvt executes, it reads one or more action files
which instruct it how to interpret troff requests. The
default action file is named actions; this file is read
before any other action files or any macro packages named on the
command line. You should understand the format of action files
if you want to modify the default action file or if you want to
write your own action files.
This document describes the syntax of action files, and discusses
how actions work. This includes argument parsing and transmission
of argument values. It also includes a list of the available actions.
Action files are plain text files. Blank lines are ignored, as
are lines beginning with "#" in column 1, which are
taken as comments. Other lines should begin with imm or
req and have the following syntax:
imm immediate-actions req request-name parsing-actions eol post-parsing-actionsimm and req lines may be continued onto the next line with a "\" as the last character.
An imm line specifies actions to be executed as soon as
they are read from an action file. (No output can be generated
from imm lines because troffcvt turns off output
while action files are being read.)
A req line describes what troffcvt should do when
a given request occurs in the input. request-name is the
name of the request (without the leading period). parsing-actions
is either empty or a list of actions to perform to parse the request
arguments. eol is mandatory and causes troffcvt
to skip to the end of the request line. post-parsing-actions
is either empty of a list of actions to perform after the request
arguments have been parsed; typically these actions process the
arguments parsed by the parsing arguments.
To include whitespace in an argument that is passed to an action,
you can surround it with either single or double quote characters.
For example:
push-string ".tm this is an argument with whitespace\n" push-string '.tm this is an argument with whitespace\n'To include a quote within such an argument, you must quote it with the other quote character. For example:
push-string ".tm this is a single-quote: '\n" push-string '.tm this is a double-quote: "\n'
Several actions are available to parse various kinds of request
line arguments, e.g., numbers, macro arguments, single characters.
Most parsing routines read arguments from the request line, store
them in the request argument vector rargv[], and bump
the request argument count rargc. Arguments stored this
way are available to other actions, which refer to them as $1,
$2, etc.
A few parsing routines do not store the arguments in rargv[]
but instead act on them directly. For example, parse-tab-stops
reads the tab values and sets the current stops directly. parse-condition
reads and tests the condition, then executes or skips the rest
of the conditional request line.
Before an argument is passed to an action, it is examined to see
whether it contains references to request arguments or to escape
sequences. The legal references and sequences are shown below
along with their meanings:
$n Becomes the n-th request argument, empty if unavailable $$ Becomes the number of arguments parsed from request line $* Becomes a string consisting of all arguments, space-separated $@ Like $*, but arguments are double-quoted as well \\ Becomes \ \n Becomes a linefeed \t Becomes a tab \X Becomes X, for any character not listed aboveAn empty string is substituted for a request argument if n is not a digit, if there is no such argument, if the argument was empty, or if the action occurs in an immediate action list. Note that $* and $@ produce a single string, and thus count as a single argument when passed to an action.
Quotes may be used around an action argument to include whitespace
in the argument. However, as noted in the "General
Syntax" section, to include a single quote within an
argument, you must quote the argument with double quotes, and
vice-versa.
Example: The following has defines a string as the second
argument if the first argument isn't empty:
push-string '.if !"$1"" .ds $1 "$2\n'
The parse-num action is used to parse a number from the
request line. It takes one argument, a scaling indicator used
to scale numbers in request argument expressions that don't have
explicit scaling indicators. For example, the .sp request
can be specified like this:
req sp parse-num v eol break space $1
In this case parse-num uses v's as the default scaling.
Then the arguments to the requests
.sp 1 .sp 1i .sp 1i+1
will be interpreted as "1v", "1i" and "1i+1v",
respectively. (The "i", where present, overrides the
default of v).
In each case, after parse-num finishes evaluating the expression,
the result is converted into basic units, and that is what is
stored in the request argument vector. If the resolution is 432
units/inch and a v is 72 units, the arguments for the three
requests above would be stored as the strings "72",
"432" and "504". In other words, the value
stored may not look much like the value on the request line.
Allowable scaling indicators are i (inches), c (centimeters),
P (picas), m (ems), n (ens), p (points),
v (vertical spacing units), u (basic units) and
x (ignore scaling).
The x specifier is special; it means there is no default,
numbers are interpreted without scaling, and the result stored
in the request argument vector is not converted to basic
units. This is appropriate for requests such as .ce or
.ls, which may be specified like so:
req ce parse-num x eol center $1 req ls parse-num x eol line-spacing $1
If a request parsing action attempts to parse an argument that
is not present on the request line or that does not conform to
what the action is looking for, the request-parsing action stores
an empty string in the rargv[] array. Functions for actions
that allow arguments to be missing (empty) must be written to
test for that case.
Actions that take numeric parameters usually expect that the number
be given in basic units, unless an explicit scaling indicator
is appended. The reason for this is that numeric arguments to
actions are generally obtained from parsing actions earlier in
the action list, and those will usually have been converted to
basic units. This means that even for actions used in imm
lines, the same convention must be followed. Thus, the two lines
following have very different results:
imm space 12 Set spacing to 12 units imm space 12p Set spacing to 12 points
Some requests allow parameter values to be changed either to an
absolute value, or relatively by a specified amount. Examples:
.ps 3 Set point size to 3 points .ps +3 ... to 3 points greater than current value .nr x 4 Define number register with initial value 4 .nr x +4 ...with initial value 4 greater than current valueThis kind of request is handled with the parse-absrel-num action. parse-absrel-num is like parse-num in that it takes a default scaling indicator, but it also takes a second argument which specifies the current value of the relevant parameter. Since you can't specify the current value literally in an action file (the current value is a volatile value), you can refer to the parameter symbolically. The parameters which may be referred to this way are point-size, spacing, line-spacing, indent, line-length, page-length, title-length and offset.
Example: the current indent may be set absolutely or relative
to the current value, so it may be specified like this:
req in parse-absrel-num m indent eol break indent $1
If the argument on an .in request begins with a plus or
minus sign, the sign is stripped, the rest of the argument is
evaluated to produce a number, and the result is the current indent
modified up or down by that number. (The result does not
modify the current indent; it is simply stored in rargv[].)
If there is no initial sign, the request argument is an absolute
number and the parse result is simply the value of the argument
(the current indent value is ignored). In the example above, the
result goes into $1. The second reference to indent
is as an action, which is what actually sets the indentation (to
the value of $1).
As a special-case hack, if the current-value argument to parse-absrel-num
begins with a backslash, the rest of the argument is taken as
the name of a number register, and the value of the register is
used. This is used in relation to the .nr request, which
allows a register to be redefined relative to its current value.
.nr can be specified in an action file as:
req nr parse-name parse-absrel-num u \\$1 parse-num x eol define-register $1 $2 $3
It is necessary that parse-num and parse-absrel-num
both exist, because an expression such as "-10+10" means
"0" as an absolute number, but "decrease by 20"
as a relative number.
Each action is discussed below. Action descriptions are accompanied
by one or more of the following indicators to specify the allowable
contexts in which the action can be used:
imm Allowable in imm lines p Allowable in argument parsing section of req lines pp Allowable in post-parsing section of req linesWhenever you specify an action in an action list, you must specify all the arguments that it expects. However, an argument often can be the empty string. Thus escape-char @ sets the escape character to "@" while escape-char "" restores the default escape character.
abort string (pp, imm)
adjust c (pp, imm)
alias-macro xx yy (pp, imm)
alias-register xx yy (pp, imm)
append-macro xx yy (pp)
append-string xx value (pp, imm)
begin-page N (pp, imm)
break (pp, imm)
center N (pp, imm)
constant-width F N M (pp, imm)
continuous-underline N (pp, imm)
debug-flag N (p, pp, imm)
define-macro xx yy (pp)
define-register xx init incr (pp, imm)
define-string xx value (pp, imm)
diversion-append xx (pp)
diversion-begin xx (pp)
dump-bad-requests N (pp, imm)
\other bad-req: original input lineIf N is zero, this kind of output is suppressed.
dump-input-stack (pp, imm)
dump-macro xx (p, pp, imm)
echo string (p, pp, imm)
embolden S F N (pp, imm)
end-input (pp)
end-macro xx (pp, imm)
environment name (pp, imm)
eol (p)
fill (pp, imm)
font F (pp, imm)
font-position N F (pp, imm)
flush (pp, imm)
hyphenate mode (pp, imm)
hyphen-char c (pp, imm)
ignore yy (pp, imm)
indent N (pp, imm)
input-trap N xx (pp)
line-length N (pp, imm)
line-spacing N (pp, imm)
need N (pp, imm)
no-space c (pp, imm)
noadjust (pp, imm)
noescape (pp, imm)
nofill (pp, imm)
offset N (pp, imm)
output-control string (pp, imm)
output-special string (pp, imm)
output-text string (pp, imm)
page-length N (pp, imm)
page-num-char c (pp, imm)
page-number N (pp, imm)
parse-absrel-num c N (p)
parse-char (p)
parse-condition c (p)
parse-embolden (p)
.bd F N .bd S F NThe resulting argument vector will have either two or three elements.
parse-filename (p)
parse-macro-args (p)
# assume first argument, if present, is numeric expression to set indent. req XX parse-macro-args eol push-string ".if $$>0 .in $1\n"
parse-name (p)
parse-names (p)
parse-num c (p)
parse-string-value c (p)
parse-tab-stops (p)
parse-title (p)
parse-transliteration (p)
point-size N (pp, imm)
process-condition (p)
process-do (p)
push-file filename (pp)
push-macro-file filename (pp)
push-string string (pp, imm)
\push-string "\*x" \push-string "\*(xx" \push-string "\*[xxx]"To push a request or macro onto the stack, use:
\push-string ".xx\n"It can be dangerous to use push-string in immediate mode, since your execution environment may not yet be fully set up. That is, although you can process a string immediately using an imm line, you should make sure that any requests or macros named in the string have been defined, or they'll be ignored.
register-format xx format (pp, imm)
remove-name c name (pp, imm)
remove-names c name1 name2 name3 name4 name5 name6 name7 name8 name9 (pp, imm)
rename xx yy (pp, imm)
set-compatibility N (pp, imm)
set-control c (pp, imm)
set-control2 c (pp, imm)
set-escape c (pp, imm)
set-field delim pad (pp, imm)
set-leader c (pp, imm)
set-tab c (pp, imm)
shift-args N (pp, imm)
space N (pp, imm)
space-size N (pp, imm)
spacing N (pp, imm)
special-char xx output (pp, imm)
switch-file name (pp)
temp-indent N (pp, imm)
title left middle right (pp, imm)
title-length N (pp, imm)
transliterate list (pp, imm)
underline N (pp, imm)
underline-font F (pp, imm)
If the set of actions is insufficient for your purposes, here's
how to modify troffcvt to write a new one:
int ActionFunction (int argc, XChar **argv) { }The function should perform the action and return non-zero for success, zero for failure. Possibly it should simply panic if something catastrophic (unrecoverable) happens. Note that sometimes it is useful to return non-zero for certain kinds of failures. If an action in an action list returns zero, no actions following it in an action list are executed. If you want them to execute anyway, your function should always return non-zero.