gbranden pushed a commit to branch master
in repository groff.

commit e0a498b6bc2a0b209616910bfa16d33a8453139c
Author: G. Branden Robinson <g.branden.robin...@gmail.com>
AuthorDate: Mon Jul 21 19:27:59 2025 -0500

    [mdoc]: Fix Savannah #67363 (3/4).
    
    Make man page rendering more robust against meddling with the
    hyphenation mode by individual pages.  In part this is to ensure that
    meddling doesn't persist outside the meddlesome document when rendering
    multiple pages, but it also makes user preferences more reliably
    discernible.  Prompted by a discussion (about adjustment in man(7)) with
    Russ Allbery in late 2023.
    
    * tmac/doc.tmac ([initialization]): When rendering a man page, this
      macro file is read before any mdoc(7) document:track man/mdoc
      initialization status in new register `andoc*is-initialized`.  When
      initializing, if the `HY` register is set, we know it was specified on
      the command line or by the "man.local" file.  Stash its value in new
      register `andoc*HY` so it can be recovered after meddling by the
      document.
    
    * tmac/mdoc/doc-common (Dd): Remove the potentially page-local `HY`
      register when starting a new document, and call
      `doc-reset-hyphenation-mode` interpolating `andoc*HY` as an argument
      to impose the user's preference (or the package default) at each new
      document.
    
      (doc-reset-hyphenation-mode): Accept an argument, and if valid, assign
      its value to the `HY` register.
    
      (Sh): Configure hyphenation _after_ determining formatting parameters
      dependent on the name of the section heading.  Move `nh` request out
      of control branch matching the `doc-sec-head` and
      `doc-section-synopsis` strings.  Later, invoke `nh` if we're in a
      synopsis section and `doc-reset-hyphenation-mode` otherwise.
    
      (Ss): Invoke `nh` if we're in a synopsis section and
      `doc-reset-hyphenation-mode` otherwise.  As a side effect, this
      subjects subsection headings to hyphenation.  (Section headings
      produced by the `Sh` macro already were.)
    
    * tmac/tests/doc_hyphenation-mode-restoration-works.sh: Add test to
      verify preservation of document- and user-selected hyphenation modes.
    * tmac/tmac.am (tmac_TESTS): Run test.
    
    Fixes <https://savannah.gnu.org/bugs/?67363> (3/4).  Thanks to Russ
    Allbery for posing the challenge.
---
 ChangeLog                                          |  42 +++++++++
 tmac/doc.tmac                                      |   9 +-
 tmac/mdoc/doc-common                               |  34 ++++++-
 .../doc_hyphenation-mode-restoration-works.sh      | 100 +++++++++++++++++++++
 tmac/tmac.am                                       |   1 +
 5 files changed, 180 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d39d27264..954466dc5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2025-07-27  G. Branden Robinson <g.branden.robin...@gmail.com>
+
+       [mdoc]: Make man page rendering more robust against meddling
+       with the hyphenation mode by individual pages.  In part this is
+       to ensure that meddling doesn't persist outside the meddlesome
+       document when rendering multiple pages, but it also makes user
+       preferences more reliably discernible.  Prompted by a discussion
+       {about adjustment in man(7)} with Russ Allbery in late 2023.
+
+       * tmac/doc.tmac ([initialization]): When rendering a man page,
+       this macro file is read before any mdoc(7) document:track
+       man/mdoc initialization status in new register
+       `andoc*is-initialized`.  When initializing, if the `HY` register
+       is set, we know it was specified on the command line or by the
+       "man.local" file.  Stash its value in new register `andoc*HY` so
+       it can be recovered after meddling by the document.
+       * tmac/mdoc/doc-common (Dd): Remove the potentially page-local
+       `HY` register when starting a new document, and call
+       `doc-reset-hyphenation-mode` interpolating `andoc*HY` as an
+       argument to impose the user's preference (or the package
+       default) at each new document.
+       (doc-reset-hyphenation-mode): Accept an argument, and if valid,
+       assign its value to the `HY` register.
+       (Sh): Configure hyphenation _after_ determining formatting
+       parameters dependent on the name of the section heading.  Move
+       `nh` request out of control branch matching the `doc-sec-head`
+       and `doc-section-synopsis` strings.  Later, invoke `nh` if we're
+       in a synopsis section and `doc-reset-hyphenation-mode`
+       otherwise.
+       (Ss): Invoke `nh` if we're in a synopsis section and
+       `doc-reset-hyphenation-mode` otherwise.  As a side effect, this
+       subjects subsection headings to hyphenation.  (Section headings
+       produced by the `Sh` macro already were.)
+
+       * tmac/tests/doc_hyphenation-mode-restoration-works.sh: Add test
+       to verify preservation of document- and user-selected
+       hyphenation modes.
+       * tmac/tmac.am (tmac_TESTS): Run test.
+
+       Fixes <https://savannah.gnu.org/bugs/?67363> (3/4).  Thanks to
+       Russ Allbery for posing the challenge.
+
 2025-07-27  G. Branden Robinson <g.branden.robin...@gmail.com>
 
        [man]: Make man page rendering more robust against meddling with
