groff-commit
[Top][All Lists]
Advanced

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

[groff] 05/05: [mdoc]: Implement hyperlink support.


From: G. Branden Robinson
Subject: [groff] 05/05: [mdoc]: Implement hyperlink support.
Date: Sun, 25 Feb 2024 20:01:38 -0500 (EST)

gbranden pushed a commit to branch master
in repository groff.

commit d063402936c3e339a158f33225b41a47eff5c17d
Author: G. Branden Robinson <g.branden.robinson@gmail.com>
AuthorDate: Sun Feb 25 18:15:09 2024 -0600

    [mdoc]: Implement hyperlink support.
    
    * tmac/doc.tmac: Recognize `U` register as man(7) does to enable
      hyperlinking, defaulting on.
    
      (doc-begin-hyperlink-ascii):
      (doc-begin-hyperlink-cp1047):
      (doc-begin-hyperlink-latin1):
      (doc-begin-hyperlink-utf8):
      (doc-begin-hyperlink-html):
      (doc-begin-hyperlink-pdf):
      (doc-end-hyperlink-ascii):
      (doc-end-hyperlink-cp1047):
      (doc-end-hyperlink-latin1):
      (doc-end-hyperlink-utf8):
      (doc-end-hyperlink-html):
      (doc-end-hyperlink-pdf): New macros produce appropriate device control
      commands to start and stop hyperlinking of formatted text.
    
      (doc-begin-hyperlink-nop): (doc-end-hyperlink-nop): New do-nothing
      macros handle user-driven hyperlink disablement and devices lacking
      hyperlink support.  Create aliases for the "X100", "X100-12", "X75",
      "X75-12", "dvi", "lbp", "lj4", and "ps" devices corresponding to
      these.
    
      (doc-Xr-usage, doc-Mt-usage, doc-Lk-usage): New macros eliminate
      repeated logic to emit usage messages.
    
      (Xr): Heavily rewrite to support production of hyperlinked argument
      text.  In mdoc, there appears to be no mechanism to inject macro calls
      later in the argument stream.  To correctly place a
      `doc*end-hyperlink` call, we must parse (optional) arguments ourselves
      until we have seen enough to know we've reached the end of the link
      text (an inline macro call, punctuation, or the end of the input
      line).  Throw usage diagnostic in more cases of bad input.
    
      (Mt): Include "mailto:"; schema in generated hyperlink but not in
      visible link text.
    
      (Mt, Lk): Call device-appropriate `doc-{begin,end}-hyperlink` macros.
    
      (Lk): Hyperlink argument only if it is a "string" (mdoc parlance for
      formattable text as opposed to a macro name or punctuation).  Format
      link text as groff man(7) does, not like mandoc(1) does, with its
      "link-text: <url>" presentation.
    
    * tmac/mdoc.local: Add commented code illustrating how to disable
      hyperlinking.
    
    * tmac/tests/doc_Lk-respects-sentence-ending-punctuation.sh:
    * tmac/tests/doc_Lk-works.sh:
    * tmac/tests/doc_Mt-works.sh:
    * tmac/tests/doc_Xr-works.sh:
    * tmac/tests/doc_heading-font-remapping-works.sh: Update test
      expectations and check output when hyperlink output enabled and
      disabled.
    
    * NEWS:
    * tmac/groff_mdoc.7.man (Options): Document it.
---
 ChangeLog                                          |  58 +++++
 NEWS                                               |  10 +-
 tmac/doc.tmac                                      | 280 +++++++++++++++++----
 tmac/groff_mdoc.7.man                              |  19 ++
 tmac/mdoc.local                                    |   4 +
 .../doc_Lk-respects-sentence-ending-punctuation.sh |  20 +-
 tmac/tests/doc_Lk-works.sh                         |  36 ++-
 tmac/tests/doc_Mt-works.sh                         |  23 +-
 tmac/tests/doc_Xr-works.sh                         |  59 ++++-
 tmac/tests/doc_heading-font-remapping-works.sh     |  12 +-
 10 files changed, 440 insertions(+), 81 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 2b0cc126c..b94e48cf2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,61 @@
