Follow-up Comment #23, bug #67509 (group groff): I think these are all the ChangeLog entries material to resolution of this ticket.
(The most recent reflects an update in my working copy.) 2025-09-18 G. Branden Robinson <[email protected]> * src/roff/troff/node.cpp: Refactor. (dbreak_node::asciify): Replace call of `asciify_reverse_node_list()` with a more idiomatic recursive call of the `none` contained node's `asciify()` member function. (asciify_reverse_node_list): Drop function with no remaining call sites. Fixes <https://savannah.gnu.org/bugs/?67509> (considered in combination with many other recent asciification changes). 2025-09-18 G. Branden Robinson <[email protected]> * tmac/fallbacks.tmac: Define different fallbacks for accented non-Latin-1 Latin characters, using one ordering for nroff-mode devices and another for troff-mode devices. We assume that the former can't constructively overstrike and the latter can. On troff-mode devices, it can make sense to use the `asciify` request to serialize special characters in device extension commands, and in that case we want to write the base glyph before any combining ones. On non-constructively overstriking devices, the last character written at the drawing position "wins"; we want that to be the base glyph. Fixes <https://savannah.gnu.org/bugs/?66653>. Thanks to Deri James for pushing device extension commands to the limit, exposing this defect. 2025-09-18 G. Branden Robinson <[email protected]> [troff]: Regression-test Savannah #66653. * src/roff/groff/tests/asciify-composite-nodes-correctly.sh: Do it. * src/roff/groff/groff.am (groff_TESTS): Run test. 2025-09-17 G. Branden Robinson <[email protected]> [troff]: Refactor `node` class hierarchy. Make `node` an abstract class. The `asciify` feature had several bugs, and I think it's because the class design permitted derived classes to reuse the `node` base class's `asciify` member function, which robotically appended the `this` object to the macro pointed to in the function argument. In most cases, the correct "asciification" of a node is to do nothing, discarding it. Rather than making the member function empty in the base class, I prefer to make the base class abstract to force conscious consideration of how objects of each derived class should be "asciified". * src/roff/troff/node.h: Mark `add_char()`, `asciify()`, and `add_italic_correction()` member functions as `virtual`. Further mark `asciify` as _pure_ virtual (notation: "= 0"). * src/roff/troff/node.cpp (node::asciify): Delete. 2025-09-17 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by diverted `trf` requests. 2025-09-17 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class zero_width_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (zero_width_node::asciify): New member "asciifies" each contained node unless output is suppressed. 2025-09-15 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\Z` and `\z` drawing position reset escape sequences. 2025-09-17 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class suppress_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp: New file-scoped global variable `is_output_supressed` tracks whether output is suppressed with the `\O` escape sequence. (suppress_node::asciify): New member function assigns to `is_output_supressed` per the escape sequence argument {indirectly, via the unhelpfully named `is_on` tri-state member variable.} (node::asciify, glyph_node::asciify, kern_pair_node::asciify) (dbreak_node::asciify, ligature_node::asciify) (break_char_node::asciify, italic_corrected_node::asciify) (left_italic_corrected_node::asciify) (space_char_hmotion_node::asciify, word_space_node::asciify) (unbreakable_space_node::asciify, composite_node::asciify): Texify node contents only if output is not suppressed. 2025-09-15 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class overstrike_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (overstrike_node::asciify): New member function simply does nothing. 2025-09-15 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\o` overstriking escape sequences. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class vline_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (vline_node::asciify): New member function simply does nothing. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\L` vertical rule escape sequences. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class hline_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (hline_node::asciify): New member function simply does nothing. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\l` horizontal rule escape sequences. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class extra_size_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (extra_size_node::asciify): New member function simply does nothing. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\x` extra leading escape sequences. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class draw_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (draw_node::asciify): New member function simply does nothing. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\D` drawing escape sequences. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/troff/node.h (class diverted_space_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (diverted_space_node::asciify): New member function simply does nothing. 2025-09-14 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by diverted `sp` requests. 2025-09-17 G. Branden Robinson <[email protected]> * src/roff/troff/node.cpp (glyph_node::asciify) (kern_pair_node::asciify, dbreak_node::asciify) (asciify_reverse_node_list, ligature_node::asciify) (break_char_node::asciify, italic_corrected_node::asciify) (left_italic_corrected_node::asciify) (space_char_hmotion_node::asciify, space_node::asciify) (word_space_node::asciify, unbreakable_space_node::asciify) (line_start_node::asciify, vertical_size_node::asciify) (dummy_node::asciify, transparent_dummy_node::asciify) (tag_node::asciify, device_extension_node::asciify) (vmotion_node::asciify, bracket_node::asciify) (hyphen_inhibitor_node::asciify, composite_node::asciify): Stop deleting the `this` object. I added many of these deletions recently, but several {`glyph_node`, `kern_pair_node`, `dbreak_node`, `ligature_node`, `break_char_node`, `italic_corrected_node`, `left_italic_corrected_node`, `hmotion_node`, `space_char_hmotion_node`, `space_node`, `word_space_node`, `unbreakable_space_node`, `line_start_node`, `vertical_size_node`, and `composite_node`} already were in groff 1.23.0, and for untold years before--sometimes only conditionally depending on the node contents. Why stop deleting them? Because the node list is actually a list of (potential) trees; some nodes can contain further nodes, and so on recursively. It's difficult, and there is no need, to mark the root of each tree in the list so that we can return to it later to delete it; instead what we can do is have the `asciify` request walk the list again and perform a delete operation, which due to the class hierarchy design will automatically be a complete, recursive operation. There is no reason _not_ to do this because the whole point of `asciify` is to convert nodes back into some form of text; the idiomatic application (and only one, as seen in "om.tmac" and "e.tmac") is to convert strings or diversions into PDF bookmarks or HTML URLs that are embedded in a document as metadata or markup, not as formatted text. * src/roff/troff/input.cpp (asciify_macro): When asciifying a node in a macro/string/diversion, copy the node first, asciify the copy (which in many cases produces nothing), and delete the original. This should be less wasteful of memory, as there's no need to carry around node data that is unrepresentable as text in an irreversibly transformed macro/string/diversion. * src/roff/troff/node.cpp (dbreak_node::asciify) (break_char_node::asciify, italic_corrected_node::asciify) (left_italic_corrected_node::asciify): Revert (from commit d445aee94e, 10 September) formerly dead stores nulling out pointers to contained nodes. Since the parent object is no longer deleted, these stores are no longer dead, and I expect the nullification to matter when asciifying node lists that contain `suppress_node`s. 2025-09-18 G. Branden Robinson <[email protected]> * src/roff/groff/groff.am (EXTRA_DIST): Ship "throughput-file" test artifact in distribution archive. Continues commit 09060903cf, 3 March. 2025-09-11 G. Branden Robinson <[email protected]> [troff]: "Asciify" bracket nodes as nothing. (Bracket nodes are produced by the `\b` bracket-building escape sequence.) * src/roff/troff/node.h (class bracket_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (bracket_node::asciify): New member function simply deletes `this`. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\b` (bracket-building escape sequence). 2025-09-11 G. Branden Robinson <[email protected]> [troff]: Refactor. Explicitly "asciify" hyphen inhibitor nodes as nothing. These correspond to the `\%` escape sequence at the beginning of a word. * src/roff/troff/input.cpp (class hyphen_inhibitor_node): Declare `asciify` member function, thus overriding base class. (hyphen_inhibitor_node::asciify): Delete the `this` object and return. 2025-09-11 G. Branden Robinson <[email protected]> [troff]: Refactor. Explicitly "asciify" non-interpreted `char` nodes as nothing. These correspond to the `\a` and `\t` escape sequences. * src/roff/troff/input.cpp (class non_interpreted_char_node): Declare `asciify` member function, thus overriding base class. (non_interpreted_char_node::asciify): Delete the `this` object and return. 2025-09-11 G. Branden Robinson <[email protected]> [troff]: Refactor. Explicitly "asciify" non-interpreted nodes as nothing. These correspond to what is bracketed by `\?` escape sequences. * src/roff/troff/input.cpp (class non_interpreted_node): Declare `asciify` member function, thus overriding base class. (non_interpreted_node::asciify): Delete the `this` object and return. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\%` (hyphenation control escape sequence) when it leads a word. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of nodes produced by `\?` (uninterpreted character sequence escape sequence). Check both diverted and undiverted scenarios. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test cases for transformation of nodes produced by leaders and tabs. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of node produced by `\a` (uninterpreted leader) escape sequence. 2025-09-11 G. Branden Robinson <[email protected]> [troff]: "Asciify" token nodes as nothing. Token nodes are used only internally to manipulate the input stream; they don't directly correspond to any sort of document feature. * src/roff/troff/node.h (class token_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (token_node::asciify): Plant land mine with `assert()`. Delete the `this` object and return. 2025-09-11 G. Branden Robinson <[email protected]> [troff]: "Asciify" (plain) space nodes as nothing. (Plain space nodes--contrast word space nodes and others--are produced by the formatter with a width of zero (`H0`) in nearly all cases. The exception is when a space node is copied, in which case the destination space node gets whatever width its source had.) * src/roff/troff/node.cpp (space_node::asciify): Simplify; member function now simply deletes `this`. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of node produced by `\:` (hyphenless breakpoint) escape sequence. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/troff/node.cpp (unbreakable_space_node::asciify): Asciify an unbreakable space node (the kind produced by the `\~` escape sequence) as an ordinary space instead of an `ESCAPE_TILDE` token. The latter is not necessary for the `asciify` request's revised charter (and only known application to date) of making diversion contents fit for use as device extension command arguments. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of node produced by `\~` (unbreakable space) escape sequence. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/troff/node.cpp (space_char_hmotion_node::asciify): Asciify a space character horizontal motion node (the kind produced by the `\ ` unadjustable space escape sequence) as an ordinary space instead of an `ESCAPE_SPACE` token. The latter is not necessary for the `asciify` request's revised charter {and only known application to date} of making diversion contents fit for use as device extension command arguments. 2025-09-11 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test case for transformation of node produced by `\ ` (unadjustable space) escape sequence. 2025-09-09 G. Branden Robinson <[email protected]> * src/roff/troff/node.cpp (hmotion_node::asciify): Stop asciifying a horizontal motion node that was a tab character as a tab; instead delete it like any other horizontal motion. We have no semantics for tab characters in, for example, the parameters of device extension commands. Ensure that the node object is deleted on every path through the function (now trivial). * src/roff/groff/tests/asciify-request-works.sh: Add test cases for transformation of nodes produced by `\t` and `\h` escape sequences. 2025-09-09 G. Branden Robinson <[email protected]> * src/roff/troff/node.cpp: Add more `assert()`ions and validity checks to node classes' `asciify()` member functions, eliminate unnecessary statements, and more consistently delete node objects after `asciify`-ing them. (The `asciify` request modifies a macro/string/diversion--usually a diversion--in place.) (kern_pair_node::asciify, ligature_node::asciify): `assert()` that each of the contained nodes is not a null pointer and do not recursively `asciify()` them if they are. (asciify_reverse_node_list): `assert()` that macro and node pointer arguments are not null, and return early if they are. (dbreak_node::asciify): `assert()` that macro pointer argument is not null, and do not call `asciify_reverse_node_list()` on it if it is. Also drop dead store (pointless assignment) to member variable just before deleting the object. (break_char_node::asciify, italic_corrected_node::asciify) (left_italic_corrected_node::asciify): `assert()` that macro pointer argument is not null, and do not recursively `asciify()` it if it is. Also drop dead store (pointless assignment) to member variable just before deleting the object. 2025-09-07 G. Branden Robinson <[email protected]> * src/roff/groff/tests/asciify-request-works.sh: Add test cases for null transformation (removal) of `\&`, `\(`, `\c`, `.tag`, `\X`, and `\v`. 2025-09-01 G. Branden Robinson <[email protected]> [troff]: "Asciify" vertical motion nodes as nothing, suppressing non-actionable diagnostic message. (Vertical motion nodes are produced by the `\v` escape sequence and other formatter facilities.) * src/roff/troff/node.h (class vmotion_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (vmotion_node::asciify): New member function does "nothing" in the same way that those of `line_start_node` and `vertical_size_node`s do: delete the `this` object and return. 2025-08-31 G. Branden Robinson <[email protected]> [troff]: "Asciify" device extension nodes as nothing, suppressing non-actionable diagnostic message. (Device extension nodes are produced by the `device` and `devicem` requests, and the `\X` and `\Y` escape sequences.) * src/roff/troff/node.h (class device_extension_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (device_extension_node::asciify): New member function does "nothing" in the same way that those of `line_start_node` and `vertical_size_node`s do: delete the `this` object and return. 2025-08-31 G. Branden Robinson <[email protected]> [troff]: "Asciify" tag nodes as nothing, suppressing non-actionable diagnostic message. (Tag nodes are produced by the undocumented `tag` request.) * src/roff/troff/node.h (class tag_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (tag_node::asciify): New member function does "nothing" in the same way that those of `line_start_node` and `vertical_size_node`s do: delete the `this` object and return. 2025-08-31 G. Branden Robinson <[email protected]> [troff]: "Asciify" transparent dummy nodes as nothing, suppressing non-actionable diagnostic message. (The output line continuation escape sequence produces a transparent dummy node when formatted.) * src/roff/troff/node.h (class transparent_dummy_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (transparent_dummy_node::asciify): New member function does "nothing" in the same way that those of `line_start_node` and `vertical_size_node`s do: delete the `this` object and return. 2025-08-31 G. Branden Robinson <[email protected]> [troff]: "Asciify" dummy nodes as nothing, suppressing non-actionable diagnostic message. (The dummy character escape sequence produces a dummy node when formatted.) * src/roff/troff/node.h (class dummy_node): Declare `asciify` member function, thus overriding base class. * src/roff/troff/node.cpp (dummy_node::asciify): New member function does "nothing" in the same way that those of `line_start_node` and `vertical_size_node`s do: delete the `this` object and return. Thanks to Deri James for the discussion and proof of concept. 2025-09-04 G. Branden Robinson <[email protected]> [groff]: Unit-test `asciify` request. * src/roff/groff/tests/asciify-request-works.sh: Test undiversion of ordinary character and some special character types. * src/roff/groff/groff.am (groff_TESTS): Run test. _______________________________________________________ Reply to this item at: <https://savannah.gnu.org/bugs/?67509> _______________________________________________ Message sent via Savannah https://savannah.gnu.org/
signature.asc
Description: PGP signature
