groff
[Top][All Lists]
Advanced

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

Re: [BUG] groff: inconsistent behavior of " to separate arguments


From: G. Branden Robinson
Subject: Re: [BUG] groff: inconsistent behavior of " to separate arguments
Date: Sun, 20 Mar 2022 20:36:25 +1100
User-agent: NeoMutt/20180716

At 2022-03-20T01:29:32+0100, Alejandro Colomar (man-pages) wrote:
> Hi Branden,
> 
> I've met some undocumented (or I couldn't find it) behavior of double
> quotes ("), which might be a bug in groff(1):

Hi Alex!

Ralph's description, as I parse it, is correct.  I'll speak to the
broader issue of documentation coverage of the matter.

> Could you please improve the documentation regarding '"'?

Yes, something about this should become part of groff(7).  In
groff_man_style(7), I have restricted the discussion to advising people
to use the \(dq special character (also spellable as \[dq]).

The issue _is_ discussed in the groff Texinfo manual.  Here's the
relevant material from the groff 1.22.4 version.

[[
5.5.1.1 Request and Macro Arguments
...................................
[...]
   A double quote that isn't preceded by a space doesn't start a macro
argument.  If not closing a string, it is printed literally.

   For example,

     .xxx a" "b c" "de"fg"

has the arguments 'a"', 'b c', 'de', and 'fg"'.  Don't rely on this
obscure behaviour!

   There are two possibilities to get a double quote reliably.

   * Enclose the whole argument with double quotes and use two
     consecutive double quotes to represent a single one.  This
     traditional solution has the disadvantage that double quotes don't
     survive argument expansion again if called in compatibility mode
     (using the '-C' option of 'groff'):

          .de xx
          .  tm xx: `\\$1' `\\$2' `\\$3'
          .
          .  yy "\\$1" "\\$2" "\\$3"
          ..
          .de yy
          .  tm yy: `\\$1' `\\$2' `\\$3'
          ..
          .xx A "test with ""quotes""" .
              => xx: `A' `test with "quotes"' `.'
              => yy: `A' `test with ' `quotes""'

     If not in compatibility mode, you get the expected result

          xx: `A' `test with "quotes"' `.'
          yy: `A' `test with "quotes"' `.'

     since 'gtroff' preserves the input level.

   * Use the double quote glyph '\(dq'.  This works with and without
     compatibility mode enabled since 'gtroff' doesn't convert '\(dq'
     back to a double quote input character.

     Note that this method won't work with Unix 'troff' in general since
     the glyph 'dq' isn't defined normally.

   Double quotes in the 'ds' request are handled differently.  *Note
Strings::, for more details.
]]

> I've also seen """ to mean \(dq in some manual pages.

If you wanted to pass a macro a lone double quote as an argument, and
wanted to avoid \(dq for some reason, you would, amazingly enough, need
to type four double quotes in a row.

> I guess the behavior is related to this report, but I'd like to
> understand why it works that way.

The quote preceded by a space suppresses recognition of spaces as
argument delimiters.  A subsequent double quote _not followed
immediately by another double quote_ ends the argument and reactivates
recognition of spaces as argument delimiters.  Any other double quote is
treated as a literal.

There is a kind of elegance to that; unfortunately, as our Texinfo
manual notes, the non-literal double quotes are removed in the process
of macro argument interpolation, so if one macro wraps another naïvely,
the result will be unfortunate.

The following demonstration is written to be compatible with AT&T troff,
and works the same with and without groff's -C option.  This is also
with groff 1.22.4, though Git HEAD behaves the same (as it should).

[[
$ cat EXPERIMENTS/double-quotes.roff 
.de In
INNER: $1=\\$1, $2=\\$2, $3=\\$3
..
.de Ou
OUTER: $1=\\$1, $2=\\$2, $3=\\$3
.br
.In \\$1 \\$2 \\$3
..
.Ou """" "flavored with ""meat"""
$ groff -b -ww -Tutf8 EXPERIMENTS/double-quotes.roff | cat -s
OUTER: $1=", $2=flavored with "meat", $3=
INNER: $1= flavored with , $2=meat", $3=

]]

I haven't checked yet, but I _think_ all *roffs that are actively
developed (so, ours, Heirloom Doctools's, and neatroff) make a point of
defining the `dq` special character.  It baffles me that Kernighan
didn't establish this practice when making AT&T troff device-independent
in ~1979.  Maybe some day if I ever get the chance, I'll ask him why.

Somewhere else in groff's docs--I've forgotten where--there was a nicely
economical description of these quotation rules (they are re-used in
another part of the system), and I aim to promote them to this Texinfo
node and the corresponding section of groff(7) when I revise that part
of the manual.

I would like to drop the statement
  Don't rely on this obscure behaviour!
from our Texinfo manual.  I suspect that no implementation can change
these semantics without leaving a smoking crater where even vaguely
compatible macro argument processing once was.  The key point is to
recommend the use of \(dq except where "pathological levels of
portability" are required, e.g., to write new documents for Unix V7 or
2.11BSD in SIMH.

This mechanism of embedding a double quote in a macro argument may not
be best practice, but it should be _reliable_--if you don't need to pass
that argument to another macro.

Does this help?

Regards,
Branden

Attachment: signature.asc
Description: PGP signature


reply via email to

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