Hi onf,

Hadn't forgotten about this, but I did put it on the shelf for the sake
of getting a release out.

I've done that.

At 2025-12-08T05:49:33+0100, onf wrote:
> (Sorry for the missing In-Reply-To; you forgot to Cc me and the list
> archive's "reply via email" button doesn't seem to work for me.)

Let's see if I manage it this time.  ;-)

I'm trying to write a unit test for `sv` and `os`, so this issue pressed
its way back to the forefront of my mind.

> On Sun Dec 7, 2025 at 9:19 AM CET, G. Branden Robinson wrote:
> > At 2025-11-21T20:28:49+0100, onf wrote:
> > [...] 
> > > Groff gives the same output when the wh line is
> > > changed to
> > >   .wh 2v+1u x
> > > 
> > > (in troff mode; in nroff mode, +1v is needed due to lower
> > > resolution)
> > 
> > Strictly, +0.5v plus 1 motion quantum should do the trick.  At least
> > it does for me (approximating "one motion quantum" as ".03v").
> > [...]
> 
> Fair point, I didn't spend too much time examining the nroff behavior.
> 
> > At 2025-11-23T22:13:29+0100, onf wrote:
> > > Actually, CSTR #54 does not specify what should happen when sv's
> > > argument equals distance to next trap:
> > [...]
> > > In other words, the reason that ne does nothing when .t == N is
> > > because another line can fit in before the trap will be sprung.
> > 
> > That makes sense.
> > 
> > > However, in sv's case, what we're interested in is whether the
> > > given SPACE can fit in before springing the trap, not some text,
> > > so in that case we need the opposite behavior.
> > 
> > Hmmm.  That seems plausible.
> > 
> > The questions I have are:
> > 
> > * If we've broken compatibility with AT&T troff in this regard, what
> >   has that cost us?  What exhibits of `sv` and `os` usage in
> >   practical documents can we lay our hands on?  It may be that `sv`
> >   and `os` were developed first, and then someone noticed that `sv`
> >   was being used a lot without any `os` subsequently, so `ne` was
> >   created.  Just a guess.
> 
> I have even less information on this than you do as I don't come into
> much contact with historical troff documents.

I have a hypothesis that the difference in behavior you're observing
does not have to do with differences in computation but rather in trap
springing behavior.  Since almost any trap worth writing is going to do
something to alter the vertical drawing position, that could look like a
computational difference to casual observation.

I've confirmed that almost every implementation other than GNU troff
behaves differently from GNU troff.  I didn't test neatroff, but all of:

* Plan 9 from User Space nroff
* DWB 3.3 nroff
* Heirloom Doctools nroff
* Seventh Edition Unix nroff

...all behave the same way in that if `sv`'s test fails, they spring the
trap.  That can include the implicit page trap,[1] which is the only one
that exists if the input doesn't configure another.

As a postscript, I have a sample document and the output it produces.

> > * What's more conceptually consistent?  To have `ne` do the exact
> >   same computation as `sv`, or not?
> 
> I believe I have made a good case above for them to behave
> differently, which also seems to be supported by the behavior of Plan
> 9 and Heirloom troffs.

I'm not convinced yet.

> > * Do we want to make groff's compatibility mode work like AT&T troff
> >   in this respect, even if we don't conform in non-compatibility
> >   mode?  If so, this would become an example of an "engine level",
> >   rather than a syntactical, behavior change to which I recently
> >   referred.[1]
> 
> Provided that AT&T troff actually does behave like that (I tested only
> Plan 9 troff; I doubt AT&T will differ, but it should be tested itself),
> the right thing to do is to provide such a behavior at least in
> compatibility mode IMO.

You may be right.  I'm dithering.

> > The most important question to me is the first.  If someone can show
> > me a practical (preferably historical) document that renders
> > correctly with AT&T troff but not with groff, I'm inclined to give
> > that a lot of weight.

Excluding contrived test cases, before today I didn't know of a
document, historical or otherwise, that uses `sv`, let alone `os`, so I
guess I'd better go looking.  I recently trawled BSD history to justify
a change,[2] so I can employ the same resource for this purpose.

Let's see...

$ grep -E "^['.][[:space:]]*(sv|os)" \
  $(find 4.3BSD-Tahoe/usr/doc -type f -and -not -name Makefile) | sort
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t:.sv 2
4.3BSD-Tahoe/usr/doc/run/tmac.sU:.os

The story appears to be a sparse one.

One of those is a bona fide document, and the other a macro package.

Let's get some context, looking first at "06.sysman".

4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.AE
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.LP
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.bp
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.ft B
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.br
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t:.sv 2
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.ce
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-TABLE OF CONTENTS
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.ft R
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.LP
4.3BSD-Tahoe/usr/doc/ps1/06.sysman/0.t-.sp 1

