commit 0ccb60f70e3f6bf2355663a389f8e83af53c8c52
Author: G. Branden Robinson <g.branden.robinson@gmail.com>
Date:   Wed Jul 26 02:58:27 2023 -0500

    [troff]: Add new `.trap` built-in register.
    
    * src/roff/troff/div.h (class diversion, class macro_diversion, class
      top_level_diversion): Declare new member function
      `get_next_trap_name`.
    
    * src/roff/troff/div.cpp (macro_diversion::get_next_trap_name): New
      member function returns name of diversion trap if any, and if its
      position is greater than the vertical drawing position, otherwise an
      empty string.
    
      (top_level_diversion::get_next_trap_name): New member function returns
      the name of the next vertical position trap, if any, otherwise an
      empty string.
    
      (class next_trap_name_reg): New class has one member, a `get_string()`
      function.
    
      (next_trap_name_reg::get_string): New function.
    
      (init_div_requests): Add `.trap` to the register dictionary and wire
      it up to `next_trap_name_reg`.
    
    * doc/groff.texi (Page Localization Traps):
    * man/groff.7.man (Read-only registers):
    * man/groff_diff.7.man (New registers): Document it.
    
    * src/roff/groff/tests/dot-trap_register_works.sh: Test it.
    * src/roff/groff/groff.am (groff_TESTS): Run test.
    
    * NEWS: Add item.
    
    Fixes <https://savannah.gnu.org/bugs/?64212>.  Thanks to Dave Kemper for
    feedback.

diff --git a/ChangeLog b/ChangeLog
index a184500e1..d5311f09c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2023-07-26  G. Branden Robinson <g.branden.robinson@gmail.com>
+
+	[troff]: Add new `.trap` built-in register.
+
+	* src/roff/troff/div.h (class diversion, class macro_diversion,
+	class top_level_diversion): Declare new member function
+	`get_next_trap_name`.
+	* src/roff/troff/div.cpp (macro_diversion::get_next_trap_name):
+	New member function returns name of diversion trap if any, and
+	if its position is greater than the vertical drawing position,
+	otherwise an empty string.
+	(top_level_diversion::get_next_trap_name): New member function
+	returns the name of the next vertical position trap, if any,
+	otherwise an empty string.
+	(class next_trap_name_reg): New class has one member, a
+	`get_string()` function.
+	(next_trap_name_reg::get_string): New function.
+	(init_div_requests): Add `.trap` to the register dictionary and
+	wire it up to `next_trap_name_reg`.
+
+	* doc/groff.texi (Page Localization Traps):
+	* man/groff.7.man (Read-only registers):
+	* man/groff_diff.7.man (New registers): Document it.
+
+	* src/roff/groff/tests/dot-trap_register_works.sh: Test it.
+	* src/roff/groff/groff.am (groff_TESTS): Run test.
+
+	* NEWS: Add item.
+
+	Fixes <https://savannah.gnu.org/bugs/?64212>.  Thanks to Dave
+	Kemper for feedback.
+
 2023-07-21  G. Branden Robinson <g.branden.robinson@gmail.com>
 
 	* src/roff/troff/node.cpp (unbreakable_space_node::tprint):
diff --git a/NEWS b/NEWS
index 92a895cf2..0bd1348f5 100644
--- a/NEWS
+++ b/NEWS
@@ -23,6 +23,9 @@ o GNU troff output now reports unbreakable spaces (those produced with
   the `\~` escape sequence) as word breaks with the documentary 'w'
   command, just as regular breakable spaces do.
 
+o A new read-only, string-valued register, `.trap`, interpolates the
+  name of the next vertical position trap that will be sprung.
+
 eqn
 ---
 
diff --git a/doc/groff.texi b/doc/groff.texi
index e8e62c05c..a5d66e112 100644
--- a/doc/groff.texi
+++ b/doc/groff.texi
@@ -14693,6 +14693,13 @@ @node Page Location Traps
 sense to interpolate it outside of macros called by traps.
 @endDefreg
 
+@Defreg {.trap}
+@cindex next trap name register (@code{.trap})
+@cindex trap name, next, register (@code{.trap})
+This read-only, string-valued register interpolates the name of the next
+vertical position trap that will be sprung.
+@endDefreg
+
 @Defreg {.pe}
 @cindex @code{bp} request, and traps (@code{.pe})
 @cindex traps, sprung by @code{bp} request (@code{.pe})
diff --git a/man/groff.7.man b/man/groff.7.man
index 91809aefb..ce1ae7779 100644
--- a/man/groff.7.man
+++ b/man/groff.7.man
@@ -6265,6 +6265,16 @@ .SS "Read-only registers"
 (string-valued).
 .
 .TP
+.REG .trap
+Name of the next vertical position trap that will be sprung
+(string-valued);
+see
+.request .wh ,
+.request .ch ,
+and
+.request .dt .
+.
+.TP
 .REG .trunc
 Amount of vertical space truncated by the most recently sprung
 vertical position trap,
diff --git a/man/groff_diff.7.man b/man/groff_diff.7.man
index 90901ea56..da35a3b78 100644
--- a/man/groff_diff.7.man
+++ b/man/groff_diff.7.man
@@ -4469,6 +4469,14 @@ .SS "New registers"
 .
 .
 .TP
+.B \[rs]n[.trap]
+Interpolate the name of the next vertical position trap that will be
+sprung.
+.
+This is a string-valued register.
+.
+.
+.TP
 .B \[rs]n[.trunc]
 Interpolate amount of vertical space truncated by the most recently
 sprung page location trap,
