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