Follow-up Comment #2, bug #64212 (project groff): [comment #1 comment #1:] > [comment #0 original submission:] > > if, somehow, there is no next trap at the top level, but > > that...should be impossible? > > The info documentation for \n[.t] thinks it's possible: "If there are no traps between the current position and the bottom of the page, it contains the distance to the page bottom."
Right. Sorry, I wasn't clear about this; the internal page-bottom trap always exists, but it has no name. Consider the following. $ cat ./EXPERIMENTS/dot-t-within-trap.groff .de TT .tm trap: $0=\\$0, .t=\\n(.t, .trap=\\n[.trap] .. .wh 11i TT Hello, world! .tm body1: .t=\n(.t, .trap=\n[.trap] .br .tm body2: .t=\n(.t, .trap=\n[.trap] $ ./build/test-groff -Tascii ./EXPERIMENTS/dot-t-within-trap.groff | cat -s body1: .t=2640, .trap= body2: .t=2600, .trap= Hello, world! The TT trap was never sprung! Now, let's change the placement of the trap from 11i (the bottom of a U.S. letter page in portrait orientation, and the formatter default) to 10i. $ ./build/test-groff -Tascii ./EXPERIMENTS/dot-t-within-trap.groff | cat -s body1: .t=2400, .trap=TT body2: .t=2360, .trap=TT trap: $0=TT, .t=240, .trap= Hello, world! Consider what must have happened: our trap _was_ planted, and in fact if we move it back to 11i and invoke `ptr` we can see it. $ ./build/test-groff -Tascii ./EXPERIMENTS/dot-t-within-trap.groff | cat -s TT 2640 body1: .t=2640, .trap= body2: .t=2600, .trap= Hello, world! The internal trap is not reported, because it has no name. But it's there--and, if our documentation is accurate, it is at 2640 units, _before_ the TT trap. That is why \n[.trap] interpolates an empty value. -- Request: .wh dist [name] Call macro NAME when the vertical position DIST on the page is reached or passed in the downward direction. The default scaling unit is 'v'. Non-negative values for DIST set the trap relative to the top of the page; negative values set the trap relative to the bottom of the page. An existing _visible_ trap (see below) at DIST is removed; this is 'wh''s sole function if NAME is missing. A trap is sprung only if it is "visible", meaning that its location is reachable on the page(1) (*note Page Location Traps-Footnote-1::) and it is not hidden by another trap at the same location already planted there. [...] It is possible to have more than one trap at the same location (although only one at a time can be visible); to achieve this, the traps must be defined at different locations, then moved to the same place with the 'ch' request. [...] -- Register: \n[.t] The read-only register '.t' holds the distance to the next vertical position trap. If there are no traps between the current position and the bottom of the page, it contains the distance to the page bottom. Within a diversion, in the absence of a diversion trap, this distance is the largest representable integer in basic units--effectively infinite. -- Request: .ch name [dist] Change the location of a trap by moving macro NAME to new location DIST, or by unplanting it altogether if DIST is absent. The default scaling unit is 'v'. Parameters to 'ch' are specified in the opposite order from 'wh'. If NAME is the earliest planted macro of multiple traps at the same location, (re)moving it from that location exposes the macro next least recently planted at the same place.(2) (*note Page Location Traps-Footnote-2::) [...] (1) A trap planted at '20i' or '-30i' will not be sprung on a page of length '11i'. (2) It may help to think of each trap location as maintaining a queue; 'wh' operates on the head of the queue, and 'ch' operates on its tail. Only the trap at the head of the queue is visible. I am beginning to think I should start discussing the unnamed internal trap more explicitly. I still have a hangover from some earlier documentation applying the term "pseudo" to it. It seems clearer to me now that there truly is nothing ersatz about it. It exists and behaves like a named trap in most respects, except insofar as it doesn't have a name. In fact, the unnamed/implicit/internal trap has a superpower; recall this in the description of `wh`. "An existing _visible_ trap (see below) at DIST is removed;" ...which doesn't apply to the implicit trap. You can shove a named trap in **front** of it with `wh`, but it will not be deleted. Observe: $ cat EXPERIMENTS/expose-implicit-trap.roff .de TT .tm I'm a happy trapper on page \\n%. .. .wh 11i TT Hello, world! .bp .ch TT 10i Page two. .bp Page three. $ ./build/test-groff -Tascii EXPERIMENTS/expose-implicit-trap.roff | cat -s I'm a happy trapper on page 2. I'm a happy trapper on page 3. Hello, world! Page two. Page three. > In all the standard macro packages, it's likely impossible unless the user goes to some pains to remove the bottom-margin trap. It's conceivable that a full-service macro package will not set up a page-bottom trap if no footer is configured and no footnotes occur. I haven't verified if any of the ones we ship are so parsimonious, however. > > Does anybody want this? > > I don't see any harm in adding it even if only people who are debugging groff internals ever end up using it. Okay. I'll ask the list. The funny situation I ran across is this: What should the value of \n[.t] (and, for that matter, the proposed \[.trap]) be when you're _already executing a trap_? As we saw above, when we instrument a trap to report next-trap information, we got this: trap: $0=TT, .t=240, .trap= _groff_ has dutifully updated `\$0` for us, a nice feature even though traps can't take arguments (and especially helpful since trap-called macros, like others, can be aliased). \n[.trap] is empty, and since we're not in a diversion, does this mean that the next trap is the implicit trap? Well, there are a couple of ways to find out. One is to plant another trap. $ cat EXPERIMENTS/two-traps.groff .de TT .tm TT trap: $0=\\$0, .t=\\n(.t, .trap=\\n[.trap] .. .de UU .tm UU trap: $0=\\$0, .t=\\n(.t, .trap=\\n[.trap] .. .wh 10i TT .wh 10.5i UU Hello, world! $ ./build/test-groff -Tascii EXPERIMENTS/two-traps.groff|cat -s TT trap: $0=TT, .t=120, .trap=UU UU trap: $0=UU, .t=120, .trap= Hello, world! Lovely! Another is to rudely invade the macro/string/diversion name space and _give_ the unnamed trap a name, at least according to this proposed '\n[.trap]' feature. +const char *top_level_diversion::get_next_trap_name() +{ + vunits next_trap_pos; + trap *next_trap = find_next_trap(&next_trap_pos); + if (0 /* nullptr */ == next_trap) + return "GBR-NO-NEXT-TOP-LEVEL-TRAP"; + else + return next_trap->nm.contents(); +} + $ (cd build && make -j troff) $ ./build/test-groff -Tascii EXPERIMENTS/two-traps.groff|cat -s TT trap: $0=TT, .t=120, .trap=UU UU trap: $0=UU, .t=120, .trap=GBR-NO-NEXT-TOP-LEVEL-TRAP Hello, world! As can be seen, what I was really dealing with was a null pointer. The implicit trap is neither wave nor particle. Formatter logic will arrange for the page bottom to eventually be reached, no matter what. (In _groff_, vertical position traps can be disabled, which as our manual notes, means that the current page "can't be ejected".) So I stuck a ".vpt 0" at the end of "two-traps.groff" above, and got this: $ ./build/test-groff -Tascii EXPERIMENTS/two-traps.groff|cat -s troff: error: can't continue page ejection because vertical position traps disabled troff: error: can't continue page ejection because vertical position traps disabled Hello, world! The formatter manages to wrangle itself free of this situation and exit rather than looping infinitely. (I wonder why the diagnostic is thrown _twice_...) So there would seem to be a lot of special casing going on here to stumble forward with invalid input documents. Maybe '\n[.trap]', plus a more explicit exploration of the implicit trap in our documentation, will help people. _______________________________________________________ Reply to this item at: <https://savannah.gnu.org/bugs/?64212> _______________________________________________ Message sent via Savannah https://savannah.gnu.org/