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
signature.asc
Description: PGP signature