diff --git a/tmac/doc.tmac b/tmac/doc.tmac
index d2b934861..94bddf47e 100644
--- a/tmac/doc.tmac
+++ b/tmac/doc.tmac
@@ -213,8 +213,6 @@
 .rr doc-HF-length
 .rm doc-heading-style
 .
-.\" `doc-reset-hyphenation-mode` handles the `HY` register.
-.
 .\" groff mdoc does _not_ use groff man's `IN` register.
 .
 .\" LL and LT registers are handled by the doc-setup-page-layout macro.
@@ -7214,6 +7212,13 @@ should this have been '.Em ...'?
 .ie d msoquiet .msoquiet mdoc.local
 .el            .mso      mdoc.local
 .
+.\" Save `HY` value configured by {man,mdoc}.local or command line.
+.if !r andoc*is-initialized \{\
+.  ie r HY .nr andoc*HY \n[HY]
+.  el      .nr andoc*HY 1
+.  nr andoc*is-initialized 1
+.\}
+.
 .cp \n[*groff_doc_tmac_C]
 .do rr *groff_doc_tmac_C
 .
diff --git a/tmac/mdoc/doc-common b/tmac/mdoc/doc-common
index bd2539fb4..3bf54839e 100644
--- a/tmac/mdoc/doc-common
+++ b/tmac/mdoc/doc-common
@@ -308,8 +308,12 @@
 .  \}
 .  if \n[C] .rr P
 .
+.  \" When rendering multiple documents, we want to clear any page-local
+.  \" manipulation of hyphenation mode from the previous document.
+.  rr HY
+.
 .  doc-reset-titles
-.  doc-reset-hyphenation-mode
+.  doc-reset-hyphenation-mode \n[andoc*HY]
 .
 .  if !\n[.$] \
 .    doc-warn .Dd directive expects an argument
@@ -1129,9 +1133,29 @@
 .
 .\" NS doc-reset-hyphenation-mode
 .\" NS   Repair automatic hyphenation after man page meddling.
+.\"
+.\" Resetting the hyphenation mode is a complicated dance.
+.\"   1.  Man pages sometimes disable automatic hyphenation--when they
+.\"       do, they nearly always forget to put it back the way it was.
+.\"   2.  In AT&T troff there was no register exposing the hyphenation
+.\"       mode (nor the enablement status of automatic hyphenation), so
+.\"       no idioms for performing such restoration have arisen.
+.\"   3.  groff mdoc(7)'s `HY` register isn't supported everywhere.
+.\"   4.  We want user preferences, if expressed, to override the page
+.\"       author's.
+.\"   5.  Even if we didn't want (4), one page author's can override
+.\"       another's when formatting multiple mdoc(7) documents in
+.\"       sequence--we thus keep track of the initial hyphenation mode.
+.\"
+.\" So we recover the "page's" preferred hyphenation mode, if expressed
+.\" via `HY`, at every paragraph (and (sub)sectioning macro call; and
+.\" recover the user's preferred hyphenation mode at each new document
+.\" by accepting it as an argument (see the `Dd` macro definition).
+.\" Also see the initialization logic in "tmac/doc.tmac".
 .
 .eo
 .de doc-reset-hyphenation-mode
+.  if \n[.$] .if \B'\$1' .nr HY \$1
 .  nr doc-want-hyphenation 1
 .  if r HY .ie !\n[HY] .nr doc-want-hyphenation 0
 .  ie \n[doc-want-hyphenation] \
@@ -1430,7 +1454,6 @@
 .  doc-parse-args \$@
 .
 .  ad \*[AD]
-.  doc-reset-hyphenation-mode
 .
 .  ie "\*[doc-sec-head]"\*[doc-section-name]" \{\
 .    doc-set-up-titles
@@ -1446,7 +1469,6 @@
 .
 .    ie        "\*[doc-sec-head]"\*[doc-section-synopsis]" \{\
 .      na
-.      nh
 .      nr doc-in-synopsis-section 1
 .      nr doc-indent-synopsis 0
 .      nr doc-indent-synopsis-active 0
@@ -1473,6 +1495,9 @@
 .    nr doc-have-author 0
 .  \}
 .
