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/

Attachment: signature.asc
Description: PGP signature

Reply via email to