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++; }