+2024-02-25  G. Branden Robinson <g.branden.robinson@gmail.com>
+
+       [mdoc]: Implement hyperlink support.
+
+       * tmac/doc.tmac: Recognize `U` register as man(7) does to enable
+       hyperlinking, defaulting on.
+       (doc-begin-hyperlink-ascii):
+       (doc-begin-hyperlink-cp1047):
+       (doc-begin-hyperlink-latin1):
+       (doc-begin-hyperlink-utf8):
+       (doc-begin-hyperlink-html):
+       (doc-begin-hyperlink-pdf):
+       (doc-end-hyperlink-ascii):
+       (doc-end-hyperlink-cp1047):
+       (doc-end-hyperlink-latin1):
+       (doc-end-hyperlink-utf8):
+       (doc-end-hyperlink-html):
+       (doc-end-hyperlink-pdf): New macros produce appropriate device
+       control commands to start and stop hyperlinking of formatted
+       text.
+       (doc-begin-hyperlink-nop):
+       (doc-end-hyperlink-nop): New do-nothing macros handle
+       user-driven hyperlink disablement and devices lacking hyperlink
+       support.  Create aliases for the "X100", "X100-12", "X75",
+       "X75-12", "dvi", "lbp", "lj4", and "ps" devices corresponding to
+       these.
+       (doc-Xr-usage, doc-Mt-usage, doc-Lk-usage): New macros eliminate
+       repeated logic to emit usage messages.
+       (Xr): Heavily rewrite to support production of hyperlinked
+       argument text.  In mdoc, there appears to be no mechanism to
+       inject macro calls later in the argument stream.  To correctly
+       place a `doc*end-hyperlink` call, we must parse (optional)
+       arguments ourselves until we have seen enough to know we've
+       reached the end of the link text (an inline macro call,
+       punctuation, or the end of the input line).  Throw usage
+       diagnostic in more cases of bad input.
+       (Mt): Include "mailto:"; schema in generated hyperlink but not in
+       visible link text.
+       (Mt, Lk): Call device-appropriate `doc-{begin,end}-hyperlink`
+       macros.
+       (Lk): Hyperlink argument only if it is a "string" (mdoc parlance
+       for formattable text as opposed to a macro name or punctuation).
+       Format link text as groff man(7) does, not like mandoc(1) does,
+       with its "link-text: <url>" presentation.
+       * tmac/mdoc.local: Add commented code illustrating how to
+       disable hyperlinking.
+
+       * tmac/tests/doc_Lk-respects-sentence-ending-punctuation.sh:
+       * tmac/tests/doc_Lk-works.sh:
+       * tmac/tests/doc_Mt-works.sh:
+       * tmac/tests/doc_Xr-works.sh:
+       * tmac/tests/doc_heading-font-remapping-works.sh: Update test
+       expectations and check output when hyperlink output enabled and
+       disabled.
+
+       * NEWS:
+       * tmac/groff_mdoc.7.man (Options): Document it.
+
 2024-02-25  G. Branden Robinson <g.branden.robinson@gmail.com>
 
        * tmac/tests/doc_Xr-works.sh: Add checks of inline `Pf` and `Ns`
diff --git a/NEWS b/NEWS
index 9cede44c1..44c339510 100644
--- a/NEWS
+++ b/NEWS
@@ -118,9 +118,9 @@ o The device-specific macro files loaded by "troffrc" 
automatically on
   grolbp(1).
 
 o Hyperlink support is now enabled by default on non-HTML devices that
-  support it (PDF and terminal devices) for an (man) documents.
-  Instructions and commented code for disabling it remain in the
-  "man.local" file.
+  support it (PDF and terminal devices) for an (man) and doc (mdoc)
+  documents.  Instructions and commented code for disabling it are in
+  the "man.local" and "mdoc.local" files.
 
 o The an (man) package now supports use of its hyperlink macros (`UR`,
   `UE`, `MT`, and `ME`) as paragraph tags (that is, on the next line
@@ -176,6 +176,10 @@ o The doc (mdoc) macro package's `Mt` macro now sets its 
argument in
   configured to use).  A new string, `doc-Mt-font`, for use in
   "mdoc.local" files and similar, supports configuration of this face.
 
+o The doc (mdoc) macro package's `Lk`, `Mt`, and `Xr` macros now produce
+  hyperlinks on HTML, PDF, and terminal devices.  See above regarding
+  hyperlink support being enabled by default.
+
 o The new macro file "koi8-r.tmac" supports the KOI8-R character
   encoding, which supports the new Russian locale for groff.
 
diff --git a/tmac/doc.tmac b/tmac/doc.tmac
index 0c9f3a0cf..de02fe834 100644
--- a/tmac/doc.tmac
+++ b/tmac/doc.tmac
@@ -235,7 +235,9 @@
 .\" Use -rSN=<xxx> to set the subsection heading indentation amount.
 .if !r SN .nr SN 3n
 .
-.\" TODO: Implement U register.
+.\" URI enablement desired
+.if !r U \
+.  nr U 1
 .
 .\" page number after which to apply letter suffixes
 .\"
@@ -4502,6 +4504,103 @@
 .ec
 .
 .
