I have an update on this.

At 2022-11-25T02:33:57-0600, G. Branden Robinson wrote:
> At 2022-11-24T19:47:35+0100, hbezemer--- via wrote:
> > When I make exams I sometimes use linenumbering so I can refer to a
> > specific line of a source.  I noticed some unexpected behaviour when
> > having a table after the part that has linenumbers.  The table also
> > gets linenumbers added, which should not be the case of course.  I
> > can reproduce the behaviour with the example below.
[...]
> I think the formatter's line numbering feature and the tbl
> preprocessor interact in unexpected ways, and apparently always have.
> 
> First let me offer an example input and then I will show how it
> behaves with Unix Version 7 troff (1979), Heirloom Doctools troff
> (2019), groff 1.22.4 (2018) and groff Git HEAD.
> 
> $ cat ~/tmp/line-numbered-table.roff
> .nm 1
> Here is a line of output.
> Sic transit adispicing meatballs.
> Let's pad it out with more content to ensure the line breaks.
> .TS

I added a "box;" region option to this; the result is shown below.

I do not recommend doing that on Unix Version 7 or Heirloom Doctools
nroff.  You will not be happy.

> L.
> This is my table.
> There are many like it but this one is mine.
> T{
> My table is my best exhibit;
> I will guard it as I guard my life.
> Even as it requires multiple output lines for an entry.
> T}
> .TE
> What is the line number now?
> 
> Unix Version 7 on a SIMH PDP-11/45:
> 
>   1 Here is a line of  output.   Sic  transit  adispicing  meatballs.
>   2 Let's pad it out with more content to ensure the line breaks.
>   6 This is my table.
>   7 There are many like it but this one is mine.
>   8
>   9   3 My table is my best exhibit; I will guard it
>  10   4 as  I  guard  my  life.  Even as it requires
>  11   5 multiple output lines for an entry.
>  12 What is the line number now?
[...]
> groff 1.22.4:
> 
>   1 Here  is  a  line  of  output.  Sic transit adispicing meatballs.
>   2 Let’s pad it out with more content to ensure the line breaks.
>   6 This is my table.
>   7 There are many like it but this one is mine.
>   8   3 My table is my best exhibit; I will guard it
>   9   4 as  I  guard  my  life.  Even as it requires
>  10   5 multiple output lines for an entry.
> What is the line number now?
> 
> groff Git HEAD:
> 
>   1 Here  is  a  line  of  output.  Sic transit adispicing meatballs.
>   2 Let’s pad it out with more content to ensure the line breaks.
> This is my table.
> There are many like it but this one is mine.
>   3 My table is my best exhibit; I will guard it
>   4 as I guard my life.   Even  as  it  requires
>   5 multiple output lines for an entry.
> What is the line number now?

Here's what my working copy produces now.

$ ./build/test-groff -t -Tascii EXPERIMENTS/line-numbered-table.roff \
 | cat -s
  1 Here  is  a  line  of  output.  Sic transit adispicing meatballs.
  2 Let's pad it out with more content to ensure the line breaks.
+---------------------------------------------+
|This is my table.                            |
|There are many like it but this one is mine. |
|My table is my best exhibit; I will guard it |
|as I guard my life.   Even  as  it  requires |
|multiple output lines for an entry.          |
+---------------------------------------------+
  3 What is the line number now?

> 6.  groff has an apparently long-standing bug where the state of output
>     line numbering does not get properly restored after a table region.
>     That just ain't right and should be fixed.

I have fixed this.

> 7.  groff Git shuts off line numbering when beginning a table.  I think
>     this is what people want most of the time, so I propose to retain it
>     as the default.  But I should probably document it in NEWS.
> 
>     Also see <https://savannah.gnu.org/bugs/?59812>.

I had actually planned NOT to do this right away, by trying to pivot to
a bug-fixed AT&T tbl behavior first, but it turned out to be really easy
to just suppress all line numbering in tables while still correctly
restoring it afterward.  This also enabled me to remove a lot of code
from George Helffrich in 2011 that was complex and defeated all my
efforts to understand it.

> 8.  However my fix for Savannah #59812 was not complete.  I didn't
>     suppress line numbering in the diversion/environment used to format
>     text blocks.  We see above that it rises from the grave.  We don't
>     get double numbering like old-school troff but it's still likely a
>     shock to users unaccustomed to the old system.

