gbranden pushed a commit to branch master
in repository groff.
commit d50be3efd3e5b4d1e139c420a75be6686a318427
Author: G. Branden Robinson <[email protected]>
AuthorDate: Mon Sep 2 00:43:47 2024 -0500
[troff]: Fix Savannah #61100.
Prevent nested use of `do` requests from causing an assertion failure.
* src/roff/troff/input.cpp: Improve handling of nested selections of
compatibility mode. Use a global STL stack of `bool`,
`want_att_compat_stack`, to manage them. Drop global `int`
`do_old_want_att_compat` with its less-useful-than-hoped "-1" value to
signal an outermost "scope".
(do_request): Drop dubious `assert()`ion. Push the value of
`want_att_compat` onto the stack before invoking/calling the named
request/macro, and pop it afterwards.
(class enclosing_want_att_compat_reg): Add new class to handle dynamic
construction of `.cp` register value. I modeled the approach on an
idiom heavily used in "env.cpp", where read-only registers are not
modeled as accessors of C++ globals, because they interpolate
per-environment state.
(enclosing_want_att_compat_reg::get_string): Member function of new
class constructs `.cp` register value by peeking at the top of
`want_att_compat_stack`.
(init_input_requests): Wire up `.cp` register to an object of
`enclosing_want_att_compat_reg` class instead of now-dead
`do_old_want_att_compat` global.
Fixes <https://savannah.gnu.org/bugs/?66110>. Problem introduced by me
when introducing `.cp` register in commit 6a37bb5f00, 17 April 2020.
Thanks to Bjarni Ingi Gislason for suggesting the direction a minimal
reproducer could take.
---
ChangeLog | 30 +++++++++++++++++++++++++++
src/roff/groff/tests/dot-cp-register-works.sh | 4 ++--
src/roff/troff/input.cpp | 24 +++++++++++++++------
3 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index fd3ceb1e2..91cef8c25 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2024-09-02 G. Branden Robinson <[email protected]>
+
+ [troff]: Prevent nested use of `do` requests from causing an
+ assertion failure.
+
+ * src/roff/troff/input.cpp: Improve handling of nested
+ selections of compatibility mode. Use a global STL stack of
+ `bool`, `want_att_compat_stack`, to manage them. Drop global
+ `int` `do_old_want_att_compat` with its less-useful-than-hoped
+ "-1" value to signal an outermost "scope".
+ (do_request): Drop dubious `assert()`ion. Push the value of
+ `want_att_compat` onto the stack before invoking/calling the
+ named request/macro, and pop it afterwards.
+ (class enclosing_want_att_compat_reg): Add new class to handle
+ dynamic construction of `.cp` register value. I modeled the
+ approach on an idiom heavily used in "env.cpp", where read-only
+ registers are not modeled as accessors of C++ globals, because
+ they interpolate per-environment state.
+ (enclosing_want_att_compat_reg::get_string): Member function of
+ new class constructs `.cp` register value by peeking at the top
+ of `want_att_compat_stack`.
+ (init_input_requests): Wire up `.cp` register to an object of
+ `enclosing_want_att_compat_reg` class instead of now-dead
+ `do_old_want_att_compat` global.
+
+ Fixes <https://savannah.gnu.org/bugs/?66110>. Problem
+ introduced by me when introducing `.cp` register in commit
+ 6a37bb5f00, 17 April 2020. Thanks to Bjarni Ingi Gislason for
+ suggesting the direction a minimal reproducer could take.
+
2024-09-01 G. Branden Robinson <[email protected]>
Regression-test Savannah #61100.
diff --git a/src/roff/groff/tests/dot-cp-register-works.sh
b/src/roff/groff/tests/dot-cp-register-works.sh
index 963ab8d31..6aa513d4b 100755
--- a/src/roff/groff/tests/dot-cp-register-works.sh
+++ b/src/roff/groff/tests/dot-cp-register-works.sh
@@ -57,14 +57,14 @@ echo "$output"
echo "checking value of '.cp' when not started in compatibility mode" \
>&2
-echo "$output" | grep -Fqx "A 0 B 0 C 1 D 0 E 1 F -1" || wail
+echo "$output" | grep -Fqx "A 0 B 0 C 1 D 0 E 1 F 0" || wail
output=$(printf "%s" "$input" | "$groff" -C -T ascii)
echo "$output"
echo "checking value of '.cp' when started in compatibility mode" \
>&2
-echo "$output" | grep -Fqx "A 1 B 1 C 1 D 0 E 1 F -1" || wail
+echo "$output" | grep -Fqx "A 1 B 1 C 1 D 0 E 1 F 0" || wail
test -z "$fail"
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 1e0447f3e..6ef735fa1 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -106,7 +106,6 @@ static symbol end_of_input_macro_name;
static symbol blank_line_macro_name;
static symbol leading_spaces_macro_name;
static bool want_att_compat = false;
-static int do_old_want_att_compat = -1; // for .do request
bool want_abstract_output = false;
bool want_output_suppressed = false;
bool is_writing_html = false;
@@ -2899,6 +2898,8 @@ static void trapping_blank_line()
blank_line();
}
+std::stack<bool> want_att_compat_stack;
+
void do_request()
{
if (!has_arg()) {
@@ -2907,16 +2908,16 @@ void do_request()
skip_line();
return;
}
- assert(do_old_want_att_compat == -1);
- do_old_want_att_compat = want_att_compat;
+ want_att_compat_stack.push(want_att_compat);
want_att_compat = false;
symbol nm = get_name();
if (nm.is_null())
skip_line();
else
interpolate_macro(nm, true /* don't want next token */);
- want_att_compat = do_old_want_att_compat;
- do_old_want_att_compat = -1;
+ assert(!want_att_compat_stack.empty());
+ want_att_compat = want_att_compat_stack.top();
+ want_att_compat_stack.pop();
request_or_macro *p = lookup_request(nm);
macro *m = p->to_macro();
if (m)
@@ -8058,6 +8059,17 @@ const char *break_flag_reg::get_string()
return i_to_a(input_stack::get_break_flag());
}
+class enclosing_want_att_compat_reg : public reg {
+public:
+ const char *get_string();
+};
+
+const char *enclosing_want_att_compat_reg::get_string()
+{
+ return i_to_a(want_att_compat_stack.empty() ? 0
+ : want_att_compat_stack.top());
+}
+
class readonly_text_register : public reg {
const char *s;
public:
@@ -9007,7 +9019,7 @@ void init_input_requests()
register_dictionary.define(".$", new nargs_reg);
register_dictionary.define(".br", new break_flag_reg);
register_dictionary.define(".C", new
readonly_boolean_register(&want_att_compat));
- register_dictionary.define(".cp", new
readonly_register(&do_old_want_att_compat));
+ register_dictionary.define(".cp", new enclosing_want_att_compat_reg);
register_dictionary.define(".O", new variable_reg(&suppression_level));
register_dictionary.define(".c", new lineno_reg);
register_dictionary.define(".color", new
readonly_boolean_register(&want_color_output));
_______________________________________________
Groff-commit mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/groff-commit