Background
----------

mmroff(1)
pdfmom(1)

...and today's dispute between Deri and myself.

https://lists.gnu.org/archive/html/groff/2026-07/msg00094.html
https://lists.gnu.org/archive/html/groff/2026-07/msg00095.html

Summary
-------

For purposes of this proposal, a "reference" is an internal reference
within a document that may be discussed in formatted text and/or
hyperlinked.

Let's add reference tracking inside the formatter itself.  The scope is
as minimal as possible: we want to know if any forward reference
"promises" that a document makes have gone unfulfilled--"unkept" by the
time the formatter exits.  Management of auxiliary reference-related
information, such as section or page numbers, or human-readable text to
accompany it, is beyond the scope of this proposal; a macro package can
use the unique reference identifiers supported here, however, as
scaffolding to perform their own data management by constructing their
own register and string names.

Sketch of UI
------------

Two new requests respectively manipulate and query or dump a dictionary
of reference identifiers.

Here's a sketch of changes to the groff(7) man page.

.ref {link|mark} identifier
    Register a reference _identifier_.  GNU troff maintains a dictionary
    of references.  The request populates the dictionary with an entry
    for _identifier_ if it does not already exist.  If the first
    argument is "mark", then the reference's datum is assigned with a
    true value: the reference is fulfilled within the document.  If the
    first argument is "link", the datum is not manipulated.

    If the first argument is neither "link" nor "mark", GNU troff
    diagnoses an error and the reference dictionary is not affected.  It
    is not an error to mark the same _identifier_ multiple times.  (To
    be determined: should we warn in that event?  Should we employ a
    new warning category for this scenario?)

.pref [identifier ...]
    Report, to the standard error stream in JSON format, any match in
    the reference dictionary for each _identifier_.  If no arguments are
    specified, report all entries in the reference dictionary.

Here's a sketch of changes to the groff(1) man page.

Exit status
...
    If any reference identifier was linked before it was marked, GNU
    troff exits with status 3 and issues a list of all such references
    to file descriptor 3 (but see the -Q option) as a sequence of `ref
    mark` requests.  If the reference was subsequently marked in the
    input, a comment follows the request on the same line reporting the
    file name and line number where the mark actually occurred.  After
    these requests, GNU troff then emits an `.lf 1` request.

Options
...
   Transparent options
     The following options are passed as‐is to the formatter program
     troff(1) and described in more detail in its man page.
...
     -Q fd    Emit _troff_ requests recording “forward” and unfulfilled
              reference links to file descriptor _fd_ instead of 3.

How to use this reference system
--------------------------------

The idea is that you'd run groff(1) or troff(1) repeatedly (up to twice,
except possibly in the case of documents that emit reference links or
marks only conditionally).  A forward reference causes the formatter to
exit with status 3 and dump out GNU troff input like the following to
file descriptor 3.

.ref mark footnote-1 \" 45 "chapter1.me
.ref mark footnote-2 \" 206 "chapter6.me
.lf 1

(Why the neutral double quotation mark  before "chapter"? See [1].)

You can then re-run groff(1) or troff(1) with the foregoing input
prepended to your originally specified input, and since every
`ref link` request encountered will be preceded by a corresponding `ref
mark` for the same identifier, GNU troff will exit with status 0 if the
document encounters no other formatting problems.

Consider a case where the document was composed erroneously, with a
reference link that was never fulfilled with a corresponding mark.  Say
our previous example never actually populated "footnote-1".  We'd then
see something like this.

.ref mark footnote-2 \" 206 "chapter6.me
.lf 1

...and attempting to format the foregoing prepended to the original
input stream to troff(1) would still exit with status 3.

Notes
-----

The comment after the synthesized `ref mark` requests is not an
essential aspect of this proposal, and can be discarded.  However I'm,
say, 75% sure that it would be easy to support; GNU troff's input stack
is designed such that it always knows the file name and line number
associated with the top of the stack.  Tracking this information would
require using an `object_dictionary` rather than a "plain" `dictionary`
for the reference dictionary structure, but I don't think that would be
a challenge.  I already had to learn my way around these when working on
stream management functions and implementing the `pstream` request new
to groff 1.24.0.

https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/src/roff/troff/dictionary.cpp?h=1.24.1
https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/src/roff/troff/dictionary.h?h=1.24.1

(If you're wondering why GNU troff doesn't use an STL container for this
sort of thing, you're right to do so.  It's technical debt from ~1990,
and is on my list of issues to resolve.)

This sketch owes a great deal to the existing design of mmroff(1) and to
the principle of operation of a one-pass assembler (where references to
symbols "unresolved" at the time they are encountered are emitted after
assembly as "errata"), which is no accident.  The shape of the problem
is an old and familiar one.

Anti-goal
---------

Having references that are `mark`ed but not `link`ed is not an error
condition.  I can easily imagine _ms_(7) or _mm_(7) documents producing
automatic `ref mark` requests for every section heading macro call.
Whether any of these ever get `ref link`ed is up to the document
author, who might never have inbound links to some sections, and/or may
never call `TC` to produce a table of contents.  Some macro packages
have a "threshold" on the table of contents, such that section headings
nested beyond a certain depth do not show up in the table of contents.

So I don't think there is any need to track whether a "mark"ed reference
was ever "link"ed.

Conclusion
----------

I reckon I can do this for groff 1.26.

Who wants to see it?

Comments?

"I can dream up schemes when I'm sitting in my seat;
 I don't see any flaws 'til I get to my feet."
                           -- The Police, "Darkness"

Regards,
Branden

[1] https://cgit.git.savannah.gnu.org/cgit/groff.git/tree/NEWS?h=1.24.1#n20

Attachment: signature.asc
Description: PGP signature

Reply via email to