gbranden pushed a commit to branch master
in repository groff.

commit a245e2f7d7c7ec16a2331f182508129899604f48
Author: G. Branden Robinson <[email protected]>
AuthorDate: Tue Apr 28 23:53:23 2026 -0500

    src/roff/troff/reg.cpp: Slightly refactor.
    
    * src/roff/troff/reg.cpp: Tweak and make consistent the buffer-
      scribbling technique when formatting register properties.  Never hit
      the stack for this purpose; use only one file-global static buffer,
      `scratchpad`.  Shrink size of said buffer to only what we require:
      `INT_DIGITS` decimal places plus a leading sign.  As long as
      `INT_DIGITS` remains (generously) sized for 64-bit `int`s--though I'm
      not familiar with a port of groff to an I(LP)64 host--doing so buys
      back over 100 bytes of BSS.  If this technique seems old-fashioned, it
      is--but GNU troff is single-threaded and none of the functions using
      this buffer participate in cycles in the program's call graph (i.e.,
      they're not reëntrant).
    
      (number_value_to_ascii, number_format_to_ascii, dump_register): Zero
      out the buffer with memset(3) before using it, for paranoia's sake.
    
      (number_format_to_ascii): Use the `scratchpad` buffer instead of a
      different, local `static` buffer of 24 bytes.
    
      (dump_register): Use `scratchpad` instead of automatic (stack) storage
      as buffer for autoincrement value.  Declare `n` variable closer to
      use.
---
 ChangeLog                | 23 +++++++++++++++++++
 src/roff/troff/input.cpp |  2 +-
 src/roff/troff/reg.cpp   | 59 +++++++++++++++++++++++++++++++-----------------
 3 files changed, 62 insertions(+), 22 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8539fbef3..c47e674f2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2026-04-27  G. Branden Robinson <[email protected]>