This looks like straightforward stuff.  The author obviously didn't want
a break between his "TABLE OF CONTENTS" caption and the contents list
itself.

Unfortunately, that's a weak case for `sv`.  `ne` is more idiomatic
here.  I'm familiar too with the practice of `br`eaking first before
asserting `ne`eded space.  You can see this pattern all over my "poor
man's keeps" in groff's man pages.

Because the space is never output with `os`, `sv` achieves nothing here
that `ne` doesn't.  So I am inclined to disregard it as a meritorious
application of `sv`.  For `sv` to be worth preserving as a *roff
request--either in terms of behavior detail or overall existence, where
a "compatibility macro" would be a compelling alternative--it has to
offer something that `ne` doesn't.

Okay, what about this other guy?  "tmac.sU"?  Some kind of add-on to the
manuscript macros?

4.3BSD-Tahoe/usr/doc/run/tmac.sU-.ch FO -\\n(FMu
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.ch FX \\n(.pu-\\n(FMu
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.if \\n(MF .FV
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.nr MF 0
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.mk
4.3BSD-Tahoe/usr/doc/run/tmac.sU:.os
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.ev 1
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.if !\\n(TD .if \\n(TC<5 .XK
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.nr TC 0
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.ev
4.3BSD-Tahoe/usr/doc/run/tmac.sU-.nr TQ \\n(.i

This doesn't leave me much the wiser.  The macro names smell like
footnote management, which seems always to be a severe test of the macro
package programmer's mettle.

$ grep -r '.de.*XK' 4.3BSD-Tahoe/
4.3BSD-Tahoe/usr/lib/tmac/tmac.os:.de XK
4.3BSD-Tahoe/usr/lib/tmac/tmac.s:.de XK
4.3BSD-Tahoe/usr/doc/run/tmac.sU:.de XK

Hmm, yeah, "tmac.os" looks like Lesk ms(7), and "tmac.s" Tuthill
ms(7).[3]

Anyway, thing is, _none of the manuscript macro files call `sv`_.

I guess the user's document could, in which case `os` dutifully spits
out whatever space the user's document `sv`-ed.

It seems to make sense in context--28 lines prior to this we have

  .de NP

...which is the historical internal ms(7) macro for starting a page.
This `os` call comes after `HD` (commented "undefined") is called...

groff_ms(7):
     For even greater flexibility, ms is designed to permit the
     redefinition of the macros that are called when formatter traps
     that ordinarily cause the headers and footers to be output are
     sprung.  PT (“page trap”) is called by ms when the header is to be
     written, and BT (“bottom trap”) when the footer is to be.  The
     groff page location trap that ms sets up to format the header also
     calls the (normally undefined) HD macro after PT; you can define HD
     if you need additional processing after setting the header.  The HD
     hook is a Berkeley extension.  Any such macros you (re)define must
     implement any desired specialization for odd‐, even‐, or first
     numbered pages.

...which, in my biased view, supports my guess.

> > Without such an exhibit, I don't see this issue as demanding
> > resolution in the short term.  (At the moment, I'm kind of
> > preoccupied with cutting a release candidate of groff 1.24.0.)
> 
> I have reported this issue mostly to make groff developers aware of
> the deviation from behavior of other troffs and to invite alternative
> explanations besides "groff is wrong", which Bjarni did provide. It is
> no longer the goal of any of my postings to convince you to modify
> groff in any way as I no longer use groff in production of anything.
> So as far as I am concerned, it's completely up to you whether you
> remove this deviation or not and I certainly do not have the time to
> go digging for historical troff documents.

Okay, well, I happen to have already collected a big mound of dirt.  ;-)

> My opinion is that it should be removed for the sake of correctness,

I have trouble recognizing your understanding of the feature as the
correct one _per the official AT&T troff documentation_.  I readily
concede that `sv` interaction with traps is consistently one in AT&T
troffs and inconsistent with GNU troff.  However, all the AT&T troffs
are genealogically related and this seems to be an obscure feature.

> but I also understand that doing so could break existing documents
> which rely on the current behavior, so I don't claim that removing it
> is the only reasonable course of action. Of course, if it ends up not
> getting removed, it would make sense to document this difference in
> groff_diff(7).

Here's what CSTR #54 says.

.sv N  -  N=1 V   v
    Save a contiguous vertical block of size _N_.  If the distance to
    the next trap is greater than _N_, _N_ vertical space is output.
    No-space mode has no effect.  If this distance is less than _N_, no
    vertical space is immediately output, but _N_ is remembered for
    later output (see `os`).  Subsequent `sv` requests will overwrite
    any still remembered `N`.

(The annotations mean: (a) an initial value is not applicable; (b) an
_N_ of "1" is assumed if the argument is omitted; (c) "v" means the
default scaling unit is vees; and (d) "V" means that the unit applicable
to the default argument is vees--see `ls`.)

I think it's right and proper for `os` to spring page location traps as
a side-effect of dumping space on the output.

I don't see _any_ reason, apart from slavish bug-compatibility, for `sv`
to spring page location traps.

I propose that, in GNU troff, `sv` continue to behave as CSTR #54 says,
and not spring traps.  If you want to save some space _and_ spring a
trap, use both requests.

.if !\n(.g .ne 3v \" GNU troff's `sv` doesn't spring traps.
.sv 3v

Maybe I can get Plan 9 from User Space to join GNU in heresy.[4]  ;-)

Regards,
Branden

P.S. Exhibits:

$ cat ATTIC/sv-and-os.roff
.nf
1
2
3
4
5
6
7
8
9
.sv 1i/2u
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
.sv 1i
A
B
C
D
E
F
.os
G
H
I
DONE
$ ~/groff-1.24.1/bin/nroff ATTIC/sv-and-os.roff | nl -ba
     1  1
     2  2
     3  3
     4  4
     5  5
     6  6
     7  7
     8  8
     9  9
    10
    11
    12
    13  13
    14  14
    15  15
    16  16
    17  17
    18  18
    19  19
    20  20
    21  21
    22  22
    23  23
    24  24
    25  25
    26  26
    27  27
    28  28
    29  29
    30  30
    31  31
    32  32
    33  33
    34  34
    35  35
    36  36
    37  37
    38  38
    39  39
    40  40
    41  41
    42  42
    43  43
    44  44
    45  45
    46  46
    47  47
    48  48
    49  49
    50  50
    51  51
    52  52
    53  53
    54  54
    55  55
    56  56
    57  57
    58  58
    59  59
    60  60
    61  A
    62  B
    63  C
    64  D
    65  E
    66  F
    67
    68
    69
    70
    71
    72
    73  G
    74  H
    75  I
    76  DONE
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
   100
   101
   102
   103
   104
   105
   106
   107
   108
   109
   110
   111
   112
   113
   114
   115
   116
   117
   118
   119
   120
   121
   122
   123
   124
   125
   126
   127
   128
   129
   130
   131
   132
$ 9 nroff ATTIC/sv-and-os.roff | nl -ba
     1  1
     2  2
     3  3
     4  4
     5  5
     6  6
     7  7
     8  8
     9  9
    10
    11
    12
    13  13
    14  14
    15  15
    16  16
    17  17
    18  18
    19  19
    20  20
    21  21
    22  22
    23  23
    24  24
    25  25
    26  26
    27  27
    28  28
    29  29
    30  30
    31  31
    32  32
    33  33
    34  34
    35  35
    36  36
    37  37
    38  38
    39  39
    40  40
    41  41
    42  42
    43  43
    44  44
    45  45
    46  46
    47  47
    48  48
    49  49
    50  50
    51  51
    52  52
    53  53
    54  54
    55  55
    56  56
    57  57
    58  58
    59  59
    60  60
    61
    62
    63
    64
    65
    66
    67  A
    68  B
    69  C
    70  D
    71  E
    72  F
    73  G
    74  H
    75  I
    76  DONE
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
   100
   101
   102
   103
   104
   105
   106
   107
   108
   109
   110
   111
   112
   113
   114
   115
   116
   117
   118
   119
   120
   121
   122
   123
   124
   125
   126
   127
   128
   129
   130
   131
   132

[1] 
https://www.gnu.org/software/groff/manual/groff.html.node/The-Implicit-Page-Trap.html#The-Implicit-Page-Trap
[2] 
https://cgit.git.savannah.gnu.org/cgit/groff.git/commit/?id=2426d1b52c883a592fa7589a31c56558dbd3c357

[3] $ nroff -t -rLL=74n -ms ~/groff-1.24.1/share/doc/groff-1.24.1/ms.ms
...
9.  Further reading

“Typing  Documents on the Unix System: Using the -ms Macros with Troff and
   Nroff”; M. E. Lesk; November 13, 1978.  This document describes the  ms
   supplied with AT&T Unix Version 7.

“A Revised Version of -ms”; Bill Tuthill; August 1983.  The 4.2BSD release
   featured several extensions to ms, most of which are recreated in groff
   ms.   (The  exceptions  are the TM and CT macros for setting a doctoral
   thesis in the format required by  the  contemporaneous  degree‐granting
   authorities of the University of California at Berkeley.)
...

[4] ...but don't count it.  They seem disinclined to accept patches for
    behavioral changes without someone undertaking code hygiene efforts
    first.

    https://github.com/9fans/plan9port/pull/739

Attachment: signature.asc
Description: PGP signature

Reply via email to