bugGNU roff - Bugs: bug #56499, adjacent trap behavior...

 
 

bug #56499: adjacent trap behavior undocumented and probably undesirable

Submitter:  Dave <barx>
Submitted:  Sat 15 Jun 2019 10:54:01 AM UTC
   
 
Category:  Core Severity:  4 - Important
Item Group:  Incorrect behaviour Status:  Confirmed
Privacy:  Public Assigned to:  None
Open/Closed:  Open Planned Release:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Sun 18 Dec 2022 10:13:03 PM UTC, comment #3: 

comment #2:

> comment #1:
> > ...I believe wrongly assume that there will be only one next
> > trap to be sprung.
>
> Interesting that this logic error seems to have been made independently in both AT&T troff and groff.  (Plan 9 and Heirloom, I believe, both branched from AT&T source code; groff did not.)


That's correct as I understand it.  I wonder how neatroff behaves; it is also independent (and maybe the only other *roff that is).

But I'm not too surprised that the flaw was independently arrived at...  If I'm correct, then it avoids creating a new data structure or maintaining a sub-quantum trap location pointer that is distinct from the vertical drawing position.
 

> Additionally, the last-trap-planted-wins behavior avoids the question of what order multiple traps at the same position should be run in, or whether there's even a guaranteed order.


Agreed.

G. Branden Robinson <gbranden>
Group administrator
Sun 18 Dec 2022 09:57:04 PM UTC, comment #2: 

comment #1:

> ...I believe wrongly assume that there will be only one next
> trap to be sprung.


Interesting that this logic error seems to have been made independently in both AT&T troff and groff.  (Plan 9 and Heirloom, I believe, both branched from AT&T source code; groff did not.)

> The weird phenomenon of "active" traps hiding earlier-planted
> ones at the same position has a long pedigree.  We might need to
> retain it, even as weird as it is, for historical compatibility.


Sure, there's the fact that some coders may have relied on this behavior, but there's also the fact that it's easy to document and understand, neither of which is a property of the vertical-motion-quantum behavior.

Additionally, the last-trap-planted-wins behavior avoids the question of what order multiple traps at the same position should be run in, or whether there's even a guaranteed order.

Dave <barx>
Group Member
Sun 18 Dec 2022 01:02:01 AM UTC, comment #1: 

I think I have nearly root-caused this, and it's going to take some internal fiddling to resolve.

There's a member function called `top_level_diversion::find_next_trap()`.

https://git.savannah.gnu.org/cgit/groff.git/tree/src/roff/troff/div.cpp?id=88e89ebe9ce3931b49fb3cbd7fa20eacb03bf69f#n339

Here's what I think right now.

Only one of its callers, `top_level_diversion::distance_to_next_trap()`, has its needs completely met.  This function's job is to compute a value for the \n[.t] register, I think.

The other three callers...

top_level_diversion::output()
top_level_diversion::space()
top_level_diversion::begin_page()

...I believe wrongly assume that there will be only one next trap to be sprung.  But as Dave has documented at length, there could be more than one, and moreover while their positions in basic units may differ, traps at different places may lie within the same vertical motion quantum of the output device.  (I believe this explains the Heisenbuggy boundary issues, e.g., why you might see "trap 2" sprung but not "trap 1", particularly when the traps are not "aligned to the grid" of the document's vertical spacing.  An analogy with the horrible problems OS kernel programmers had to learn about involving memory page boundaries when virtual memory systems came into vogue also comes to my mind.)

Specifically, in ::output and ::space we see logic like this:


trap *next_trap = find_next_trap(&next_trap_pos);

  if (vertical_position_traps_flag && next_trap != 0 && y >= next_trap_pos) {
    vertical_position = next_trap_pos;
    nl_reg_contents = vertical_position.to_units();
    truncated_space = y - vertical_position;
    spring_trap(next_trap->nm);
  }


Notice that this does not iterate.

Also, ::output performs logic like this twice, with close similarities.  I am wondering if a common function that springs all traps until a given position pos is warranted.

But not really "all", maybe.  The weird phenomenon of "active" traps hiding earlier-planted ones at the same position has a long pedigree.  We might need to retain it, even as weird as it is, for historical compatibility.

But a trap should not be able to hide another trap at a different position in basic units, no matter how coarse the vertical motion quantum of the device, but it can, and that is what is causing the grief that Dave reported.

It may also lie at the knotted root of a dozen entangled me(7) package footnote-management bugs.

This item should be high on the priority list for post-1.23.0.

G. Branden Robinson <gbranden>
Group administrator
Sat 15 Jun 2019 10:54:01 AM UTC, original submission:  

$ groff -a << EOF
.de trap1
.tm trap 1
..
.de trap2
.tm trap 2
..
.de trap3
.tm trap 3
..
.de trap4
.tm trap 4
..
.de trap5
.tm trap 5
..
.de trap6
.tm trap 6
..
.wh 2.1i trap1
.wh 2.2i trap2
.wh 2.3i trap3
.wh 2.4i trap4
.wh 2.5i trap5
.wh 2.6i trap6
.nf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EOF

The above command produces the following (a mix of stdout and stderr):

1
2
3
4
5
6
7
8
9
10
11
12
13
trap 1
14
trap 2
15
trap 4
16
trap 6
17
18
19
20

In a response on the email list (http://lists.gnu.org/archive/html/groff/2018-06/msg00013.html, which also addresses an unrelated issue), Werner writes, "[W]hile your first trap is active, it moves down one line, which passes some traps without triggering them."  But this behavior is not only undocumented, but seemingly unwarranted: Nothing in these traps should cause vertical movement.

Neither groff nor CSTR 54 (as Ralph points out in http://lists.gnu.org/archive/html/groff/2018-06/msg00017.html) documents this behavior.  But the behavior is not limited to the GNU troff implementation; Heirloom (http://lists.gnu.org/archive/html/groff/2018-10/msg00008.html) and Plan 9 (http://lists.gnu.org/archive/html/groff/2018-10/msg00012.html) troffs also exhibit a version of the bug.  However, Heirloom's results are different from groff's, implying that groff is not precisely reproducing historical behavior.  And there seems to be no rationale for the behavior anyway.

Furthermore, it inhibits useful functionality.  One of the above links describes a use case (for PostScript output) that the current behavior hinders: "I [was] trying to place a trap that gets triggered at the end of the current output line (that is, the next time the output's vertical position is advanced).  I did this by placing the trap a single basic unit below the current vertical position, which works in the general case.  However, if the output line in question happens to be the last line of the page, this new trap causes groff to skip the generic end-of-page trap, even though it's not overwriting that trap's page position."

Dave <barx>
Group Member

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by gbranden (Posted a comment)
  • -email is unavailable- added by barx (Submitted the item)
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only logged-in users can vote.

     

    Follow 2 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2022-12-18 gbranden Severity3 - Normal 4 - Important
        StatusNone Confirmed

    Back to the top

    Powered by Savane 3.13-4448.
    Corresponding source code