]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: fix handling of raw ANSI escape sequences printed from Python
authorAndrew Burgess <aburgess@redhat.com>
Thu, 15 Jan 2026 10:28:45 +0000 (10:28 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Thu, 15 Jan 2026 17:44:35 +0000 (17:44 +0000)
Bug PR gdb/33748 reports a regression with print styled output from
Python when using ANSI escape sequences to apply the styling.  This
regression was introduced by commit:

  commit 3825c972a636852600b47c242826313f4b9963b8
  Date:   Wed Jun 18 15:02:29 2025 +0100

      gdb: allow gdb.Color to work correctly with pagination

Prior to this commit GDB would always forward any ANSI escape
sequences directly to the output stream, but this meant that GDB
didn't know which style was currently in effect.

The above commit changed GDB so that we would parse the ANSI escape
sequence, and then apply it to the output stream, this allowed GDB to
track which style was in use, which in turn meant that GDB could
correctly suspend the style and reapply it when the pager was
activated.

The problem though is that ANSI escape sequences can be built up in
parts.  For example, a user can emit the sequence to change the
foreground blue, and then later emit the sequence to change the text
to bold.  The result is output that is both blue and bold.

In the above commit, when parsing an incoming ANSI sequence, GDB was
always starting from the default styling state.  The result of parsing
the ANSI sequence was then being applied.  In our above example, this
meant that when parsing the sequence for bold text we would "forget"
that the current style had a blue foreground color.

This can be easily fixed by starting from the current style, rather
than the default style.

This fix was suggested by Patryk Sondej who originally reported the
bug.

Co-Authored-By: Patryk Sondej <patryk.sondej@gmail.com>
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33748

Approved-By: Tom Tromey <tom@tromey.com>
gdb/testsuite/gdb.python/py-color-via-ansi-esc.exp [new file with mode: 0644]
gdb/utils.c

diff --git a/gdb/testsuite/gdb.python/py-color-via-ansi-esc.exp b/gdb/testsuite/gdb.python/py-color-via-ansi-esc.exp
new file mode 100644 (file)
index 0000000..9aac552
--- /dev/null
@@ -0,0 +1,70 @@
+# Copyright (C) 2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests styled output from
+# Python via raw ANSI escape sequences.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+require {!is_remote host}
+
+# Start with a fresh GDB, but enable color support.
+with_ansi_styling_terminal {
+    clean_restart
+}
+
+# Create two variables in the parent scope called ${NAME} and
+# ${NAME}_re.  The variable ${NAME} will be the string for the ANSI
+# escape sequence containing VALUE, and ${NAME}_re will be the regular
+# expression to match that same sequence.
+proc setup_ansi { name value } {
+    upvar 1 $name var
+    upvar 1 ${name}_re var_re
+
+    set var "\\033\[${value}m"
+    set var_re "\033\\\[${value}m"
+}
+
+# Different colors.
+setup_ansi red 31
+setup_ansi green 32
+setup_ansi blue 34
+
+# Different intensities.
+setup_ansi normal 22
+setup_ansi bold 1
+setup_ansi dim 2
+
+# Italic.
+setup_ansi italic 3
+setup_ansi no_italic 23
+
+# Underline.
+setup_ansi underline 4
+setup_ansi no_underline 24
+
+# Restore all settings to default.
+setup_ansi default 0
+
+gdb_test_no_output "python flag=\"OUTPUT=\""
+
+gdb_test "python print('%s${blue}blue ${bold}blue+bold ${green}green+bold ${normal}green ${red}red${default}' % (flag))" \
+    "OUTPUT=${blue_re}blue ${bold_re}blue\\+bold ${green_re}green\\+bold ${normal_re}green ${red_re}red${default_re}" \
+    "foreground colors and bold"
+
+gdb_test "python print('%snormal ${italic}italic ${underline}italic+underline ${no_underline}italic ${no_italic}normal' % (flag))" \
+    "OUTPUT=normal ${italic_re}italic ${underline_re}italic\\+underline ${no_underline_re}italic ${no_italic_re}normal" \
+    "italic and underline"
index 876aad1c4488f6d8bddf829ed378ef2f225e6b25..8c5f34b96875b54d6db0279a52bc939c1a10f1e2 100644 (file)
@@ -1703,8 +1703,11 @@ pager_file::puts (const char *linebuffer)
              /* We don't consider escape sequences as characters, so we
                 don't increment chars_printed here.  */
 
+             /* This style sequence might not set every style attribute,
+                so start with the currently applied style, and update
+                that.  */
              size_t style_len;
-             ui_file_style style;
+             ui_file_style style = m_applied_style;
              if (style.parse (linebuffer, &style_len)
                  && style_len <= skip_bytes)
                {