diff --git a/src/roff/groff/groff.am b/src/roff/groff/groff.am
index 306677bf9..d33f86d51 100644
--- a/src/roff/groff/groff.am
+++ b/src/roff/groff/groff.am
@@ -44,6 +44,7 @@ groff_TESTS = \
   src/roff/groff/tests/dot-cp_register_works.sh \
   src/roff/groff/tests/dot-nm_register_works.sh \
   src/roff/groff/tests/dot-nn_register_works.sh \
+  src/roff/groff/tests/dot-trap_register_works.sh \
   src/roff/groff/tests/draw-arc.sh \
   src/roff/groff/tests/draw-circle.sh \
   src/roff/groff/tests/draw-ellipse.sh \
diff --git a/src/roff/groff/tests/dot-trap_register_works.sh b/src/roff/groff/tests/dot-trap_register_works.sh
new file mode 100755
index 000000000..810e2cb50
--- /dev/null
+++ b/src/roff/groff/tests/dot-trap_register_works.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Copyright (C) 2023 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+groff="${abs_top_builddir:-.}/test-groff"
+
+fail=
+
+wail () {
+  echo ...FAILED >&2
+  fail=yes
+}
+
+input='.pl 3v
+.de bomb
+.tm Boom!
+..
+.nf
+.wh 1v bomb
+.ptr
+.tm A: .trap=\n[.trap]
+foo nl=\n[nl]u
+.tm B: .trap=\n[.trap]
+bar nl=\n[nl]u'
+
+error=$(printf "%s\n" "$input" | "$groff" -T ascii 2>&1 > /dev/null)
+echo "$error"
+
+echo "checking operation of .trap register prior to trap"
+echo "$error" | grep -Fqx 'A: .trap=bomb' || wail
+
+echo "checking operation of .trap register when no traps remain"
+echo "$error" | grep -Fqx 'B: .trap=' || wail
+
+input='.de XX
+.tm SNAP!
+..
+.di DD
+.dt 1v XX
+.tm A: .trap=\n[.trap]
+.nf
+foo
+.tm B: .trap=\n[.trap]
+.di'
+
+error=$(printf "%s\n" "$input" | "$groff" -T ascii 2>&1 > /dev/null)
+echo "$error"
+
+echo "checking operation of .trap in diversion, prior to trap"
+echo "$error" | grep -Fqx 'A: .trap=XX' || wail
+
+echo "checking operation of .trap in diversion, after trap"
+echo "$error" | grep -Fqx 'B: .trap=' || wail
+
+test -z "$FAIL"
diff --git a/src/roff/troff/div.cpp b/src/roff/troff/div.cpp
index f4dc06f33..f1f1ebda5 100644
--- a/src/roff/troff/div.cpp
+++ b/src/roff/troff/div.cpp
@@ -250,6 +250,15 @@ vunits macro_diversion::distance_to_next_trap()
     return vunits(INT_MAX - vresolution);
 }
 
+const char *macro_diversion::get_next_trap_name()
+{
+  if (!diversion_trap.is_null()
+      && (diversion_trap_pos > vertical_position))
+    return diversion_trap.contents();
+  else
+    return "";
+}
+
 void macro_diversion::transparent_output(unsigned char c)
 {
   mac->append(c);
@@ -372,6 +381,16 @@ vunits top_level_diversion::distance_to_next_trap()
     return d - vertical_position;
 }
 
+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 "";
+  else
+    return next_trap->nm.contents();
+}
+
 void top_level_diversion::output(node *nd, int retain_size,
 				 vunits vs, vunits post_vs, hunits width)
 {
@@ -1059,6 +1078,16 @@ const char *diversion_name_reg::get_string()
   return curdiv->get_diversion_name();
 }
 
+class next_trap_name_reg : public reg {
+public:
+  const char *get_string();
+};
+
+const char *next_trap_name_reg::get_string()
+{
+  return curdiv->get_next_trap_name();
+}
+
 class page_number_reg : public general_reg {
 public:
   page_number_reg();
@@ -1194,6 +1223,7 @@ void init_div_requests()
   register_dictionary.define(".pe", new page_ejecting_reg);
   register_dictionary.define(".pn", new next_page_number_reg);
   register_dictionary.define(".t", new distance_to_next_trap_reg);
+  register_dictionary.define(".trap", new next_trap_name_reg);
   register_dictionary.define(".trunc",
       new constant_vunits_reg(&truncated_space));
   register_dictionary.define(".vpt",
diff --git a/src/roff/troff/div.h b/src/roff/troff/div.h
index 15c7807a5..6180caa0b 100644
--- a/src/roff/troff/div.h
+++ b/src/roff/troff/div.h
@@ -57,6 +57,7 @@ public:
   vunits get_vertical_position() { return vertical_position; }
   vunits get_high_water_mark() { return high_water_mark; }
   virtual vunits distance_to_next_trap() = 0;
+  virtual const char * get_next_trap_name() = 0;
   void need(vunits);
   const char *get_diversion_name() { return nm.contents(); }
   virtual void set_diversion_trap(symbol, vunits) = 0;
@@ -84,6 +85,7 @@ public:
   void vjustify(symbol);
 #endif /* COLUMN */
   vunits distance_to_next_trap();
+  const char *get_next_trap_name();
   void set_diversion_trap(symbol, vunits);
   void clear_diversion_trap();
   void copy_file(const char *filename);
@@ -125,6 +127,7 @@ public:
   hunits get_page_offset() { return page_offset; }
   vunits get_page_length() { return page_length; }
   vunits distance_to_next_trap();
+  const char *get_next_trap_name();
   void add_trap(symbol nm, vunits pos);
   void change_trap(symbol nm, vunits pos);
   void remove_trap(symbol);