+.\" Define device-specific macros implementing hyperlinks.
+.eo
+.de doc-begin-hyperlink-ascii
+.  nop \X'tty: link \$*'\c
+..
+.ec
+.
+.eo
+.de doc-end-hyperlink-ascii
+.  nop \X'tty: link'\c
+..
+.ec
+.
+.als doc-begin-hyperlink-cp1047 doc-begin-hyperlink-ascii
+.als doc-begin-hyperlink-latin1 doc-begin-hyperlink-ascii
+.als doc-begin-hyperlink-utf8   doc-begin-hyperlink-ascii
+.
+.als doc-end-hyperlink-cp1047 doc-end-hyperlink-ascii
+.als doc-end-hyperlink-latin1 doc-end-hyperlink-ascii
+.als doc-end-hyperlink-utf8   doc-end-hyperlink-ascii
+.
+.eo
+.de doc-begin-hyperlink-html
+.  nop \X'html:<a href="\$*">'\c
+..
+.ec
+.
+.eo
+.de doc-end-hyperlink-html
+.  nop \X'html:</a>'\c
+..
+.ec
+.
+.eo
+.de doc-begin-hyperlink-pdf
+.  pdfhref W -D \$1 -- |
+..
+.ec
+.
+.eo
+.de doc-end-hyperlink-pdf
+.  nop \X'pdf: markend'\m[default]\c
+..
+.ec
+.
+.\" Define stubs for devices without a hyperlink feature.
+.de doc-begin-hyperlink-nop
+..
+.
+.de doc-end-hyperlink-nop
+..
+.
+.als doc-begin-hyperlink-X100    doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-X100-12 doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-X75     doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-X75-12  doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-dvi     doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-lbp     doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-lj4     doc-begin-hyperlink-nop
+.als doc-begin-hyperlink-ps      doc-begin-hyperlink-nop
+.
+.als doc-end-hyperlink-X100    doc-begin-hyperlink-nop
+.als doc-end-hyperlink-X100-12 doc-begin-hyperlink-nop
+.als doc-end-hyperlink-X75     doc-begin-hyperlink-nop
+.als doc-end-hyperlink-X75-12  doc-begin-hyperlink-nop
+.als doc-end-hyperlink-dvi     doc-begin-hyperlink-nop
+.als doc-end-hyperlink-lbp     doc-begin-hyperlink-nop
+.als doc-end-hyperlink-lj4     doc-begin-hyperlink-nop
+.als doc-end-hyperlink-ps      doc-begin-hyperlink-nop
+.
+.if !\n[U] \{\
+.  als doc-begin-hyperlink-ascii  doc-begin-hyperlink-nop
+.  als doc-begin-hyperlink-latin1 doc-begin-hyperlink-nop
+.  als doc-begin-hyperlink-cp1047 doc-begin-hyperlink-nop
+.  als doc-begin-hyperlink-utf8   doc-begin-hyperlink-nop
+.  als doc-begin-hyperlink-html   doc-begin-hyperlink-nop
+.  als doc-begin-hyperlink-pdf    doc-begin-hyperlink-nop
+.
+.  als doc-end-hyperlink-ascii  doc-end-hyperlink-nop
+.  als doc-end-hyperlink-latin1 doc-end-hyperlink-nop
+.  als doc-end-hyperlink-cp1047 doc-end-hyperlink-nop
+.  als doc-end-hyperlink-utf8   doc-end-hyperlink-nop
+.  als doc-end-hyperlink-html   doc-end-hyperlink-nop
+.  als doc-end-hyperlink-pdf    doc-end-hyperlink-nop
+.\}
+.
+.
+.\" NS doc-Xr-usage macro
+.
+.eo
+.de doc-Xr-usage
+.  tm Usage: .Xr manpage_name [section#] ... (#\n[.c])
+.  doc-reset-args
+..
+.ec
+.
+.
 .\" NS Xr user macro
 .\" NS   cross reference (for man pages only)
 .\" NS
@@ -4510,7 +4609,12 @@
 .\" NS   doc-macro-name
 .\" NS
 .\" NS local variables:
-.\" NS   doc-reg-Xr
+.\" NS   doc-has-section-Xr
+.\" NS   doc-lasttext-Xr
+.\" NS   doc-next-arg-ptr-Xr
+.\" NS   doc-space-before-next-macro-Xr
+.\" NS   doc-section-arg-ptr-Xr
+.\" NS   doc-target-Xr
 .\" NS
 .\" NS width register 'Xr' set in doc-common
 .
@@ -4521,47 +4625,78 @@
 .      ds doc-macro-name Xr
 .      doc-parse-args \$@
 .    \}
-.    el \
+.    el \{\
 .      doc-Xr-usage
+.      return
+.    \}
 .  \}
 .
 .  if !\n[doc-arg-count] \
 .    return
 .
+.  if (\n[doc-arg-count] <= \n[doc-arg-ptr]) \{\
+.    doc-Xr-usage
+.    return
+.  \}
+.
+.  \" The first argument is a man page identifier.
 .  nr doc-arg-ptr +1
