bugGNU roff - Bugs: bug #63587, [troff] set .R register to maximum...

 
 

bug #63587: [troff] set .R register to maximum representable integer

Submitter:  G. Branden Robinson <gbranden>
Submitted:  Thu 29 Dec 2022 03:52:30 AM UTC
   
 
Category:  Core Severity:  1 - Wish
Item Group:  Feature change Status:  Fixed
Privacy:  Public Assigned to:  gbranden
Open/Closed:  Closed Planned Release:  1.24.0
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Tue 20 Aug 2024 05:19:10 AM UTC, comment #15: 

comment #13:

> Which is maybe still not worth quibbling over, but I'm noting it
> for the record.


This quibble was also addressed: commit c3c026044

Dave <barx>
Group Member
Tue 20 Aug 2024 12:38:59 AM UTC, comment #14: 


commit 4b39065871599553a508cba28c97638b21c7354a
Author: G. Branden Robinson <g.branden.robinson@gmail.com>
Date:   Sat Aug 17 20:26:56 2024 -0500

    [troff]: Fix Savannah #63587 (\n[.R] := INT_MAX).

    * src/roff/troff/input.cpp (init_input_requests): Initialize `.R`
      read-only register to `INT_MAX` instead of 10000.

    * doc/groff.texi.in (Built-in Registers):
    * man/groff.7.man (Read-only registers):
    * man/groff_diff.7.man (Altered registers): Document it.

    * doc/groff.texi.in (Manipulating Filling and Adjustment)
      (End-of-input Traps): Apply it to examples.

    Fixes <https://savannah.gnu.org/bugs/?63587>.


G. Branden Robinson <gbranden>
Group administrator
Fri 16 Aug 2024 05:42:03 PM UTC, comment #13: 

comment #12:

> No, it's still true, for devices with a vertical resolution of 1.


Fair point.  In fact, it's truer now than before, losing the off-by-oneness noted in comment #2.

> it wasn't true even before, for devices with non-unit vertical resolutions.


True, but it was close enough to not bother quibbling over the phrase "largest representable integer."  Now it's orders of magnitude less.  Which is maybe still not worth quibbling over, but I'm noting it for the record.

Dave <barx>
Group Member
Fri 16 Aug 2024 11:37:57 AM UTC, comment #12: 

comment #11:

> comment #2:
> > there is a way to determine INT_MAX from within the roff language.
> [...]
> > "Within a diversion, in the absence of a diversion trap, [the .t
> > register] is the largest representable integer in basic units."
>
> By my reading of the code change in commit 5505e9864 (I've yet to compile said change to verify), neither my statement nor the quoted line of the manual is true any longer.


No, it's still true, for devices with a vertical resolution of 1.


$ ./build/test-groff -T ps
.di foo
.tm .t=\n(.t
.t=2147483647
.nr t \n(.t
.nr t +1
troff:<standard input>:4: error: integer addition wrapped
.di
.ex


The trouble is, it wasn't true even before, for devices with non-unit vertical resolutions.


$ ~/groff-stable/bin/groff -T utf8 # a.k.a. 1.23.0
.di foo
.tm .t=\n(.t
.t=2147483600
.nr t \n(.t
.nr t +1
.tm t=\nt
t=2147483601
.di
.ex


G. Branden Robinson <gbranden>
Group administrator
Fri 16 Aug 2024 08:37:17 AM UTC, comment #11: 

comment #2:

> there is a way to determine INT_MAX from within the roff language.

[...]

> "Within a diversion, in the absence of a diversion trap, [the .t
> register] is the largest representable integer in basic units."


By my reading of the code change in commit 5505e9864 (I've yet to compile said change to verify), neither my statement nor the quoted line of the manual is true any longer.

Dave <barx>
Group Member
Fri 21 Jun 2024 03:55:12 AM UTC, comment #10: 

comment #5:

> I can think of no situation where a user might need 10,001
> registers, so, despite this number having no clear connection to
> any internal groff limit, it's probably a reasonable limit to
> claim to the user who asks.  It will enable the processing of
> any ancient roff documents that do test this register,


I have to backpedal on my own argument here: this point, as far as I can see now, remains just as true no matter what big number .R holds.  Groff having no fixed register limit, 10,000 and INT_MAX are equally wrong values to return, but equally useful in terms of back compatibility to documents for ancient troffs.

Dave <barx>
Group Member
Sat 15 Apr 2023 05:22:42 PM UTC, comment #9: 

comment #8:

> the possible existence of there being other ways to trip over
> what appears to be deficient integer overflow handling.
>
> I'm less concerned with complexity here than with there being an
> access hatch from the roff language to undefined behavior in C/C++.


Makes sense.

> Also I will admit that Heirloom returning a correct INT_MAX for
> \n[.t] in a diversion while we don't chaps me a little.  ;-)


If it's any consolation, I think groff still wins in terms of overall correct behavior, and certainly in terms of developer responsiveness to bugs.  Not that it's a competition ;-)

Dave <barx>
Group Member
Sat 15 Apr 2023 12:31:45 AM UTC, comment #8: 


comment #7:

> comment #6:
> > When fixing it so that \n[.t] really did return INT_MAX
> > in a diversion, things started to go wrong.
>
> So perhaps the fix is to insert the word "nearly" in the documentation before the phrase "the largest representable integer." ;-)  Kinda joking, but also, the exact value here doesn't really matter, right?


Probably not here, no.

> Absent any other mechanism for discovering INT_MAX--as roff languages have been since forever--to the roff coder there's no effective difference between "\n[.t] is the largest representable integer" and "\n[.t] is a really big number."  As long as it's always the same really big number, the hack in comment #2 lets the roff coder determine what that number is should they need to compare subsequent \n[.t] interpolations against it.


Yes, but part of what I'm worried about is the possible existence of there being other ways to trip over what appears to be deficient integer overflow handling.

> > This appears to be a kludge covering some sins in integer
> > arithmetic handling.
>
> I don't know the nature of those sins, but they could be as benign as "doing it 'right' makes these algorithms way more complicated than when using a close-enough approximation."


I'm less concerned with complexity here than with there being an access hatch from the roff language to undefined behavior in C/C++.

Also I will admit that Heirloom returning a correct INT_MAX for \n[.t] in a diversion while we don't chaps me a little.  ;-)

G. Branden Robinson <gbranden>
Group administrator
Fri 14 Apr 2023 11:52:23 PM UTC, comment #7: 

comment #6:

> When fixing it so that \n[.t] really did return INT_MAX
> in a diversion, things started to go wrong.


So perhaps the fix is to insert the word "nearly" in the documentation before the phrase "the largest representable integer." ;-)  Kinda joking, but also, the exact value here doesn't really matter, right?  Absent any other mechanism for discovering INT_MAX--as roff languages have been since forever--to the roff coder there's no effective difference between "\n[.t] is the largest representable integer" and "\n[.t] is a really big number."  As long as it's always the same really big number, the hack in comment #2 lets the roff coder determine what that number is should they need to compare subsequent \n[.t] interpolations against it.

> This appears to be a kludge covering some sins in integer
> arithmetic handling.


I don't know the nature of those sins, but they could be as benign as "doing it 'right' makes these algorithms way more complicated than when using a close-enough approximation."

> There is a miniature science to this.


Thanks for that link!

Dave <barx>
Group Member
Fri 14 Apr 2023 07:17:09 PM UTC, comment #6: 

comment #2:

> I have to update my enthusiasm level from "I like this" to "I'm OK with this," because I just discovered there is a way to determine INT_MAX from within the roff language.  Branden already knew this, having rewritten the relevant info-manual sentence in 2020: "Within a diversion, in the absence of a diversion trap, [the .t register] is the largest representable integer in basic units."
>
> Sure enough:


> .di dummy
> .nr int_max \n[.t]
> .di
> .tm int_max is \n[int_max]


> spits out "int_max is 2147483646" on stderr.
>
> (It's intriguingly off by one:


Yes.  This appears to be a kludge covering some sins in integer arithmetic handling.  When fixing it so that \n[.t] really did return INT_MAX in a diversion, things started to go wrong.

So this is yet another one of those cases where we tug on a thread and a startling number of things unravels.

I took a couple of stabs at it but angered the formatter in surprising ways.  (For instance, "fixing" integer arithmetic breaks the bracket-building escape sequence.  I still haven't figured out how.)

> but close enough for the practical uses for it Branden lists in points 1-3 (and maybe 4, but I confess I don't grok what distinguishes 4 from 3) in his initial report.)


Case #4 is the simple question "how big are integers in this programming system?"

Case #3 is "how do I (as a *roff programmer doing arithmetic) need to structure my operations to avoid overflows, in intermediate calculations or otherwise"?

There is a miniature science to this.

https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow

G. Branden Robinson <gbranden>
Group administrator
Thu 13 Apr 2023 12:42:38 AM UTC, comment #5: 

Further musings:

It slightly weakens inter-roff interoperability to have \n[.R] mean different things in different roffs.  On those grounds, perhaps there should be a new register to return INT_MAX, and \n[.R] should be left to its original job.

And it does appear groff in fact has a register limit.  At first blush, it looks to be far above the 10,000 reported by \n[.R]:

$ for num in `seq 1000000`; do echo .nr a$num 12; done | groff
troff:<standard input>:597608: fatal error: too many symbols

But upon further investigation, "how many registers does groff let you define?" does not have as straightforward an answer as one might hope: registers and strings, while not sharing a namespace, do seem to share the same internal "symbol" pool that is being depleted above:

$ for num in `seq 1000000`; do echo .nr a$num 12; echo .ds b$num twelve; done | groff
troff:<standard input>:597608: fatal error: too many symbols

The failure here happens on the same input line, but only half as many registers have been defined by that point in this modified example.

This suggests it should even be possible to contrive a situation where the number of user-defined registers available is ZERO.  And, indeed:

$ ( for num in `seq 597607`; do echo .ds b$num twelve; done; echo .tm so far so good; echo .nr my-only-register 12) | groff
so far so good
troff:<standard input>:597609: fatal error: too many symbols

But "contrive" is the operative word there: given the point at which this limit is being hit, I can conceive of no real-world case where one would run out of symbols.  Similarly, I can think of no situation where a user might need 10,001 registers, so, despite this number having no clear connection to any internal groff limit, it's probably a reasonable limit to claim to the user who asks.  It will enable the processing of any ancient roff documents that do test this register, and--as you note back at the start--anyone writing documents in the groff era is unlikely to be looking at \n[.R]'s value at all.

Dave <barx>
Group Member
Wed 12 Apr 2023 08:49:18 PM UTC, comment #4: 

comment #3:

> 1. Having to create a diversion just to discover this
> implementation limit, which is in no way restricted to
> diversion applications, feels lame.


It's definitely hackish, and certainly not obvious to even (some) semi-experienced roff users.  A simple, documented interface is certainly better; I was just pointing out (perhaps only to myself, or perhaps to a vast silent savannah audience) that it's not impossible, nor even particularly difficult, with existing groff.  As an added benefit, the hack is portable, at least to Heirloom (which, as a bonus, actually gets the number right).

Also, without knowing what the largest integer is, how does one distinguish between a diversion trap that's very far away from the current position (i.e., a very large value of \n[.t]) and no diversion traps being set (i.e., also a very large value of \n[.t])?  This could be item 5 on your use-case list -- though for it to work as such, the off-by-one would have to be redressed.

Dave <barx>
Group Member
Wed 12 Apr 2023 06:57:26 PM UTC, comment #3: 

Fair.  However:

1. Having to create a diversion just to discover this implementation limit, which is in no way restricted to diversion applications, feels lame.

2. Having that value not be the actual implementation limit feels even lamer.

G. Branden Robinson <gbranden>
Group administrator
Wed 12 Apr 2023 06:45:33 PM UTC, comment #2: 

I have to update my enthusiasm level from "I like this" to "I'm OK with this," because I just discovered there is a way to determine INT_MAX from within the roff language.  Branden already knew this, having rewritten the relevant info-manual sentence in 2020: "Within a diversion, in the absence of a diversion trap, [the .t register] is the largest representable integer in basic units."

Sure enough:

.di dummy
.nr int_max \n[.t]
.di
.tm int_max is \n[int_max]

spits out "int_max is 2147483646" on stderr.

(It's intriguingly off by one:

$ groff
.nr big1 2147483646
.nr big2 2147483647
.nr big3 2147483648
troff: <standard input>:3: numeric overflow

but close enough for the practical uses for it Branden lists in points 1-3 (and maybe 4, but I confess I don't grok what distinguishes 4 from 3) in his initial report.)

Dave <barx>
Group Member
Thu 19 Jan 2023 09:21:31 PM UTC, comment #1: 

I like this.  Even if repurposing .R turns out to be infeasible for some reason, having some other predefined register specifying groff's INT_MAX would be worthwhile.

Dave <barx>
Group Member
Thu 29 Dec 2022 03:52:30 AM UTC, original submission:  

I have a few uses in mind for .R.  It is presently fixed at 10,000, which is not presently a useful value for any particular purpose.  It just means "large", and I've never seen any code that tests it for its historical purpose (checking for register availability, a sort of crude memory capacity test).

1. We could advise users to employ it if they want to turn on centering or right-alignment "forever".

2. We could advise users to use it, or turn our own macro packages toward it, for expression of an "infinite" page length.

3. If done carefully, a user could avoid integer overflow errors before undertaking arithmetic with large magnitudes.

4. It would be easy to determine the width of the underlying integer type from within the roff language.  At present the language doesn't really abstract this, it simply lays down land mines that you blunder into if your arithmetic gets too ambitious.

G. Branden Robinson <gbranden>
Group administrator

 

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

Attach Files:
   
   
Comment:
   

No files currently attached

 

Carbon-Copy List
  • -email is unavailable- added by barx (Posted a comment)
  • -email is unavailable- added by gbranden (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 11 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2024-08-20 gbranden Planned ReleaseNone 1.24.0
    2024-08-20 gbranden StatusIn Progress Fixed
        Open/ClosedOpen Closed
    2024-08-18 gbranden StatusNone In Progress
        Assigned toNone gbranden
    2024-07-17 gbranden Dependencies- Depends on bugs #66001
    2024-07-17 gbranden DependenciesRemoved dependency to bugs #64301 -
    2024-01-22 gbranden Dependencies- bugs #65190 is dependent
    2023-08-10 gbranden StatusPostponed None
        Dependencies- Depends on bugs #64301
    2023-08-10 gbranden Dependencies- bugs #63837 is dependent

    Back to the top

    Powered by Savane 3.14-f13d.
    Corresponding source code