+
+       * src/roff/troff/reg.cpp: Slightly refactor.  Tweak and make
+       consistent the buffer-scribbling technique when formatting
+       register properties.  Never hit the stack for this purpose; use
+       only one file-global static buffer, `scratchpad`.  Shrink size
+       of said buffer to only what we require: `INT_DIGITS` decimal
+       places plus a leading sign.  As long as `INT_DIGITS` remains
+       {generously} sized for 64-bit `int`s--though I'm not familiar
+       with a port of groff to an I(LP)64 host--doing so buys back over
+       100 bytes of BSS.  If this technique seems old-fashioned, it
+       is--but GNU troff is single-threaded and none of the functions
+       using this buffer participate in cycles in the program's call
+       graph (i.e., they're not reëntrant).
+       (number_value_to_ascii, number_format_to_ascii, dump_register):
+       Zero out the buffer with memset(3) before using it, for
+       paranoia's sake.
+       (number_format_to_ascii): Use the `scratchpad` buffer instead of
+       a different, local `static` buffer of 24 bytes.
+       (dump_register): Use `scratchpad` instead of automatic (stack)
+       storage as buffer for autoincrement value.  Declare `n` variable
+       closer to use.
+
 2026-04-27  G. Branden Robinson <[email protected]>
 
        * src/roff/troff/input.cpp: Trivially refactor.
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 5a590715d..3e4d99328 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -34,7 +34,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>. */
                   // ungetc()
 #include <stdlib.h> // atoi(), exit(), EXIT_FAILURE, EXIT_SUCCESS,
                    // free(), getenv(), setenv(), strtol(), system()
-#include <string.h> // strcpy(), strdup(), strerror()
+#include <string.h> // memset(), strcpy(), strdup(), strerror()
 
 // GNU extensions to C standard library
 #include <getopt.h> // getopt_long()
diff --git a/src/roff/troff/reg.cpp b/src/roff/troff/reg.cpp
index abb3df573..14eb518be 100644
--- a/src/roff/troff/reg.cpp
+++ b/src/roff/troff/reg.cpp
@@ -24,6 +24,7 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>. */
 
 #include <assert.h>
 #include <stdio.h> // prerequisite of searchpath.h
+#include <string.h> // memset()
 
 // libgroff
 #include "errarg.h" // prerequisite of troff.h
@@ -43,6 +44,21 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>. */
 
 object_dictionary register_dictionary(101);
 
+// Scribble into this any time we need to format a *roff integer as a
+// C string and will use the result immediately.  Think of *roff
+// register values, decimal number formats, and autoincrement amounts.
+//
+// Register values in decimal (and the corresponding number format)
+// occupy the most possible space in this buffer.  Non-decimal numbers
+// and their formats are always smaller.  Even AT&T troff's extended
+// Roman numerals can't express much more than a signed 16-bit integer,
+// and base-26 alphabetic formats are a more compact representation than
+// decimal.
+//
+// C++11: constexpr
+static const size_t scratchpad_size = INT_DIGITS + 1 /* leading sign */;
+static char scratchpad[scratchpad_size];
+
 bool reg::get_value(units * /*d*/)
 {
   return false;
@@ -114,20 +130,21 @@ static char lowercase_array[] = {
 static const char *number_value_to_ascii(int value, char format,
                                         int width)
 {
-  static char buf[128];                // must be at least 21
+  (void) memset(scratchpad, 0, (scratchpad_size * sizeof(char)));
   switch (format) {
   case '1':
     if (width <= 0)
       return i_to_a(value);
-    else if (width > int((sizeof buf) - 2))
-      sprintf(buf, "%.*d", int((sizeof buf) - 2), int(value));
+    else if (width > int((sizeof scratchpad) - 2))
+      sprintf(scratchpad, "%.*d", int((sizeof scratchpad) - 2),
+             int(value));
     else
-      sprintf(buf, "%.*d", width, int(value));
+      sprintf(scratchpad, "%.*d", width, int(value));
     break;
   case 'i':
   case 'I':
     {
-      char *p = buf;
+      char *p = scratchpad;
       bool is_value_out_of_roman_numeral_range = false;
       int n = int(value);
       // AT&T troff uses z and w to represent 10000 and 5000 in Roman
@@ -209,7 +226,7 @@ static const char *number_value_to_ascii(int value, char 
format,
   case 'A':
     {
       int n = value;
-      char *p = buf;
+      char *p = scratchpad;
       if (n == 0) {
        *p++ = '0';
        *p = '\0';
@@ -230,7 +247,7 @@ static const char *number_value_to_ascii(int value, char 
format,
                                 uppercase_array[d - 1];
        }
        *p-- = '\0';
-       char *q = buf[0] == '-' ? buf + 1 : buf;
+       char *q = scratchpad[0] == '-' ? scratchpad + 1 : scratchpad;
        while (q < p) {
          char temp = *q;
          *q = *p;
@@ -245,7 +262,7 @@ static const char *number_value_to_ascii(int value, char 
format,
     assert(0 == "unhandled case of register format");
     break;
   }
-  return buf;
+  return scratchpad;
 }
 
 const char *general_reg::get_string()
@@ -298,22 +315,22 @@ void general_reg::alter_format(char f, int w)
 
 static const char *number_format_to_ascii(char format, int width)
 {
-  static char buf[24];
+  (void) memset(scratchpad, 0, (scratchpad_size * sizeof(char)));
   if (format == '1') {
     if (width > 0) {
       int n = width;
-      if (n > int(sizeof buf) - 1)
-       n = int(sizeof buf) - 1;
-      sprintf(buf, "%.*d", n, 0);
-      return buf;
+      if (n > (int(sizeof scratchpad) - 1))
+       n = int(sizeof scratchpad) - 1;
+      sprintf(scratchpad, "%.*d", n, 0);
+      return scratchpad;
     }
     else
       return "0";
   }
   else {
-    buf[0] = format;
-    buf[1] = '\0';
-    return buf;
+    scratchpad[0] = format;
+    scratchpad[1] = '\0';
+    return scratchpad;
   }
 }
 
@@ -577,15 +594,15 @@ static void rename_register_request()
 
 static void dump_register(symbol *id, reg *r)
 {
-  int n;
-  const size_t sz = INT_DIGITS + 1 /* leading sign */;
-  char inc[sz];
   errprint("%1\t", id->contents());
+  int n;
   if (r->get_value(&n)) {
     errprint("%1", n);
     if (r->can_autoincrement()) {
-      (void) snprintf(inc, sz, "%+d", r->get_increment());
-      errprint("\t%1", inc);
+      (void) memset(scratchpad, 0, (scratchpad_size * sizeof(char)));
+      (void) snprintf(scratchpad, scratchpad_size, "%+d",
+                     r->get_increment());
+      errprint("\t%1", scratchpad);
     }
     if (r->has_format()) {
       const char *f = r->get_format();

_______________________________________________
groff-commit mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/groff-commit

Reply via email to