-.  doc-print-prefixes
-.  ie (\n[doc-arg-count] >= \n[doc-arg-ptr]) \{\
-.    \" first argument must be a string
-.    ie (\n[doc-type\n[doc-arg-ptr]] == 2) \{\
-.      nr doc-curr-font \n[.f]
-.      ds doc-arg\n[doc-arg-ptr] \*[doc-Xr-font]\*[doc-arg\n[doc-arg-ptr]]\f[]
-.
-.      if (\n[doc-arg-count] > \n[doc-arg-ptr]) \{\
-.        nr doc-reg-Xr (\n[doc-arg-ptr] + 1)
-.        \" modify second argument if it is a string and
-.        \" remove space in between
-.        if (\n[doc-type\n[doc-reg-Xr]] == 2) \{\
-.          ds doc-arg\n[doc-reg-Xr] \*[lp]\*[doc-arg\n[doc-reg-Xr]]\*[rp]
-.          ds doc-space\n[doc-arg-ptr]
-.        \}
-.      \}
-.      doc-print-recursive
-.    \}
-.    el \
-.      doc-Xr-usage
+.  ie (\n[doc-type\n[doc-arg-ptr]] == 2) \
+.    ds doc-target-Xr "\*[doc-arg\n[doc-arg-ptr]]
+.  el \{\
+.    doc-Xr-usage
+.    return
+.  \}
+.
+.  \" Search for trailing punctuation.
+.  nr doc-lasttext-Xr \n[doc-arg-count]
+.  while (\n[doc-lasttext-Xr] >= \n[doc-arg-ptr]) \{\
+.    if !(\n[doc-type\n[doc-lasttext-Xr]] == 3) \
+.      break
+.    nr doc-lasttext-Xr -1
+.  \}
+.
+.  nr doc-has-section-Xr 0
+.  nr doc-space-before-next-macro-Xr 0
+.
+.  if (\n[doc-arg-count] > \n[doc-arg-ptr]) \{\
+.    nr doc-next-arg-ptr-Xr (\n[doc-arg-ptr] + 1)
+.    if (\n[doc-type\n[doc-next-arg-ptr-Xr]] == 1) \
+.      if !'\*[doc-arg\n[doc-next-arg-ptr-Xr]]'Ns' \
+.        nr doc-space-before-next-macro-Xr 1
+.    if (\n[doc-type\n[doc-next-arg-ptr-Xr]] == 2) \
+.      nr doc-has-section-Xr 1
+.  \}
+.
+.  ds doc-target-Xr \*[doc-Xr-font]\*[doc-arg\n[doc-arg-ptr]]\f[]
+.
+.  ie \n[doc-has-section-Xr] \{\
+.    nr doc-section-arg-ptr-Xr (\n[doc-arg-ptr] + 1)
+.    as doc-target-Xr \*[lp]\*[doc-arg\n[doc-section-arg-ptr-Xr]]\*[rp]
 .  \}
 .  el \
-.    doc-Xr-usage
-..
-.ec
+.    if \n[doc-space-before-next-macro-Xr] \
+.      as doc-target-Xr " \"
 .
+.  \" TODO: handle the crazy macOS alternative URL schemata
+.  if \n[doc-has-section-Xr] \
+.    doc-begin-hyperlink-\*[.T] man:\*[doc-target-Xr]
 .
-.\" NS doc-Xr-usage macro
+.  nr doc-curr-font \n[.f]
+.  nop \&\*[doc-target-Xr]\c
+.  nr doc-arg-ptr +1
 .
-.eo
-.de doc-Xr-usage
-.  tm Usage: .Xr manpage_name [section#] ... (#\n[.c])
-.  doc-reset-args
+.  if \n[doc-has-section-Xr] \{\
+.    doc-end-hyperlink-\*[.T]
+.    nr doc-arg-ptr +1
+.  \}
+.
+.  ie (\n[doc-arg-ptr] <= \n[doc-arg-count]) \
+.    doc-print-recursive
+.  el \{\
+.    nop \&
+.    doc-reset-args
+.  \}
 ..
 .ec
 .
@@ -6806,8 +6941,18 @@
 .ec
 .
 .
+.\" NS doc-Mt-usage macro
+.
+.eo
+.de doc-Mt-usage
+.  tm Usage: .Mt email_address ... (#\n[.c])
+.  doc-reset-args
+..
+.ec
+.
+.
 .\" NS Mt user macro
-.\" NS   mailto (for conversion to HTML)
+.\" NS   mailto hyperlink
 .\" NS
 .\" NS modifies:
 .\" NS   doc-arg-ptr
@@ -6816,6 +6961,7 @@
 .\" NS
 .\" NS local variables:
 .\" NS   doc-target-Mt
+.\" NS   doc-visible-target-Mt
 .
 .eo
 .de Mt
@@ -6824,29 +6970,35 @@
 .      ds doc-macro-name Mt
 .      doc-parse-args \$@
 .    \}
-.    el \
-.      tm Usage: .Mt email_address ... (#\n[.c])
+.    el \{\
+.      doc-Mt-usage
+.      return
+.    \}
 .  \}
 .
 .  if !\n[doc-arg-count] \
 .    return
 .
 .  if (\n[doc-arg-count] <= \n[doc-arg-ptr]) \{\
-.    tm Usage: .Mt email_address ... (#\n[.c])
-.    doc-reset-args
+.    doc-Mt-usage
 .    return
 .  \}
 .
 .  \" The argument is the email address.
 .  nr doc-arg-ptr +1
-.  ds doc-target-Mt "\*[doc-arg\n[doc-arg-ptr]]
+.  ds doc-visible-target-Mt "\*[doc-arg\n[doc-arg-ptr]]
+.  ds doc-target-Mt mailto:\*[doc-visible-target-Mt]
 .  nr doc-arg-ptr +1
 .
+.  doc-begin-hyperlink-\*[.T] \*[doc-target-Mt]
+.
 .  \" Format the link target.
 .  nr doc-curr-font \n[.f]
-.  nop \*[doc-Mt-font]\*[doc-target-Mt]\c
+.  nop \*[doc-Mt-font]\*[doc-visible-target-Mt]\c
 .  nop \f[\n[doc-curr-font]]\c
 .
+.  doc-end-hyperlink-\*[.T]
+.
 .  \" Format trailing arguments, like punctuation, if any.
 .  ie (\n[doc-arg-ptr] <= \n[doc-arg-count]) \
 .    doc-print-recursive
@@ -6858,8 +7010,18 @@
 .ec
 .
 .
+.\" NS doc-Lk-usage macro
+.
+.eo
+.de doc-Lk-usage
+.  tm Usage: .Lk uri [link_text ...] (#\n[.c])
+.  doc-reset-args
+..
+.ec
+.
+.
 .\" NS Lk user macro
-.\" NS   link (for conversion to HTML)
+.\" NS   hyperlink
 .\" NS
 .\" NS modifies:
 .\" NS   doc-arg-ptr
@@ -6877,25 +7039,31 @@
 .      ds doc-macro-name Lk
 .      doc-parse-args \$@
 .    \}
-.    el \
-.      tm Usage: .Lk uri [link_text ...] (#\n[.c])
+.    el \{\
+.      doc-Lk-usage
+.      return
+.    \}
 .  \}
 .
 .  if !\n[doc-arg-count] \
 .    return
 .
 .  if (\n[doc-arg-count] <= \n[doc-arg-ptr]) \{\
-.    tm Usage: .Lk uri [link_text ...] (#\n[.c])
-.    doc-reset-args
+.    doc-Lk-usage
 .    return
 .  \}
 .
 .  \" The first argument is the target URI.
 .  nr doc-arg-ptr +1
-.  ds doc-target-Lk "\*[doc-arg\n[doc-arg-ptr]]
+.  ie (\n[doc-type\n[doc-arg-ptr]] == 2) \
+.    ds doc-target-Lk "\*[doc-arg\n[doc-arg-ptr]]
+.  el \{\
+.    doc-Lk-usage
+.    return
+.  \}
 .  nr doc-arg-ptr +1
 .
-.  \" Search backwards for the first closing punctuation.
+.  \" Search for trailing punctuation.
 .  nr doc-lasttext-Lk \n[doc-arg-count]
 .  while (\n[doc-lasttext-Lk] >= \n[doc-arg-ptr]) \{\
 .    if !(\n[doc-type\n[doc-lasttext-Lk]] == 3) \
@@ -6903,25 +7071,31 @@
 .    nr doc-lasttext-Lk -1
 .  \}
 .
+.  doc-begin-hyperlink-\*[.T] \*[doc-target-Lk]
+.
 .  \" Format the link text, if any.
-.  \" XXX: The forced use of the emphasis font and a trailing colon
-.  \" seems intrusive.
 .  nr doc-curr-font \n[.f]
-.  if (\n[doc-arg-ptr] <= \n[doc-lasttext-Lk]) \{\
-.    nop \*[doc-Em-font]\c
+.  ie (\n[doc-arg-ptr] <= \n[doc-lasttext-Lk]) \{\
 .    while (\n[doc-arg-ptr] < \n[doc-lasttext-Lk]) \{\
 .      nop \&\*[doc-arg\n[doc-arg-ptr]]
 .      nr doc-arg-ptr +1
 .    \}
 .    nop \&\*[doc-arg\n[doc-arg-ptr]]\c
-.    nop \f[\n[doc-curr-font]]:
 .    nr doc-arg-ptr +1
-.  \}
 .
-.  \" Format the link target.
-.  nop \*[doc-Lk-font]\*[doc-target-Lk]\c
+.    \" If we won't/can't hyperlink, format the link target.
+.    if !\n[U] \{\
+.      nop \" put a breaking space on the output
+.      nop \[la]\*[doc-target-Lk]\[ra]\c
+.    \}
+.  \}
+.  \" If no link text, format the link target as its own link text.
+.  el \
+.    nop \*[doc-Lk-font]\*[doc-target-Lk]\c
 .  nop \f[\n[doc-curr-font]]\c
 .
+.  doc-end-hyperlink-\*[.T]
+.
 .  \" Format trailing arguments, like punctuation, if any.
 .  ie (\n[doc-arg-ptr] <= \n[doc-arg-count]) \
 .    doc-print-recursive
diff --git a/tmac/groff_mdoc.7.man b/tmac/groff_mdoc.7.man
index 4ccc9ecb0..79bd4e507 100644
--- a/tmac/groff_mdoc.7.man
+++ b/tmac/groff_mdoc.7.man
@@ -5151,6 +5151,25 @@ is ignored when formatting for terminal devices.
 .
 .
 .Pp
+By default,
+.Xr groff
+hyperlinks the formatted text of
+.Ql \&Lk ,
+.Ql \&Mt ,
+and
+.Ql \&Xr
+calls on output devices that support hyperlinking
+.Pf ( Dq html ,
+.Dq pdf ,
+and
+terminal devices).
+.
+Set the
+.Ql U
+register to 0 to disable this feature.
+.
+.
+.Pp
 Setting the
 .Ql X
 register to a page number
diff --git a/tmac/mdoc.local b/tmac/mdoc.local
index 94688aba0..26940e63f 100644
--- a/tmac/mdoc.local
+++ b/tmac/mdoc.local
@@ -2,6 +2,10 @@
 .\"
 .\" Put local modifications to groff_mdoc(7)'s behavior here.
 .\"
+.\" Some nroff devices or pager programs may not gracefully handle OSC 8
+.\" hyperlink escape sequences that they don't understand.
+.\" .if n .if !r U .nr U 0
+.\"
 .\" "CW" is not a portable font name, but some man pages use it anyway.
 .\" Uncomment this to suppress warnings produced by such pages.  This
 .\" test remaps the font to roman ("R") on nroff (terminal) devices. You
diff --git a/tmac/tests/doc_Lk-respects-sentence-ending-punctuation.sh 
b/tmac/tests/doc_Lk-respects-sentence-ending-punctuation.sh
index 7f2cc22ba..b3eccc364 100755
--- a/tmac/tests/doc_Lk-respects-sentence-ending-punctuation.sh
+++ b/tmac/tests/doc_Lk-respects-sentence-ending-punctuation.sh
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright (C) 2022 Free Software Foundation, Inc.
+# Copyright (C) 2022-2024 Free Software Foundation, Inc.
 #
 # This file is part of groff.
 #
@@ -20,6 +20,13 @@
 
 groff="${abs_top_builddir:-.}/test-groff"
 
+fail=
+
+wail() {
+    echo ...FAILED >&2
+    fail=yes
+}
+
 # Regression-test Savannah #59738.  The processing of arguments after
 # the link text should not break end-of-sentence detection.
 
@@ -34,7 +41,14 @@ Click
 .Lk http://example.com here .
 Follow instructions.'
 
-output=$(echo "$input" | "$groff" -Tascii -P-cbou -mdoc)
-echo "$output" | grep -Fq 'com.  Follow' # 2 spaces
+echo "checking output when hyperlinks disabled" >&2
+output=$(echo "$input" | "$groff" -rU0 -Tascii -P-cbou -mdoc)
+echo "$output"
+echo "$output" | grep -Fq 'com>.  Follow' || wail # 2 spaces
+
+echo "checking output when hyperlinks enabled" >&2
+output=$(echo "$input" | "$groff" -rU1 -Tascii -P-cbou -mdoc)
+echo "$output"
+echo "$output" | grep -Fq 'here.  Follow' || wail # 2 spaces
 
 # vim:set ai et sw=4 ts=4 tw=72:
diff --git a/tmac/tests/doc_Lk-works.sh b/tmac/tests/doc_Lk-works.sh
index 7ad621be3..20d1dbbff 100755
--- a/tmac/tests/doc_Lk-works.sh
+++ b/tmac/tests/doc_Lk-works.sh
@@ -43,28 +43,48 @@ and you get
 Follow instructions
 .Pf ( Lk http://\:hidden\:.example\:.com elsewhere ) .'
 
-output=$(echo "$input" | "$groff" -mdoc -Tascii -P-cbou)
+output=$(echo "$input" | "$groff" -rU0 -mdoc -Tascii -P-cbou)
 echo "$output"
 
 # Expected:
-#     Sometimes   you   click   one   link:   http://example.com   and   you  
get
+#     Sometimes   you   click   one   link   <http://example.com>   and  you  
get
 #     http://another.example.com.
 #
-#     Follow instructions (elsewhere: http://hidden.example.com).
+#     Follow instructions (elsewhere <http://hidden.example.com>).
 
-echo "checking that conventional Lk macro call works" >&2
+echo "checking that conventional Lk macro call works (-rU0)" >&2
 echo "$output" \
     | grep -Eq '^ +http://another\.example\.com' || wail
 
-echo "checking that inline Lk macro call works" >&2
+echo "checking that inline Lk macro call works (-rU0)" >&2
 echo "$output" \
-    | grep -Eq 'one +link: +http://example\.com' || wail
+    | grep -Eq 'one +link +<http://example\.com>' || wail
 
-echo "checking that prefixed Lk macro call works" >&2
+echo "checking that prefixed Lk macro call works (-rU0)" >&2
 echo "$output" \
-    | grep -Fq 'instructions (elsewhere: http://hidden.example.com).' \
+    | grep -Fq 'instructions (elsewhere <http://hidden.example.com>).' \
     || wail
 
+output=$(echo "$input" | "$groff" -rU1 -mdoc -Tascii -P-cbou)
+echo "$output"
+
+# Expected:
+#     Sometimes you click one link and you get http://another.example.com.
+#
+#     Follow instructions (elsewhere).
+
+echo "checking that conventional Lk macro call works (-rU1)" >&2
+echo "$output" \
+    | grep -q 'Sometimes you click one link and' || wail
+
+echo "checking that inline Lk macro call works (-rU1)" >&2
+echo "$output" \
+    | grep -Fq 'you get http://another.example.com.' || wail
+
+echo "checking that prefixed Lk macro call works (-rU1)" >&2
+echo "$output" \
+    | grep -Fq 'Follow instructions (elsewhere).' || wail
+
 test -z "$fail"
 
 # vim:set ai et sw=4 ts=4 tw=72:
diff --git a/tmac/tests/doc_Mt-works.sh b/tmac/tests/doc_Mt-works.sh
index 4a6b9fbb7..ecbe595e6 100755
--- a/tmac/tests/doc_Mt-works.sh
+++ b/tmac/tests/doc_Mt-works.sh
@@ -50,18 +50,33 @@ Certainly that scoundrel Bob Bogus
 .Pf ( Mt bogus@example.com )
 played no role.'
 
-output=$(echo "$input" | "$groff" -mdoc -Tascii -P-cbou)
+output=$(echo "$input" | "$groff" -rU0 -mdoc -Tascii -P-cbou)
 echo "$output"
 
-echo "checking that conventional Mt macro call works" >&2
+echo "checking that conventional Mt macro call works (-rU0)" >&2
 echo "$output" \
     | grep -Eq '^ +kristaps@bsd\.lv,' || wail
 
-echo "checking that inline Mt macro call works" >&2
+echo "checking that inline Mt macro call works (-rU0)" >&2
 echo "$output" \
     | grep -Eq 'Schwarze +<schwarze@openbsd\.org>' || wail
 
-echo "checking that prefixed Mt macro call works" >&2
+echo "checking that prefixed Mt macro call works (-rU0)" >&2
+echo "$output" \
+    | grep -Fq 'Bogus (bogus@example.com) played' || wail
+
+output=$(echo "$input" | "$groff" -rU1 -mdoc -Tascii -P-cbou)
+echo "$output"
+
+echo "checking that conventional Mt macro call works (-rU1)" >&2
+echo "$output" \
+    | grep -Eq '^ +kristaps@bsd\.lv,' || wail
+
+echo "checking that inline Mt macro call works (-rU1)" >&2
+echo "$output" \
+    | grep -Eq 'Schwarze +<schwarze@openbsd\.org>' || wail
+
+echo "checking that prefixed Mt macro call works (-rU1)" >&2
 echo "$output" \
     | grep -Fq 'Bogus (bogus@example.com) played' || wail
 
diff --git a/tmac/tests/doc_Xr-works.sh b/tmac/tests/doc_Xr-works.sh
index d10225d40..b641ced9e 100755
--- a/tmac/tests/doc_Xr-works.sh
+++ b/tmac/tests/doc_Xr-works.sh
@@ -65,37 +65,78 @@ about
 and
 .Xr groff Ns - Ns Xr ms .'
 
-output=$(echo "$input" | "$groff" -mdoc -Tascii -P-cbou 2>/dev/null)
-error=$(echo "$input" | "$groff" -mdoc -Tascii -P-cbou 2>&1 >/dev/null)
+output=$(echo "$input" | "$groff" -rU0 -mdoc -Tascii -P-cbou \
+    2>/dev/null)
+error=$(echo "$input" | "$groff" -rU0 -mdoc -Tascii -P-cbou \
+    2>&1 >/dev/null)
 echo "$output"
 echo "$error"
 
-echo "checking for error diagnostic on argumentless Xr call" >&2
+echo "checking for error diagnostic on argumentless Xr call (-rU0)" >&2
 echo "$error" | grep -q '9' || wail
 
-echo "checking two-argument Xr call with full cross reference" >&2
+echo "checking two-argument Xr call with full cross reference (-rU0)" \
+    >&2
 echo "$output" \
     | grep -Fq 'Read the groff(1) man page.' || wail
 
 echo "checking two-argument Xr call with partial reference and" \
-    "trailing punctuation" >&2
+    "trailing punctuation (-rU0)" >&2
 echo "$output" \
     | grep -Eq 'Read +the +man +page +groff\(1\), +reflect,' || wail
 
-echo "checking a series of partial references, some punctuated" >&2
+echo "checking a series of partial references, some punctuated (-rU0)" \
+    >&2
 echo "$output" \
     | grep -Fq 'The groff, groff_diff, and roff pages' || wail
 
-echo "checking that prefixed Xr macro call works" >&2
+echo "checking that prefixed Xr macro call works (-rU0)" >&2
 echo "$output" \
     | grep -Fq '(groff_font(5) is also worth a look.)' || wail
 
 # We don't expect an inline Pf call to prevent a word break before it.
-echo "checking that inline Pf after Xr macro call works" >&2
+echo "checking that inline Pf after Xr macro call works (-rU0)" >&2
 echo "$output" | grep -Fq 'read about groff mdoc' || wail
 
 # But we do expect Ns to do so.
-echo "checking that inline Ns after Xr macro call works" >&2
+echo "checking that inline Ns after Xr macro call works (-rU0)" >&2
+echo "$output" | grep -Fq 'Or about groff -man and groff-ms.' || wail
+
+output=$(echo "$input" | "$groff" -rU1 -mdoc -Tascii -P-cbou \
+    2>/dev/null)
+error=$(echo "$input" | "$groff" -rU1 -mdoc -Tascii -P-cbou \
+    2>&1 >/dev/null)
+echo "$output"
+echo "$error"
+
+echo "checking for error diagnostic on argumentless Xr call (-rU1)" >&2
+echo "$error" | grep -q '9' || wail
+
+echo "checking two-argument Xr call with full cross reference (-rU1)" \
+    >&2
+echo "$output" \
+    | grep -Fq 'Read the groff(1) man page.' || wail
+
+echo "checking two-argument Xr call with partial reference and" \
+    "trailing punctuation (-rU1)" >&2
+echo "$output" \
+    | grep -Eq 'Read +the +man +page +groff\(1\), +reflect,' || wail
+
+echo "checking a series of partial references, some punctuated (-rU1)" \
+    >&2
+echo "$output" \
+    | grep -Fq 'The groff, groff_diff, and roff pages' || wail
+
+echo "checking that prefixed Xr macro call works (-rU1)" >&2
+echo "$output" \
+    | grep -Fq '(groff_font(5) is also worth a look.)' || wail
+
+# We don't expect an inline Pf call to prevent a word break before it.
+echo "checking that inline Pf after Xr macro call works (-rU1)" >&2
+echo "$output" | grep -Fq 'read about groff mdoc' || wail
+
+# But we do expect Ns to do so.
+echo "checking that inline Ns after Xr macro call works (-rU1)" >&2
 echo "$output" | grep -Fq 'Or about groff -man and groff-ms.' || wail
 
 test -z "$fail"
diff --git a/tmac/tests/doc_heading-font-remapping-works.sh 
b/tmac/tests/doc_heading-font-remapping-works.sh
index d8fae14f1..58e65697e 100755
--- a/tmac/tests/doc_heading-font-remapping-works.sh
+++ b/tmac/tests/doc_heading-font-remapping-works.sh
@@ -45,8 +45,18 @@ echo "$output"
 output=$(printf "%s\n" "$input" | "$groff" -mdoc -Tascii -Z)
 echo "$output"
 
+# Expected:
+#
+# tHacking
+# wx font 4 BI
+# f4
+# h24
+# tgroff
+# n40 0
+# f1
+
 echo "$output" | sed -n '/tHacking/{n
-/x font 4 BI/{n
+/w *x font 4 BI/{n
 /f4/{n
 /h/{n
 /tgroff/{n



reply via email to

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