This no longer happens.

> 9.  Another aspect of my incomplete fix was, I think, introducing only
>     an .nm register.  I probably should have introduced one for .nn as
>     well.  (This Ossanna troff request suspends line numbering for the
>     next N lines, default 1. )  I'm thinking what I want to do is save
>     the states of _both_ of these when a table begins, and restore them
>     at the table's end, so that the lines of the table, even text
>     blocks, effectively don't count for incrementation or suspension
>     purposes.

I did indeed have to introduce an .nn register.  Also, if we are to
permit line numbering in tables, we're going to need 3 more registers
exposing troff environmental parameters: the increment, suffixed space,
and indentation of line numbering.  These are also not visible in the
groff(7) language present and are not in any other implementation I'm
aware of.  They therefore cannot be saved for restoration and any use of
`.nm` inside a tbl(1) table, whether produced by the preprocessor user,
will clobber them irrevocably.

> 10. What if someone _wants_ numbered table lines?  Or wants to number
>     certain regions of the table?  This should be possible; I think I
>     have seen some standards documents maintain numbering even in
>     tabular contexts.  In that event they should be able to use `nm` and
>     `nn` requests between table rows just as they can invoke other
>     requests.  These requests should also work within text blocks.  I
>     propose however that any such requests not override the post-table
>     restoration of line numbering and numbering suspension states.

As I noted after item #9, supporting this will mean we need more
interface registers.

>     The user can also assign arbitrary values to the `ln` register.
>     That's a little trickier.  I propose that, when starting a table
>     region, the line number `ln` be remembered by tbl, and if it has
>     been changed by the user during the course of the table, we
>     _don't_ restore it.  If it hasn't, we do, so that line numbering
>     "picks up where it left off".

I've corrected the above to say `ln`, not `nl`.  Who needs more than 2
characters in an identifier?

Picking up line numbering where it left off is now implemented.

> 11. If all that is too hard, a better approach might be to simply warn
>     that tables get their lines numbered if numbering was on when they
>     started.  But the double-numbering of text block lines should be
>     fixed in any case.

Double-numbering is fixed but there is no reason to warn here, unless we
want an inverse warning that groff simply won't number tbl lines.

> 12. Another possibility is to have a new region option (like "box" or
>     "center"), perhaps called "numbering", which retains line
>     numbering.  A variant of this would be to add "nonumbering" and
>     flip the default for slavish AT&T compatibility.

This remains a possibility.

> Because there are several changes here, and some are
> AT&T-incompatible, I propose to proceed in the following order.

Best-laid plans...

> A. Implement the `.nn` register.  This should be a piece of observable
>    state in the formatter, and it isn't (cf. \n[.ce], \n[.hlc],
>    \n[.rj]).

Done.

> B. Fix the text block numbering bug by forcing line numbering off when
>    starting a text block diversion.

Done.

> C. Update tbl(1) to advise the user about line numbering and tell them
>    how to defeat it.  (Save `nl` and do ".nm" before the table, then
>    ".nm \n[saved-register]" afterward.)

Not necessary.

> D. See what folks on this list think about my more radical ideas.
> 
> Thoughts?

Feedback still wanted.

See the attached diff for my changes to tbl(1).

Regards,
Branden
diff --git a/src/preproc/tbl/table.cpp b/src/preproc/tbl/table.cpp
index 39ea5621e..7cf5316fe 100644
--- a/src/preproc/tbl/table.cpp
+++ b/src/preproc/tbl/table.cpp
@@ -58,16 +58,13 @@ const int DEFAULT_COLUMN_SEPARATION = 3;
 #define REPEATED_VPT_MACRO PREFIX "rvpt"
 #define SUPPRESS_BOTTOM_REG PREFIX "supbot"
 #define SAVED_DN_REG PREFIX "dn"
