[looping in Russ Allbery for a podlators/pod2man question]

At 2025-08-24T13:18:59-0500, G. Branden Robinson wrote:
> My proposal would be stronger if I could point to a real-world man
> page that misrenders due to `"`'s transparency, true, so I'll go
> looking for one.

I found one.  I won't claim it's a high-impact case because the syntax
of the programming language being illustrated doesn't attach
significance to the difference, but a rendering change _does_ result.

$ zgrep -C1 'no id' $(man -w MIME::Field::ParamVal)
\&    # Get an attribute, or undefined if not present:
\&    print "no id!"  if defined($field\->param(\*(Aqid\*(Aq));
\&
\&    # Same, but use empty string for missing values:
\&    print "no id!"  if ($field\->paramstr(\*(Aqid\*(Aq) eq \*(Aq\*(Aq);
\&
$ zcat $(man -w MIME::Field::ParamVal) | nroff -Wreg -Wfont -man \
  | grep -C1 'no id'
         # Get an attribute, or undefined if not present:
         print "no id!"  if defined($field->param('id'));

         # Same, but use empty string for missing values:
         print "no id!"  if ($field->paramstr('id') eq '');
$ zcat $(man -w MIME::Field::ParamVal) | nroff -Wreg -Wfont -man -mfr \
  | grep -C1 'no id'
         # Get an attribute, or undefined if not present:
         print "no id!" if defined($field->param('id'));

         # Same, but use empty string for missing values:
         print "no id!" if ($field->paramstr('id') eq '');

Notice how two spaces shrink to one after the rendered `no id!`.  We do
encourage _groff man_ users to configure supplemental inter-sentence
space to their preference, so we can't dismiss it as affecting only
non-English man pages.

groff_man_style(7):
     /.../share/groff/site-tmac/man.local
            Put site‐local changes and customizations into this file.

                   .\" Put only one space after the end of a sentence.
                   .ss 12 0 \" See groff(7).
                   .\" Keep pages narrow even on wide terminals.
                   .if n .if \n[LL]>80n .nr LL 80n

Russ,

I observe the following in "lib/Pod/Man.pm".

.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el       .ds Aq '

Do you think it would be worthwhile to do the following as well?

.ie \n(.g .ds Dq \(dq
.el       .ds Dq ""\" two double quotes because `ds` strips the first

I don't understand the patterns where you're replacing ` and ' in POD
input to make them `\*(Aq` in *roff output well enough to make a literal
suggestion for `\*(Dq` here, unfortunately.

But I think substituting `"` with `\*(Dq` would produce better results
in most cases--I'll cover a (possibly notional) exception below--and be
backward compatible to groff 1.23 and earlier, and correct regardless of
whether groff 1.24 makes the `cflags` change to the `"` character, for
man pages only, as proposed in this thread.

The exception has to do with traditional troffs and the use of double
quotes as macro arguments.  Here is an unfortunately lengthy exploration
of the problem from groff's Texinfo manual.  The bit near the end about
"repeated argument expansion" is where I suspect the headache sets in
for podlators.  You already avoid many of these problems by using font
selection escape sequences instead of font style macros, but this issue
is worth knowing about if `"` characters appear in (sub)section headings
or as `TP` paragraph tags.

Please let me know if I can be of assistance.

---snip---
5.6.3 Calling Macros
--------------------

If a macro of the desired name does not exist when called, the formatter
creates it and assigns it an empty definition.(1)  (*note Calling
Macros-Footnote-1::) Calling an undefined macro _does_ end a macro
definition naming it as its end macro (*note Writing Macros::).

   To embed spaces _within_ a macro argument, enclose the argument in
neutral double quotes '"'.  Horizontal motion escape sequences are
sometimes a better choice for arguments to be formatted as text.

   Consider calls to a hypothetical section heading macro 'uh'.

     .uh The Mouse Problem
     .uh "The Mouse Problem"
     .uh The\~Mouse\~Problem
     .uh The\ Mouse\ Problem

The first line calls 'uh' with three arguments: 'The', 'Mouse', and
'Problem'.  The remainder call the 'uh' macro with one argument, 'The
Mouse Problem'.  The last solution, using escaped spaces, can be found
in documents prepared for AT&T 'troff'.  It can cause surprise when text
is adjusted, because '\<SPC>' inserts a _fixed-width_, non-breaking
space.  GNU 'troff''s '\~' escape sequence inserts an adjustable,
non-breaking space.(2)  (*note Calling Macros-Footnote-2::)

   The foregoing raises the question of how to embed neutral double
quotes or backslashes in macro arguments when _those_ characters are
desired as literals.  In GNU 'troff', the special character escape
sequence '\[rs]' produces a backslash and '\[dq]' a neutral double
quote.

   In GNU 'troff''s AT&T compatibility mode, these characters remain
available as '\(rs' and '\(dq', respectively.  AT&T 'troff' did not
consistently define these special characters, but its descendants can be
made to support them.  *Note Device and Font Description Files::.

   If even that is not feasible, options remain.  To obtain a literal
escape character in a macro argument, you can simply type it if you
change or disable the escape character first.  *Note Using Escape
Sequences::.  Otherwise, you must escape the escape character repeatedly
to a context-dependent extent.  *Note Copy Mode::.

   For the (neutral) double quote, you have recourse to an obscure
syntactical feature of AT&T 'troff'.  Because a double quote can begin a
macro argument, the formatter keeps track of whether the current
argument was started thus, and doesn't require a space after the double
quote that ends it.(3)  (*note Calling Macros-Footnote-3::) In the
argument list to a macro, a double quote that _isn't_ preceded by a
space _doesn't_ start a macro argument.  If not preceded by a double
quote that began an argument, this double quote becomes part of the
argument.  Furthermore, within a quoted argument, a pair of adjacent
double quotes becomes a literal double quote.

     .de eq
     .  tm arg1:\\$1 arg2:\\$2 arg3:\\$3
     .  tm arg4:\\$4 arg5:\\$5 arg6:\\$6
     .. \" 4 backslashes on the next line
     .eq a" "b c" "de"f\\\\g" h""i "j""k"
         error-> arg1:a" arg2:b c arg3:de
         error-> arg4:f\g" arg5:h""i arg6:j"k

   Apart from the complexity of the rules, this traditional solution has
the disadvantage that double quotes don't survive repeated argument
expansion in AT&T 'troff' or GNU 'troff''s compatibility mode.  This can
frustrate efforts to pass such arguments intact through multiple macro
calls.

     .cp 1
     .de eq
     .  tm arg1:\\$1 arg2:\\$2 arg3:\\$3
     .  tm arg4:\\$4 arg5:\\$5 arg6:\\$6
     ..
     .de xe
     .  eq \\$1 \\$2 \\$3 \\$4 \\$5 \\$6
     .. \" 8 backslashes on the next line
     .xe a" "b c" "de"f\\\\\\\\g" h""i "j""k"
         error-> arg1:a" arg2:b arg3:c
         error-> arg4:de arg5:f\g" arg6:h""i

   Outside of compatibility mode, GNU 'troff' doesn't exhibit this
problem because it tracks the nesting depth of interpolations.  *Note
Implementation Differences::.
---end snip---

Regards,
Branden

Attachment: signature.asc
Description: PGP signature

Reply via email to