gbranden pushed a commit to branch master in repository groff. commit 8d53c2f4f38723164f64f5df94861ea1bc2fb2dc Author: G. Branden Robinson <g.branden.robin...@gmail.com> AuthorDate: Wed Jul 16 12:10:45 2025 -0500
[mdoc]: Fix Savannah #67363 (4/4). Make man page rendering more robust against meddling with the adjustment 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 with Russ Allbery (about man(7)) in late 2023. * tmac/doc.tmac ([initialization]): When rendering a man page, this macro file is read before any mdoc(7) document; therefore, if the `AD` string is set, we know it was specified on the command line or by the "mdoc.local" file. Stash its value in new string `andoc*AD` so it can be recovered after meddling by the document. * tmac/mdoc/doc-common (Dd): Remove the potentially page-local `AD` string when starting a new document, and call `an*reset-adjustment-mode` interpolating `andoc*AD` as an argument to impose the user's preference (or the package default) at each new document. (doc-reset-adjustment-mode): New macro. Recover that value. Compute the desired adjustment mode based on, in descending priority: (1) the user's preference; (2) the page's preference. (Sh): Configure adjustment _after_ determining formatting parameters dependent on the name of the section heading. Move `na` request out of control branch matching the `doc-sec-head` and `doc-section-synopsis` strings. Later, invoke `na` if we're in a synopsis section and `doc-reset-adjustment-mode` otherwise. (Ss): Invoke `na` if we're in a synopsis section and `doc-reset-adjustment-mode` otherwise. * tmac/tests/doc_user-preferred-adjustment-restored.sh: Add test to verify preservation of user-selected adjustment mode. * tmac/tmac.am (tmac_TESTS): Run test. Fixes <https://savannah.gnu.org/bugs/?67363> (4/4). Thanks to Russ Allbery for posing the challenge. --- ChangeLog | 38 +++++++ tmac/doc.tmac | 9 +- tmac/mdoc/doc-common | 80 +++++++++++++-- .../tests/doc_adjustment-mode-restoration-works.sh | 113 +++++++++++++++++++++ tmac/tmac.am | 1 + 5 files changed, 227 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 954466dc5..417da6344 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +2025-07-27 G. Branden Robinson <g.branden.robin...@gmail.com> + + [mdoc]: Make man page rendering more robust against meddling + with the adjustment 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 + with Russ Allbery (about man(7)) in late 2023. + + * tmac/doc.tmac ([initialization]): When rendering a man page, + this macro file is read before any mdoc(7) document; therefore, + if the `AD` string is set, we know it was specified on the + command line or by the "mdoc.local" file. Stash its value in + new string `andoc*AD` so it can be recovered after meddling by + the document. + * tmac/mdoc/doc-common (Dd): Remove the potentially page-local + `AD` string when starting a new document, and call + `an*reset-adjustment-mode` interpolating `andoc*AD` as an + argument to impose the user's preference (or the package + default) at each new document. + (doc-reset-adjustment-mode): New macro. Recover that value. + Compute the desired adjustment mode based on, in descending + priority: (1) the user's preference; (2) the page's preference. + (Sh): Configure adjustment _after_ determining formatting + parameters dependent on the name of the section heading. Move + `na` request out of control branch matching the `doc-sec-head` + and `doc-section-synopsis` strings. Later, invoke `na` if we're + in a synopsis section and `doc-reset-adjustment-mode` otherwise. + (Ss): Invoke `na` if we're in a synopsis section and + `doc-reset-adjustment-mode` otherwise. + + * tmac/tests/doc_user-preferred-adjustment-restored.sh: Add test + to verify preservation of user-selected adjustment mode. + * tmac/tmac.am (tmac_TESTS): Run test. + + Fixes <https://savannah.gnu.org/bugs/?67363> (4/4). Thanks to + Russ Allbery for posing the challenge. + 2025-07-27 G. Branden Robinson <g.branden.robin...@gmail.com> [mdoc]: Make man page rendering more robust against meddling diff --git a/tmac/doc.tmac b/tmac/doc.tmac index 94bddf47e..7b97bf8d7 100644 --- a/tmac/doc.tmac +++ b/tmac/doc.tmac @@ -99,10 +99,6 @@ .if r ps4html \ . nr doc-is-output-html 1 . -.\" adjustment mode -.if !d AD \ -. ds AD b\" -. .\" base paragraph indentation .if !r BP \ . nr BP 5n @@ -7212,10 +7208,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. +.\" Save `HY` value and `AD` contents 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 +. ie d AD .ds andoc*AD \*[AD]\" +. el .ds andoc*AD b\" . nr andoc*is-initialized 1 .\} . diff --git a/tmac/mdoc/doc-common b/tmac/mdoc/doc-common index 3bf54839e..d7e38f29c 100644 --- a/tmac/mdoc/doc-common +++ b/tmac/mdoc/doc-common @@ -309,11 +309,14 @@ . if \n[C] .rr P . . \" When rendering multiple documents, we want to clear any page-local -. \" manipulation of hyphenation mode from the previous document. +. \" manipulation of hyphenation and adjustment modes from the previous +. \" document. . rr HY +. rm AD . . doc-reset-titles . doc-reset-hyphenation-mode \n[andoc*HY] +. doc-reset-adjustment-mode \*[andoc*AD] . . if !\n[.$] \ . doc-warn .Dd directive expects an argument @@ -1131,6 +1134,55 @@ .ec . . +.\" NS doc-reset-adjustment-mode +.\" NS Repair output line adjustment after man page meddling. +.\" +.\" Resetting the adjustment mode is a complicated dance. +.\" 1. Man pages sometimes disable adjustment--when they do, they +.\" often forget to put it back the way it was. +.\" 2. When they do remember to put it back, they often fail to do +.\" so correctly because of the `ad` request's quirky semantics +.\" starting from Seventh Edition Unix troff/nroff. Briefly, the +.\" `ad` request without arguments turns adjustment back on after +.\" an `na` even if the previous adjustment mode was `l` (align to +.\" the left with NO adjustment). +.\" 3. The default adjustment mode historically has not been +.\" predictable; it can depend on nroff vs. troff mode and on the +.\" vendor of the *roff system in use. +.\" 4. It's possible (and portable) to obtain the previous adjustment +.\" mode via the `.j` register so that it can be saved prior to +.\" meddling and restored later, but in practice man page authors +.\" neglect to do so. +.\" 5. groff mdoc(7)'s `AD` string isn't supported everywhere. +.\" 6. We want user preferences, if expressed, to override the page +.\" author's. +.\" 7. Even if we didn't want (6), one page author's can override +.\" another's when formatting multiple mdoc(7) documents in +.\" sequence--we thus keep track of the initial adjustment mode. +.\" +.\" So we recover the "page's" preferred adjustment mode, if expressed +.\" via `AD`, at every paragraph (and (sub)sectioning) macro call; and +.\" recover the user's preferred adjustment 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". +.\" +.\" This macro is at pains to distinguish alignment from adjustment. +. +.eo +.de doc-reset-adjustment-mode +. if \n[.$] .ds AD \$1\" +. nr doc-want-adjustment 1 +. if d AD .if '\?\*[AD]\?'\?l\?' .nr doc-want-adjustment 0 +. if d AD .if '\?\*[AD]\?'\?c\?' .nr doc-want-adjustment 0 +. if d AD .if '\?\*[AD]\?'\?r\?' .nr doc-want-adjustment 0 +. ie \n[doc-want-adjustment] .ds doc-adjustment-mode b +. el .ds doc-adjustment-mode l +. ad \*[doc-adjustment-mode] +. rr doc-want-adjustment +.. +.ec +. +. .\" NS doc-reset-hyphenation-mode .\" NS Repair automatic hyphenation after man page meddling. .\" @@ -1286,6 +1338,7 @@ . .eo .de Nd +. doc-reset-adjustment-mode . doc-reset-hyphenation-mode . nop \[em] \$* .. @@ -1453,8 +1506,6 @@ . ds doc-macro-name Sh . doc-parse-args \$@ . -. ad \*[AD] -. . ie "\*[doc-sec-head]"\*[doc-section-name]" \{\ . doc-set-up-titles . in 0 @@ -1468,7 +1519,6 @@ . nr doc-in-authors-section 0 . . ie "\*[doc-sec-head]"\*[doc-section-synopsis]" \{\ -. na . nr doc-in-synopsis-section 1 . nr doc-indent-synopsis 0 . nr doc-indent-synopsis-active 0 @@ -1495,8 +1545,14 @@ . nr doc-have-author 0 . \} . -. ie \n[doc-in-synopsis-section] .nh -. el .doc-reset-hyphenation-mode +. ie \n[doc-in-synopsis-section] \{\ +. na +. nh +. \} +. el \{\ +. doc-reset-adjustment-mode +. doc-reset-hyphenation-mode +. \} . . doc-setup-page-layout . sp \n[doc-paragraph-space]u @@ -1583,11 +1639,17 @@ . ds doc-macro-name Ss . doc-parse-args \$@ . -. ie \n[doc-in-synopsis-section] .nh -. el .doc-reset-hyphenation-mode -. . ds doc-subsection-heading \$* . +. ie \n[doc-in-synopsis-section] \{\ +. na +. nh +. \} +. el \{\ +. doc-reset-adjustment-mode +. doc-reset-hyphenation-mode +. \} +. . sp \n[doc-paragraph-space]u . if !\n[cR] \ . ne 3 diff --git a/tmac/tests/doc_adjustment-mode-restoration-works.sh b/tmac/tests/doc_adjustment-mode-restoration-works.sh new file mode 100755 index 000000000..b51ac3b24 --- /dev/null +++ b/tmac/tests/doc_adjustment-mode-restoration-works.sh @@ -0,0 +1,113 @@ +#!/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" +.ds AD l +.Sh Name +.Nm foo +.Nd a command with a very short name +.Sh Description +This paragraph should not be adjusted. +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 adjusted. +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 adjusted. +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. +.ds AD b \" 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 adjusted. +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 70 makes adjustment obvious on the first line of +# paragraph text in each "Description". +output=$(printf "%s\n" "$input" \ + | "$groff" -rLL=70n -mdoc -T ascii -P -cbou) +echo "$output" + +echo "checking that the first document's first paragraph is not" \ + "adjusted" >&2 +echo "$output" | grep -q 'totam rem aperiam eaque ipsa' || wail + +echo "checking that the first document's second paragraph is not" \ + "adjusted" >&2 +echo "$output" | grep -q 'ullam corporis suscipitlabo-' || wail + +echo "checking that the second document's paragraph is adjusted" >&2 +# Be robust to adjustment parity value. +if ! echo "$output" | grep -Eq 'Nemo enim +ipsam +voluptatem,' \ + && ! echo "$output" | grep -Eq 'This +paragraph +should +be' +then + wail +fi + +output=$(printf "%s\n" "$input" \ + | "$groff" -dAD=l -rLL=78n -mdoc -T ascii -P -cbou) +echo "$output" + +echo "checking that the third document's paragraph is not adjusted" \ + "when disabled by user" >&2 +# Be robust to adjustment parity value. +if ! echo "$output" | grep -q 'vel illum,' \ + && ! echo "$output" | grep -q 'nulla pariatur\?' +then + wail +fi + +test -z "$fail" + +# vim:set ai et sw=4 ts=4 tw=72: diff --git a/tmac/tmac.am b/tmac/tmac.am index 3c320a2c5..3b72ea7a5 100644 --- a/tmac/tmac.am +++ b/tmac/tmac.am @@ -202,6 +202,7 @@ tmac_TESTS = \ tmac/tests/doc_X-register-works.sh \ tmac/tests/doc_Xr-works.sh \ tmac/tests/doc_accept-mixed-case-section-headings.sh \ + tmac/tests/doc_adjustment-mode-restoration-works.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 \ _______________________________________________ groff-commit mailing list groff-commit@gnu.org https://lists.gnu.org/mailman/listinfo/groff-commit