-#define ROW_START_LINE_REG PREFIX "lnst"
-#define ROW_SAVE_LINE_REG PREFIX "lnsv"
-#define ROW_MAX_LINE_REG PREFIX "lnmx"
-#define REPEATED_NM_SET_MACRO PREFIX "rlns"
-#define REPEATED_NM_SUS_MACRO PREFIX "rlnx"
 #define SAVED_HYPHENATION_MODE_REG PREFIX "hyphmode"
 #define SAVED_HYPHENATION_LANG_NAME PREFIX "hyphlang"
 #define SAVED_HYPHENATION_MAX_LINES_REG PREFIX "hyphmaxlines"
 #define SAVED_HYPHENATION_MARGIN_REG PREFIX "hyphmargin"
 #define SAVED_HYPHENATION_SPACE_REG PREFIX "hyphspace"
+#define SAVED_NUMBERING_LINENO PREFIX "linenumber"
+#define SAVED_NUMBERING_SUPPRESSION_COUNT PREFIX "linenumbersuppresscnt"
 
 // this must be one character
 #define COMPATIBLE_REG PREFIX "c"
@@ -698,7 +695,8 @@ void block_entry::do_divert(int alphabetic, int ncols, const string *mw,
       return;
   }
   printfs(".di %1\n", block_diversion_name(start_row, start_col));
-  prints(".if \\n[" SAVED_FILL_REG "] .fi\n"
+  prints(".nm\n"
+	 ".if \\n[" SAVED_FILL_REG "] .fi\n"
 	 ".in 0\n");
   prints(".ll ");
   for (i = start_col; i <= end_col; i++)
@@ -1821,31 +1819,6 @@ void table::init_output()
 	 ".de " REPEATED_VPT_MACRO "\n"
 	 ".vpt \\$1\n"
 	 ".if !'\\n(.z'' \\!." REPEATED_VPT_MACRO " \"\\$1\"\n"
-	 "..\n"
-	 ".de " REPEATED_NM_SET_MACRO "\n"
-	 ".ie !'\\n(.z'' \\{.nm\n"
-	 "\\!." REPEATED_NM_SET_MACRO " \"\\$1\"\n"
-	 ".\\}\n"
-	 ".el .if \\n[.nm] .if \\n[ln] \\{\\\n"
-	 ".if '\\$1'd' .nr " ROW_START_LINE_REG " \\n[ln]\n"
-	 ".if '\\$1's' .nm \\n[" ROW_START_LINE_REG "]\n"
-	 ".if '\\$1'm' .nr " ROW_MAX_LINE_REG " \\n[ln]>?\\n[" ROW_MAX_LINE_REG "]\n"
-	 ".\\}\n"
-	 "..\n"
-	 ".de " REPEATED_NM_SUS_MACRO "\n"
-	 ".ie !'\\n(.z'' \\{.nm\n"
-	 "\\!." REPEATED_NM_SUS_MACRO " \"\\$1\"\n"
-	 ".\\}\n"
-	 ".el .if \\n[.nm] .if \\n[ln] \\{\\\n"
-	 ".ie '\\$1's' \\{\\\n"
-	 ".nr " ROW_SAVE_LINE_REG " \\n(ln<?\\n[" ROW_MAX_LINE_REG "]\n"
-	 ".nm +0 \\n[ln]+42\n"
-	 ".\\}\n"
-	 ".el \\{\\\n"
-	 ".nr ln \\n[" ROW_SAVE_LINE_REG "]\n"
-	 ".nm \\n[ln] 1\n"
-	 ".\\}\n"
-	 ".\\}\n"
 	 "..\n");
   if (!(flags & NOKEEP)) {
     prints(".de " KEEP_MACRO_NAME "\n"
@@ -1890,12 +1863,9 @@ void table::init_output()
       prints(".\\}\n");
     }
     prints(".nf\n"
-	   ".if \\n[.nm] .if \\n[ln] .nm \\n[ln]\n"
-	   ".nr " ROW_MAX_LINE_REG " \\n[ln]\n"
 	   ".ls 1\n"
 	   "." SECTION_DIVERSION_NAME "\n"
 	   ".ls\n"
-	   ".if \\n[.nm] .if \\n[ln] .nm\n"
 	   ".rm " SECTION_DIVERSION_NAME "\n"
 	   ".\\}\n"
 	   "..\n"
@@ -1939,19 +1909,23 @@ void table::init_output()
 	   ".in 0\n"
 	   ".ls 1\n"
 	   ".nf\n"
-	   ".if \\n[.nm] .if \\n[ln] .nm \\n[ln]\n"
 	   "." TABLE_DIVERSION_NAME "\n"
 	   ".\\}\n"
 	   ".rm " TABLE_DIVERSION_NAME "\n"
 	   ".\\}\n"
-	   ".if \\n[.nm] .if \\n[ln] \\{.nm\n"
-	   ".nr ln \\n[" ROW_MAX_LINE_REG "]\n"
-	   ".\\}\n"
 	   "..\n");
   }
   prints(".ec\n"
-	 ".ce 0\n"
-	 ".nf\n");
+	 ".ce 0\n");
+  // TODO: flags & NONUMBERING
+  prints(".nr " SAVED_NUMBERING_LINENO " 0\n"
+	 ".nr " SAVED_NUMBERING_SUPPRESSION_COUNT " 0\n"
+	 ".if (\\n[.nm] & (1 - \\n[.nn])) \\{\\\n"
+	 ".  nr " SAVED_NUMBERING_LINENO " \\n[ln]\n"
+	 ".  nr " SAVED_NUMBERING_SUPPRESSION_COUNT " \\n[.nn]\n"
+	 ".  nn 2147483647\n" // MAXINT; inelegant but effective
+	 ".\\}\n");
+  prints(".nf\n");
 }
 
 string block_width_reg(int r, int c)
