From e322b714dc88c2c8ce6b81e529e382ba4c81df1b Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Tue, 29 Apr 2025 15:35:53 +0100 Subject: [PATCH] libutil: amend OSC 8 escape stripping for xterm-style separator Before the change `nix` was stripping warning flags reported by `gcc-14` too eagerly: $ nix build -f. texinfo4 error: builder for '/nix/store/i9948l91s3df44ip5jlpp6imbrcs646x-texinfo-4.13a.drv' failed with exit code 2; last 25 log lines: > 1495 | info_tag (mbi_iterator_t iter, int handle, size_t *plen) > | ~~~~~~~~^~~~ > window.c:1887:39: error: passing argument 4 of 'printed_representation' from incompatible pointer type [] > 1887 | &replen); > | ^~~~~~~ > | | > | int * After the change the compiler flag remains: $ ~/patched.nix build -f. texinfo4 error: builder for '/nix/store/i9948l91s3df44ip5jlpp6imbrcs646x-texinfo-4.13a.drv' failed with exit code 2; last 25 log lines: > 1495 | info_tag (mbi_iterator_t iter, int handle, size_t *plen) > | ~~~~~~~~^~~~ > window.c:1887:39: error: passing argument 4 of 'printed_representation' from incompatible pointer type [-Wincompatible-pointer-types] > 1887 | &replen); > | ^~~~~~~ > | | > | int * Note the difference in flag rendering around the warning. https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda has a good sumamry of why it happens. Befomre the change `nix` was handling just one form or URL separator: $ printf '\e]8;;http://example.com\e\\This is a link\e]8;;\e\\\n' Now it also handled another for (used by gcc-14`): printf '\e]8;;http://example.com\aThis is a link\e]8;;\a\n' While at it fixed accumulation of trailing escape `\e\\` symbol. --- src/libutil-tests/terminal.cc | 8 ++++++++ src/libutil/terminal.cc | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libutil-tests/terminal.cc b/src/libutil-tests/terminal.cc index 329c1a186..198b20951 100644 --- a/src/libutil-tests/terminal.cc +++ b/src/libutil-tests/terminal.cc @@ -66,4 +66,12 @@ TEST(filterANSIEscapes, osc8) ASSERT_EQ(filterANSIEscapes("\e]8;;http://example.com\e\\This is a link\e]8;;\e\\"), "This is a link"); } +TEST(filterANSIEscapes, osc8_bell_as_sep) +{ + // gcc-14 uses \a as a separator, xterm style: + // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda + ASSERT_EQ(filterANSIEscapes("\e]8;;http://example.com\aThis is a link\e]8;;\a"), "This is a link"); + ASSERT_EQ(filterANSIEscapes("\e]8;;http://example.com\a\\This is a link\e]8;;\a"), "\\This is a link"); +} + } // namespace nix diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index fa0f7e871..63473d1a9 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -95,10 +95,19 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w } else if (i != s.end() && *i == ']') { // OSC e += *i++; - // eat ESC - while (i != s.end() && *i != '\e') e += *i++; - // eat backslash - if (i != s.end() && *i == '\\') e += last = *i++; + // https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda defines + // two forms of a URI separator: + // 1. ESC '\' (standard) + // 2. BEL ('\a') (xterm-style, used by gcc) + + // eat ESC or BEL + while (i != s.end() && *i != '\e' && *i != '\a') e += *i++; + if (i != s.end()) { + char v = *i; + e += *i++; + // eat backslash after ESC + if (i != s.end() && v == '\e' && *i == '\\') e += last = *i++; + } } else { if (i != s.end() && *i >= 0x40 && *i <= 0x5f) e += *i++; }