groff
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: man(7), hyphen, and minus


From: G. Branden Robinson
Subject: Re: man(7), hyphen, and minus
Date: Sat, 24 Dec 2022 01:27:59 -0600

Hi Russ,

At 2022-12-23T14:42:42-0800, Russ Allbery wrote:
> Incidentally, the rules for the second argument to .ds appear to not
> follow the normal rules for macro arguments.
> 
> .ds C` ""
> 
> defines \*(C` to a single double-quote, but:
> 
> .ds C` """"
> 
> defines \*(C` to """, not to " as one might expect if that were
> interpreted as a quoted argument and then adjacent doublequotes become
> a literal double quote.
> 
> So far as I can tell, the correct rule for escaping the second
> argument to .ds is that you should double any *leading* double quote,
> but leave all the other double quotes alone.
> 
> .ds C` "
> 
> appears to define \*(C` to the empty string.

These are all correct statements.

There are two major points to make here.

1.  Request invocations are not macro calls.  So all that stuff about
    double quotes we were talking about doesn't apply here.  :(
    Sorry about that.  That was a language design decision made
    literally before I was born.

2.  `ds` and related requests (like `as`) are unusual even among
    requests in that they treat the entire remainder of the input line
    as a single argument.

> (I'm not sure where this all might be documented.)

Would it surprise you to learn that I've rewritten parts of groff's
Texinfo manual to discuss these matters in detail?  ;-)

--snip--

5.1.7 Requests and Macros
-------------------------

We have now encountered almost all of the syntax there is in the 'roff'
language, with an exception already noted in passing.  A "request" is an
instruction to the formatter that occurs after a "control character",
which is recognized at the beginning of an input line.  The regular
control character is a dot ('.').  Its counterpart, the "no-break
control character", a neutral apostrophe ('''), suppresses the break
that is implied by some requests.  These characters were chosen because
it is uncommon for lines of text in natural languages to begin with
them.  If you require a formatted period or apostrophe (closing single
quotation mark) where GNU 'troff' is expecting a control character,
prefix the dot or neutral apostrophe with the non-printing input break
escape sequence, '\&'.

   An input line beginning with a control character is called a "control
line".  Every line of input that is not a control line is a "text
line".(1)  (*note Requests and Macros-Footnote-1::)

   Requests often take "arguments", words (separated from the request
name and each other by spaces) that specify details of the action GNU
'troff' is expected to perform.  If a request is meaningless without
arguments, it is typically ignored.

   GNU 'troff''s requests and escape sequences comprise the control
language of the formatter.  Of key importance are the requests that
define macros.  Macros are invoked like requests, enabling the request
repertoire to be extended or overridden.(2)  (*note Requests and
Macros-Footnote-2::)

[...]

   (1) The <\RET> escape sequence can alter how an input line is
classified; see *note Line Continuation::.

   (2) Argument handling in macros is more flexible but also more
complex.  *Note Calling Macros::.

[...]

5.6.2 Invoking Requests
-----------------------

A control character is optionally followed by tabs and/or spaces and
then an identifier naming a request or macro.  The invocation of an
unrecognized request is interpreted as a macro call.  Defining a macro
with the same name as a request replaces the request.  Deleting a
request name with the 'rm' request makes it unavailable.  The 'als'
request can alias requests, permitting them to be wrapped or
non-destructively replaced.  *Note Strings::.

   There is no general limit on argument length or quantity.  Most
requests take one or more arguments, and ignore any they do not expect.
A request may be separated from its arguments by tabs or spaces, but
only spaces can separate an argument from its successor.  Only one
between arguments is necessary; any excess is ignored.  GNU 'troff' does
not allow tabs for argument separation.(1)  (*note Invoking
Requests-Footnote-1::)

   Generally, a space _within_ a request argument is not relevant, not
meaningful, or is supported by bespoke provisions, as with the 'tl'
request's delimiters (*note Page Layout::).  Some requests, like 'ds',
interpret the remainder of the control line as a single argument.  *Note
Strings::.

   Spaces and tabs immediately after a control character are ignored.
Commonly, authors structure the source of documents or macro files with
them.

[...]

   (1) In compatibility mode, a space is not necessary after a request
or macro name of two characters' length.  Also, Plan 9 'troff' allows
tabs to separate arguments.

[...]

5.22 Strings
============

GNU 'troff' supports strings primarily for user convenience.
Conventionally, if a macro definition needs only to interpolate text,
without invoking any requests or calling any other macros, a string is
defined instead.  Only one string is predefined by the language.

 -- String: \*[.T]
     Contains the name of the output device (for example, 'utf8' or
     'pdf').

   The 'ds' request creates a string with a specified name and contents
and the '\*' escape sequence dereferences its name, interpolating its
contents.  If the string named by the '\*' escape sequence does not
exist, it is defined as empty, nothing is interpolated, and a warning in
category 'mac' is emitted.  *Note Warnings::, for information about the
enablement and suppression of warnings.

 -- Request: .ds name [contents]
 -- Request: .ds1 name [contents]
 -- Escape sequence: \*n
 -- Escape sequence: \*(nm
 -- Escape sequence: \*[name [arg1 arg2 ...]]
     Define a string called NAME with contents CONTENTS.  If NAME
     already exists, it is removed first (see 'rm' below).  If 'ds' is
     called with only one argument, NAME is defined as an empty string.

     The '\*' escape sequence interpolates a previously defined string
     variable NAME (one-character name N, two-character name NM).  The
     bracketed interpolation form accepts arguments that are handled as
     macro arguments are; recall *note Calling Macros::.  In contrast to
     macro calls, however, if a closing bracket ']' occurs in a string
     argument, that argument must be enclosed in double quotes.  '\\*'
     is interpreted even in copy mode (*note Copy Mode::).  When
     defining strings, argument interpolations must be escaped if they
     are to reference parameters from the calling context; *Note
     Parameters::.

          .ds cite (\\$1, \\$2)
          Gray codes are explored in \*[cite Morgan 1998].
              => Gray codes are explored in (Morgan, 1998).

     *Caution:* Unlike other requests, the second argument to the 'ds'
     request consumes the remainder of the input line, including
     trailing spaces.  This means that comments on a line with such a
     request can introduce unwanted space into a string when they are
     set off from the material they annotate, as is conventional.

          .ds H2O H\v'+.3m'\s'-2'2\v'-.3m'\s0O \" water

     Instead, place the comment on another line or put the comment
     escape sequence immediately adjacent to the last character of the
     string.

          .ds H2O H\v'+.3m'\s'-2'2\v'-.3m'\s0O\" water

     Ending string definitions (and appendments) with a comment, even an
     empty one, prevents unwanted space from creeping into them during
     source document maintenance.

          .ds author Alice Pleasance Liddell\"
          .ds empty \" might be appended to later with .as

     An initial neutral double quote '"' in CONTENTS is stripped to
     allow embedding of leading spaces.

          .ds salutation "         Yours in a white wine sauce,\"
          .ds c-var-defn "    char build_date[]="2020-07-29";\"
          .ds sucmd sudo sh -c "fdisk -l /dev/sda > partitions"\"

     Strings are not limited to a single input line of text.  '\<RET>'
     works just as it does elsewhere.  The resulting string is stored
     _without_ the newlines.

          .ds foo This string contains \
          text on multiple lines \
          of input.

     It is not possible to embed a newline in a string that will be
     interpreted as such when the string is interpolated.  To achieve
     that effect, use '\*' to interpolate a macro instead; see *note
     Punning Names::.

[...stuff about compatibility mode and the `ds1` request...]

 -- Request: .as name [contents]
 -- Request: .as1 name [contents]
     The 'as' request is similar to 'ds' but appends CONTENTS to the
     string stored as NAME instead of redefining it.  If NAME doesn't
     exist yet, it is created.  If 'as' is called with only one
     argument, no operation is performed (beyond dereferencing the
     string).

          .as salutation " with shallots, onions and garlic,\"

[...stuff about compatibility mode and the `as1` request...]

   Several requests exist to perform rudimentary string operations.
Strings can be queried ('length') and modified ('chop', 'substring',
'stringup', 'stringdown'), and their names can be manipulated through
renaming, removal, and aliasing ('rn', 'rm', 'als').

--snip--

As I noted in a previous mail, some will claim that no factual aspect of
the above presentation is absent from CSTR #54 (apart from groff
"deviations").  I invite the reader to test such claims for themselves.

Regards,
Branden

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]