@@ -2416,7 +2390,6 @@ void table::print_single_hline(int r)
 {
   prints(".vs " LINE_SEP ">?\\n[.V]u\n"
 	 ".ls 1\n"
-	 "." REPEATED_NM_SUS_MACRO " s\n"
 	 "\\v'" BODY_DEPTH "'"
 	 "\\s[\\n[" LINESIZE_REG "]]");
   if (r > nrows - 1)
@@ -2453,8 +2426,7 @@ void table::print_single_hline(int r)
     }
   }
   prints("\\s0\n");
-  prints("." REPEATED_NM_SUS_MACRO " r\n"
-	 ".ls\n"
+  prints(".ls\n"
 	 ".vs\n");
 }
 
@@ -2463,7 +2435,6 @@ void table::print_double_hline(int r)
   prints(".vs " LINE_SEP "+" DOUBLE_LINE_SEP
 	 ">?\\n[.V]u\n"
 	 ".ls 1\n"
-	 "." REPEATED_NM_SUS_MACRO " s\n"
 	 "\\v'" BODY_DEPTH "'"
 	 "\\s[\\n[" LINESIZE_REG "]]");
   if (r > nrows - 1)
@@ -2522,7 +2493,6 @@ void table::print_double_hline(int r)
     }
   }
   prints("\\s0\n"
-	 "." REPEATED_NM_SUS_MACRO " r\n"
 	 ".ls\n"
 	 ".vs\n");
 }
@@ -2729,8 +2699,7 @@ void table::define_bottom_macro()
     print_single_hline(0);
     prints(".\\}\n");
   }
-  prints("." REPEATED_NM_SUS_MACRO " s\n"
-	 ".ls 1\n");
+  prints(".ls 1\n");
   for (vertical_rule *p = vrule_list; p; p = p->next)
     p->contribute_to_bottom_macro(this);
   if (flags & DOUBLEBOX)
@@ -2747,8 +2716,7 @@ void table::define_bottom_macro()
 	   ".sp -1\n"
 	   "\\v'" BODY_DEPTH "'\\h'|\\n[TW]u'\\s[\\n[" LINESIZE_REG "]]"
 	   "\\D'l 0 |\\n[" TOP_REG "]u-1v'\\s0\n");