+.  ie \n[doc-in-synopsis-section] .nh
+.  el                             .doc-reset-hyphenation-mode
+.
 .  doc-setup-page-layout
 .  sp \n[doc-paragraph-space]u
 .  ns
@@ -1558,7 +1583,8 @@
 .  ds doc-macro-name Ss
 .  doc-parse-args \$@
 .
-.  doc-reset-hyphenation-mode
+.  ie \n[doc-in-synopsis-section] .nh
+.  el                             .doc-reset-hyphenation-mode
 .
 .  ds doc-subsection-heading \$*
 .
diff --git a/tmac/tests/doc_hyphenation-mode-restoration-works.sh 
b/tmac/tests/doc_hyphenation-mode-restoration-works.sh
new file mode 100755
index 000000000..785bdd27c
--- /dev/null
+++ b/tmac/tests/doc_hyphenation-mode-restoration-works.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# Copyright (C) 2025 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+groff="${abs_top_builddir:-.}/test-groff"
+
+fail=
+
+wail() {
+    echo ...FAILED >&2
+    fail=yes
+}
+
+input='.
+.Dd 2025-07-16
+.Dt foo 1
+.Os "groff test suite"
+.nr HY 0
+.Sh Name
+.Nm foo
+.Nd a command with a very short name
+.Sh Description
+This paragraph should not be hyphenated.
+Sed ut perspiciatis, unde omnis iste natus error sit voluptatem
+accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab
+illo inventore veritatis et quasi architecto beatae vitae dicta sunt,
+explicabo.
+.Pp
+This paragraph should also not be hyphenated.
+Ut enim ad minima veniam, quis nostrum exercitationem ullam
+corporis suscipitlaboriosam suscipitlaboriosam suscipitlaboriosam,
+nisi ut aliquid ex ea commodi consequatur?
+.Dd 2025-07-16
+.Dt bar 1
+.Os "groff test suite"
+.Sh Name
+.Nm bar
+.Nd another command with a very short name
+.Sh Description
+This paragraph should be hyphenated.
+Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur
+aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione
+voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum,
+quia dolor sit amet consectetur adipiscivelit, sed quia non-numquam eius
+modi tempora incidunt, ut labore et dolore magnam aliquam quaerat
+voluptatem.
+.nr HY 1 \" attempt to meddle with next document
+.Dd 2025-07-27
+.Dt baz 1
+.Os "groff test suite"
+.Sh Name
+.Nm baz
+.Nd you guessed it
+.Sh Description
+This paragraph should not be hyphenated.
+Quis autem vel eum iure reprehenderit, qui inea voluptate velit esse,
+quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo
+voluptas nulla pariatur?
+.'
+
+# A line length of 78n avoids adjustment on the hyphenated line.
+output=$(printf "%s\n" "$input" \
+    | "$groff" -rLL=78n -mdoc -T ascii -P -cbou)
+echo "$output"
+
+echo "checking that the first document's first paragraph is not" \
+    "hyphenated" >&2
+echo "$output" | grep -q 'veritatis  *et  *quasi$' || wail
+
+echo "checking that the first document's second paragraph is not" \
+    "hyphenated" >&2
+echo "$output" | grep -q 'ullam  *corporis  *suscipitlaboriosam' || wail
+
+echo "checking that the second document's paragraph is hyphenated" >&2
+echo "$output" | grep -q 'ut labore et dolore mag-' || wail
+
+output=$(printf "%s\n" "$input" \
+    | "$groff" -rHY=0 -rLL=78n -mdoc -T ascii -P -cbou)
+echo "$output"
+
+echo "checking that the third document's paragraph is not hyphenated" \
+    "when disabled by user" >&2
+echo "$output" | grep -q 'autem  *vel  *eum  *iure' || wail
+
+# vim:set ai et sw=4 ts=4 tw=72:
diff --git a/tmac/tmac.am b/tmac/tmac.am
index 303103653..3c320a2c5 100644
--- a/tmac/tmac.am
+++ b/tmac/tmac.am
@@ -204,6 +204,7 @@ tmac_TESTS = \
   tmac/tests/doc_accept-mixed-case-section-headings.sh \
   tmac/tests/doc_do-not-loop-infinitely-when-shortening-headers.sh \
   tmac/tests/doc_heading-font-remapping-works.sh \
+  tmac/tests/doc_hyphenation-mode-restoration-works.sh \
   tmac/tests/doc_indents-correctly.sh \
   tmac/tests/doc_output-footer-when-continuously-rendering.sh \
   tmac/tests/doc_reset-data-between-documents.sh \

_______________________________________________
groff-commit mailing list
groff-commit@gnu.org
https://lists.gnu.org/mailman/listinfo/groff-commit

Reply via email to