URL:
  <https://savannah.gnu.org/bugs/?68017>

                 Summary: [troff] NetHack Guidebook reports many "ignoring
invalid numeric expression starting with a tab character" errors
                   Group: GNU roff
               Submitter: gbranden
               Submitted: Thu 05 Feb 2026 07:41:22 AM UTC
                Category: Core
                Severity: 3 - Normal
              Item Group: Warning/Suspicious behaviour
                  Status: In Progress
                 Privacy: Public
             Assigned to: gbranden
             Open/Closed: Open
         Discussion Lock: Unlocked
         Planned Release: None


    _______________________________________________________

Follow-up Comments:


-------------------------------------------------------
Date: Thu 05 Feb 2026 07:41:22 AM UTC By: G. Branden Robinson <gbranden>
Trying out a NetHack build with my _groff_ post-1.24.0.rc2 working copy, I
observed the following with some dismay.


cat Guidebook.dated.mn | ../util/makedefs --grep --input - --output - | tbl
tmac.n - | nroff -c -Tascii -wall -Wtab -Wrange -Wel -Wscale | col -bx >
Guidebook
troff: error: unrecognized warning category 'el'
troff:<standard input>:34: error: cannot open 'tmac.nh ': No such file or
directory
troff:<standard input>:35: error: cannot open 'doc/tmac.nh': No such file or
directory
troff:<standard input>:116: error: ignoring invalid numeric expression
starting with a tab character
...
troff:<standard input>:6758: error: ignoring invalid numeric expression
starting with a tab character
troff: error: ignoring invalid numeric expression starting with a tab
character
troff: error: ignoring invalid numeric expression starting with a tab
character
troff: error: ignoring invalid numeric expression starting with a tab
character
troff: error: ignoring invalid numeric expression starting with a tab
character
troff: error: ignoring invalid numeric expression starting with a tab
character
troff: error: ignoring invalid numeric expression starting with a tab
character


In fact there are so many occurrences of the last, I had to chop them out for
this bug submission to fit.


$ grep -c 'error: ignoring invalid numeric expression starting with a tab
character' OUT
1640


So that looks terrifying, but the sheer quantity of identical diagnostics
suggests that some macro has something awry in it.  No _human_ screws up so
consistently.

First, however, let's dispose of the first few lines of output.


troff: error: unrecognized warning category 'el'


This is harmless and appropriate; as our "NEWS" file says, this warning
category is withdrawn.  Ideally, it'd probably be a warnings instead of an
error, but then we'd have the curious situation of having to introduce a
warning category about the manipulation of warnings.

And once we fix the subject of this ticket, I feel I can reassure myself that
NetHack developers will not dispatch the multiple independent hit squads that
stumbled into each other in Jeffrey Epstain's prison cell to dispose of *me*.

Anyway, we can fix the foregoing with a one-liner to NetHack.  Well, I was
planning for it to be a one-liner but got confused by the logic.

[https://github.com/NetHack/NetHack/commit/d116052796d3810f8815650e9898e10d945de606
When I looked up who the chucklehead programmer was who came up with such a
baroque inconsistency as "GROFFGE123" and "GROFFLT124", I experienced an
unhappy feeling.]


$ git diff
diff --git a/sys/unix/hints/include/misc.370
b/sys/unix/hints/include/misc.370
index 6608cf45a..40df6ab35 100644
--- a/sys/unix/hints/include/misc.370
+++ b/sys/unix/hints/include/misc.370
@@ -70,18 +70,18 @@
 MAN2TXTPRE = -T ascii
 MAN2TXTPOST= | col -b
 else
-#detection of groff
+#
+# detect groff and collect its version number
 NROFFISGROFF := $(shell echo `nroff --version | grep "GNU groff version"`)
 #$(info NROFFISGROFF=$(NROFFISGROFF))
 ifneq "$(NROFFISGROFF)" ""
-# get the version of groff and flag if it is gt or eq to 1.23
+# flag if the version of groff is greater than or equal to 1.23
 GROFFGE123 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \>= 23)
-# or less than 1.24
-GROFFLT124 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \< 24)
-# -Wtab -Wrange are for the sake of tmac.n.
+# flag if the version of groff is greater than or equal to 1.24
+GROFFGE124 := $(shell expr `echo $(NROFFISGROFF) | cut -f2 -d.` \>= 24)
 NROFF_FLAGS := -wall -Wtab -Wrange
-ifneq "$(GROFFLT124)" ""
-NROFF_FLAGS += -Wel -Wscale
+ifneq "$(GROFFGE124)" ""
+NROFF_FLAGS += -Wscale
 endif
 endif  # NROFFISGROFF
 


The crux is that we dispose of "-Wel".

Next issue?


troff:<standard input>:34: error: cannot open 'tmac.nh ': No such file or
directory
troff:<standard input>:35: error: cannot open 'doc/tmac.nh': No such file or
directory