-  prints("." REPEATED_NM_SUS_MACRO " r\n"
-	 ".ls\n");
+  prints(".ls\n");
   prints(".nr " LAST_PASSED_ROW_REG " \\n[" CURRENT_ROW_REG "]\n"
 	 ".sp |\\n[" SAVED_VERTICAL_POS_REG "]u\n"
 	 "." REPEATED_VPT_MACRO " 1\n"
@@ -2824,8 +2792,6 @@ void table::do_row(int r)
   // we might have had a .TH, for example,  since we last tried
   if (!(flags & NOKEEP) && row_begins_section(r))
     prints(".if \\n[" USE_KEEPS_REG "] ." KEEP_MACRO_NAME "\n");
-  prints("." REPEATED_NM_SET_MACRO " d\n"
-	 ".nr " ROW_MAX_LINE_REG " \\n[ln]\n");
   printfs(".mk %1\n", row_start_reg(r));
   prints(".mk " BOTTOM_REG "\n"
 	 "." REPEATED_VPT_MACRO " 0\n");
@@ -2922,7 +2888,6 @@ void table::do_row(int r)
     table_entry *e = entry[r][c];
     if (e) {
       if (e->end_row == r && e->to_simple_entry() == 0) {
-	prints("." REPEATED_NM_SET_MACRO " s\n");
 	e->position_vertically();
 	e->print();
 	prints(".nr " BOTTOM_REG " \\n[" BOTTOM_REG "]>?\\n[.d]\n");
@@ -2931,8 +2896,7 @@ void table::do_row(int r)
       c = e->end_col;
     }
   }
-  prints("." REPEATED_NM_SET_MACRO " m\n"
-	 "." REPEATED_VPT_MACRO " 1\n"
+  prints("." REPEATED_VPT_MACRO " 1\n"
 	 ".sp |\\n[" BOTTOM_REG "]u\n"
 	 "\\*[" TRANSPARENT_STRING_NAME "].nr " NEED_BOTTOM_RULE_REG " 1\n");
   if (r != nrows - 1 && (flags & ALLBOX)) {
@@ -2960,7 +2924,6 @@ void table::do_row(int r)
     if (!(flags & NOKEEP) && row_ends_section(r))
       prints(".if \\n[" USE_KEEPS_REG "] ." RELEASE_MACRO_NAME "\n");
   }
-  prints(".if \\n[.nm] .if \\n[ln] .nr ln \\n[" ROW_MAX_LINE_REG "]\n");
 }
 
 void table::do_top()
@@ -2969,8 +2932,7 @@ void table::do_top()
   if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
     prints("." TABLE_KEEP_MACRO_NAME "\n");
   if (flags & DOUBLEBOX) {
-    prints("." REPEATED_NM_SUS_MACRO " s\n"
-	   ".ls 1\n"
+    prints(".ls 1\n"
 	   ".vs " LINE_SEP ">?\\n[.V]u\n"
 	   "\\v'" BODY_DEPTH "'\\s[\\n[" LINESIZE_REG "]]\\D'l \\n[TW]u 0'\\s0\n"
 	   ".vs\n"
@@ -2984,8 +2946,7 @@ void table::do_top()
 	    "\n",
 	    column_divide_reg(0),
 	    column_divide_reg(ncolumns));
-    prints("." REPEATED_NM_SUS_MACRO " r\n"
-	   ".ls\n"
+    prints(".ls\n"
 	   ".vs\n");
   }
   else if (flags & (ALLBOX | BOX)) {
@@ -3019,10 +2980,6 @@ void table::do_bottom()
 	 "..\n");
   if (!(flags & NOKEEP) && (flags & (BOX | DOUBLEBOX | ALLBOX)))
     prints("." TABLE_RELEASE_MACRO_NAME "\n");
-  else
-    prints(".if \\n[.nm] .if \\n[ln] \\{.nm\n"
-	   ".nr ln \\n[" ROW_MAX_LINE_REG "]\n"
-	   ".\\}\n");
   if (flags & DOUBLEBOX)
     prints(".sp " DOUBLE_LINE_SEP "\n");
   // Horizontal box lines take up an entire row on nroff devices (maybe
@@ -3035,6 +2992,10 @@ void table::do_bottom()
   if (flags & DOUBLEBOX)
     prints(".if n .sp\n");
   prints("." RESET_MACRO_NAME "\n"
+	 ".nn \\n[" SAVED_NUMBERING_SUPPRESSION_COUNT "]\n"
+	 ".ie \\n[" SAVED_NUMBERING_LINENO "] \\\n"
+	 ".    nm \\n[" SAVED_NUMBERING_LINENO "]\n"
+	 ".el .nm\n"
 	 ".fc\n"
 	 ".cp \\n(" COMPATIBLE_REG "\n");
 }

Attachment: signature.asc
Description: PGP signature

Reply via email to