The first of the two lines is the precise scenario at the very top of the
"NEWS" entry for _groff_ 1.24.0.


VERSION 1.24.0 release candidate
================================

Noteworthy incompatible changes
-------------------------------

*  If your roff(7) documents follow any of the requests `cf`, `hpf`,
   `hpfa`, `mso`, `msoquiet`, `nx`, `open`, `opena`, `so`, `soquiet`, or
   `trf` with a comment after their file name argument, and did not
   place that comment immediately after the file name, you are likely to
   get a diagnostic message resembling the following.

    warning: cannot open macro file 'e.tmac ': No such file or directory


Again, we could go with a one-liner, but my ambition is uncontrollable.


diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn
index a8f739d5a..89f581285 100644
--- a/doc/Guidebook.mn
+++ b/doc/Guidebook.mn
@@ -31,8 +31,21 @@
 .lt 70n
 .\}
 .
-.so tmac.nh \" extra macros which aren't in tmac.n
-.if !\n(nH .so doc/tmac.nh
+.\" Load extra macros that "tmac.n" doesn't define.
+.de So
+.ie \n(.g&(\n(.x=1)&(\n(.y>22) .soquiet \\$1
+.el                            .so      \\$1
+..
+.
+.So tmac.nh
+.if !\n(nH So doc/tmac.nh
+.
+.if !\n(nH \{\
+.tm fatal error: cannot locate "tmac.nh" macro package
+.ab
+.\}
+.
+.rm So
 .
 .\" building Guidebook.txt doesn't have CR font available; groff 1.23 issues
 .\" a warning each time any font can't be loaded; earlier versions silently


To fix the 1,640 error diagnostics is going to require a change to GNU
_troff_.

This is essentially a partial reversion of
[https://cgit.git.savannah.gnu.org/cgit/groff.git/commit/?id=64711b3a875ce9ea3a023401d641b9922de61cc8
commit 64711b3a875, 14 Novvember.]


diff --git a/src/roff/troff/number.cpp b/src/roff/troff/number.cpp
index 0cc58dbc9..e650c4bdb 100644
--- a/src/roff/troff/number.cpp
+++ b/src/roff/troff/number.cpp
@@ -380,6 +380,11 @@ static bool is_valid_term(units *u, int scaling_unit,
       tok.next();
       is_negative = !is_negative;
     }
+    else if (tok.is_tab()) {
+      warning(WARN_TAB, "ignoring numeric expression starting with %1",
+             tok.description());
+      return false;
+    }
     else
       break;
   int c = tok.ch(); // safely compares to char literals; TODO: grochar


The provoking agent lay in Matt Bishop's "mn" macro package.

It had control lines that looked like this.


.vs<TAB><TAB><TAB><TAB><TAB>\" switch to previous vertical spacing


The `vs` request takes an _optional_ argument.  Our documentation is insistent
that tabs cannot be used to separate arguments.


5.6.2 Invoking Requests
-----------------------

A control character is optionally followed by tabs and/or spaces and
then an identifier naming a request or macro.  The invocation of an
unrecognized request is interpreted as a macro call.  Defining a macro
with the same name as a request replaces the request.  Deleting a
request name with the 'rm' request makes it unavailable.  The 'als'
request can alias requests, permitting them to be wrapped or
non-destructively replaced.  *Note Strings::.

   There is no inherent limit on argument length or quantity.  Most
requests take one or more arguments, and ignore any they do not expect.
A request may be separated from its arguments by tabs or spaces, but
only spaces can separate an argument from its successor.  Only one
between arguments is necessary; any excess is ignored.

...

5.7 Comments
============

One of the most common forms of escape sequence is the comment.(1)
(*note Comments-Footnote-1::)

 -- Escape sequence: \"
     Start a comment; read everything up to the next newline in copy
     mode (*note Copy Mode::) and discard it.  '\"' is interpreted even
     in copy mode.

     It can be tricky to keep the comments from interfering with the
     appearance of the output.  If the escape sequence is to the right
     of some text or a request, that portion of the line is ignored, but
     GNU 'troff' processes spaces preceding it normally.  This affects
     requests that read the remainder of the control line as a single
     argument, including 'ds', 'as', 'tm', and 'char'; their variants;
     as well as 'ab', 'device', 'length', 'output', 'pi', 'pso', 'rd',
     'sy', 'write', and 'writec'.

     One possibly irritating idiosyncrasy is that tabs should not be
     used to vertically align comments in the source document.  Tab
     characters are not treated as separators between a request name and
     its first argument, nor between arguments.


However, AT&T _troff_ accepted input exhibits like Bishop's when it couldn't
come up with anything better to do with them.







    _______________________________________________________

Reply to this item at:

  <https://savannah.gnu.org/bugs/?68017>

_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/

Attachment: signature.asc
Description: PGP signature

Reply via email to