# Copyright 2018-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/>.

require {!is_remote host}

load_lib gdb-python.exp

# Test CLI output styling.

standard_testfile

# Compile the test executable.
if {[build_executable "failed to build" $testfile $srcfile {debug macros}]} {
    return
}

# The tests in this file are run multiple times with GDB's styles
# disabled one at a time.  This variable is the style that is
# currently disabled.
set currently_disabled_style ""

# A wrapper around the 'style' function found in gdb-utils.exp,
# filter out requests for the disabled style.
proc limited_style { str style } {
    global currently_disabled_style

    if { $style != $currently_disabled_style } {
	return [style $str $style]
    }

    return $str
}

# A wrapper around 'clean_restart' from gdb.exp, this performs the
# normal clean_restart, but then disables the currently disabled
# style.
proc clean_restart_and_disable { prefix args } {
    global currently_disabled_style

    with_test_prefix "$prefix" {
	clean_restart {*}$args

	if { $currently_disabled_style != "" } {
	    set st $currently_disabled_style
	    gdb_test_no_output "set style $st background none"
	    gdb_test_no_output "set style $st foreground none"
	    gdb_test_no_output "set style $st intensity normal"
	}
    }
}

# The core of this test script.  Run some tests of different aspects
# of GDB's styling.
#
# Within this proc always use LIMITED_STYLE instead of STYLE, and
# CLEAN_RESTART_AND_DISABLE instead of CLEAN_RESTART, this ensures
# that the test operates as expected as styles are disabled.
proc run_style_tests { } {
    global testfile srcfile hex binfile
    global currently_disabled_style decimal hex

    with_ansi_styling_terminal {
	setenv TERM xterm-256color
	setenv COLORTERM truecolor

	# Restart GDB with the correct TERM variable setting, this
	# means that GDB will enable styling.
	clean_restart_and_disable "restart 1" $::testfile

	set readnow [readnow]

	if {![runto_main]} {
	    return
	}

	# Check that the source highlighter has not stripped away the
	# leading newlines.
	set main_line [gdb_get_line_number "break here"]
	gdb_test "list $main_line,$main_line" "return.*some_called_function.*"

	gdb_test_no_output "set style enabled off"

	set argv ""
	gdb_test_multiple "frame" "frame without styling" {
	    -re -wrap "main \\(argc=.*, (argv=$hex.*)\\).*style\\.c:\[0-9\].*" {
		set argv $expect_out(1,string)
		pass $gdb_test_name
	    }
	}

	gdb_test_no_output "set style enabled on"

	set main_expr [limited_style main function]
	set base_file_expr [limited_style ".*style\\.c" file]
	set line_expr [limited_style $decimal line-number]
	set file_expr "$base_file_expr:$line_expr"
	set arg_expr [limited_style "arg." variable]

	# On some embedded targets that don't fully support argc/argv,
	# argv may print as "0x0" or as an address with a symbol name
	# following, so use a regexp general enough to match that and
	# do not make assumptions about how long the address string is.
	gdb_test "frame" \
	    [multi_line \
		 "#0\\s+$main_expr\\s+\\($arg_expr=$decimal,\\s+$arg_expr=$hex.*\\)\\s+at\\s+$file_expr" \
		 "$line_expr\\s+.*return.* break here .*"]
	gdb_test "info breakpoints" \
	    ".*[limited_style What title].*$main_expr at $file_expr.*"

	gdb_test_no_output "set style sources off"
	gdb_test "frame" \
	    "\r\n$line_expr\[^\033\]*break here.*" \
	    "frame without sources styling"
	gdb_test_no_output "set style sources on"

	gdb_test "break -q main" "file $base_file_expr.*"

	gdb_test "print &main" " = .* [limited_style $hex address] <$main_expr>"

	# Regression test for a bug where line-wrapping would occur at
	# the wrong spot with styling.  There were different bugs at
	# different widths, so try two.
	foreach width {20 30} {
	    set argv_len [string length $argv]
	    if { $argv_len == 0 } {
		continue
	    }

	    # There was also a bug where the styling could be wrong in
	    # the line listing; this is why the words from the source
	    # code are spelled out in the final result line of the
	    # test.
	    set re0_styled \
		[multi_line \
		     "#0\\s+$main_expr\\s+\\($arg_expr=$decimal,\\s+$arg_expr=$hex\\)" \
		     "\\s+at\\s+$file_expr" \
		     "$line_expr\\s+.*return.* break here .*"]
	    set re1_styled \
		[multi_line \
		     "#0\\s+$main_expr\\s+\\($arg_expr=$decimal,\\s+" \
		     "\\s+$arg_expr=$hex.*\\)" \
		     "\\s+at\\s+$file_expr" \
		     "$line_expr\\s+.*return.* break here .*"]
	    set re2_styled \
		[multi_line \
		     "#0\\s+$main_expr\\s+\\($arg_expr=.*" \
		     "\\s+$arg_expr=$hex.*\\)\\s+at\\s+$file_expr" \
		     "$line_expr\\s+.*return.* break here .*"]

	    # The length of the line containing argv containing:
	    # - 4 leading spaces
	    # - argv string
	    # - closing parenthesis
	    set line_len [expr {4 + $argv_len + 1}]

	    if { $argv == "argv=0x0" && $width >= 27 } {
		# Embedded target with no argv support.
		# #0  main (argc=0, argv=0x0)
		set re_styled $re0_styled
	    } elseif { $line_len > $width } {
		# At on the next line.
		set re_styled $re1_styled
	    } else {
		# At on the same line as argv.
		set re_styled $re2_styled
	    }

	    # Override width for the duration of the command, letting
	    # GDB reset back to 0.  Doing this in the loop rather than
	    # after, avoids trying to do "set width 30" while width is
	    # 20, which causes horizontal scrolling in the case that
	    # the fallback stub-termcap.c is used:
	    # ^M<et width 30^M
	    # (gdb) FAIL: gdb.base/style.exp: all styles enabled: set width 30
	    gdb_test "with width $width -- frame" $re_styled \
		"frame when width=$width"
	}

	set macro_line [gdb_get_line_number "\#define SOME_MACRO"]
	gdb_test "info macro SOME_MACRO" \
	    "Defined at $base_file_expr:$macro_line\r\n#define SOME_MACRO 23"

	gdb_test_no_output "set width 0"

	# If disassembler styling is being done by the Python pygments
	# module, then we can't be sure how the 'some_called_function'
	# symbol will be styled.  However, if pygments is not being
	# used then we can know how the symbol name will be styled.
	set main [limited_style main function]
	if { $::python_disassembly_styling } {
	    set func "some_called_function"
	} else {
	    set func [limited_style some_called_function function]
	}
	# Somewhere should see the call to the function.
	gdb_test "disassemble main" \
	    [concat "Dump of assembler code for function $main:.*" \
		 "[limited_style $hex address].*$func.*"]

	set ifield [limited_style int_field variable]
	set sfield [limited_style string_field variable]
	set efield [limited_style e_field variable]
	set evalue [limited_style VALUE_TWO variable]
	set lhs [limited_style "${::valnum_re}" variable]
	gdb_test "print struct_value" \
	    "$lhs = \{$ifield = 23,.*$sfield = .*,.*$efield = $evalue.*"

	set ffield [limited_style field variable]
	set cstart [string_to_regexp "/* XXX "]
	set cend [string_to_regexp " */"]
	set p1field [limited_style "$cstart.*$decimal.*-bit.*padding.*$cend" \
			 highlight]
	set p2field [limited_style "$cstart.*$decimal.*-byte.*padding.*$cend" \
			 highlight]

	gdb_test "ptype/o just_bitfield_value" \
	    [multi_line \
		 ".* type = struct just_bitfield {" \
		 ".* unsigned int $ffield : 3;" \
		 "$p1field" \
		 "$p2field" \
		 "" \
		 ".* total size.*: *$decimal *$cend" \
		 " *}.*"]

	set address_style_expr [limited_style ".*\".*address.*\".*style.*" address]
	set color "blue"
	if { $currently_disabled_style == "address" } {
	    set color "none"
	}
	gdb_test "show style address foreground" \
	    "The ${address_style_expr} foreground color is: ${color}" \
	    "style name and style word styled using its own style in show style"

	set aliases_expr [limited_style ".*aliases.*" command]
	set breakpoints_expr [limited_style ".*breakpoints.*" command]
	gdb_test "help" \
	    [multi_line \
		 "List of classes of commands:" \
		 "" \
		 "${aliases_expr} -- User-defined aliases of other commands\." \
		 "${breakpoints_expr} -- Making program stop at certain points\." \
		 ".*" \
		] \
	    "help classes of commands styled with command"

	set taas_expr  [limited_style ".*taas.*" command]
	set tfaas_expr  [limited_style ".*tfaas.*" command]
	set cut_for_thre_expr [limited_style "cut for 'thre" highlight]
	gdb_test "apropos -v cut for 'thre" \
	    [multi_line \
		 "" \
		 "${taas_expr}" \
		 "Apply a command to all .*" \
		 "Usage:.*" \
		 "short${cut_for_thre_expr}ad apply.*" \
		 "" \
		 "${tfaas_expr}" \
		 "Apply a command to all .*" \
		 "Usage:.*" \
		 "short${cut_for_thre_expr}ad apply.*" \
		]

	clean_restart_and_disable "restart 2"

	set quoted [string_to_regexp $binfile]
	set pass_re "Reading symbols from [limited_style $quoted file]\.\.\."
	if { $readnow } {
	    set pass_re \
		[multi_line \
		     $pass_re \
		     "Expanding full symbols from [limited_style $quoted file]\.\.\."]
	}
	gdb_test "file $binfile" \
	    $pass_re \
	    "filename is styled when loading symbol file"

	gdb_test "pwd" "Working directory [limited_style .*? file].*"

	gdb_test_no_output "set print repeat 3"
	gdb_test "print {0,0,0,0,0,0,0,0}" \
	    " = \\{0 [limited_style {<repeats.*8.*times>} metadata]\\}"

	gdb_test "show logging file" \
	    "The current logfile is \"[limited_style .*? file]\"\\..*"

	# Check warnings are styled by setting a rubbish data
	# directory.
	gdb_test "set data-directory Makefile" \
	    "warning: [limited_style .*? file] is not a directory\\..*"
	gdb_test "show data-directory" \
	    "GDB's data directory is \"[limited_style .*? file]\"\\..*"

	# Check that deprecation styles command names.
	gdb_test_no_output "maintenance deprecate p \"new_p\"" \
	    "maintenance deprecate p \"new_p\" /1/"
	gdb_test "p 5" \
	    "Warning: '[limited_style p command]', an alias for the command '[limited_style print command]', is deprecated.*Use '[limited_style new_p command]'.*" \
	    "p deprecated warning, with replacement"

	# Check that the version string is styled in the output of 'show
	# version', and that this styling can be disabled.
	set vers [style "GNU gdb.*" version]
	set url [limited_style "http:.*html" file]
	gdb_test "show version" "${vers}.*<$url>.*" \
	    "'show version' is styled"

	if { $currently_disabled_style != "version" } {
	    # Check that colors in styling can be set as integer and as RGB hex
	    # triplet.  Check that the version string is styled in the output of
	    # 'show version' according to the set colors.
	    gdb_test_no_output "set style version intensity normal"
	    gdb_test_no_output "set style version background 255"
	    gdb_test_no_output "set style version foreground #FED210"
	    gdb_test "show style version background" \
		"The \033\\\[38;2;254;210;16;48;5;255;22;23;24;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \
		"Version's 256-color background style"
	    gdb_test "show style version foreground" \
		"The \033\\\[38;2;254;210;16;48;5;255;22;23;24;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \
		"Version's TrueColor foreground style"
	}

	gdb_test_no_output "set host-charset UTF-8"
	# Chosen since it will print an error.
	gdb_test "maint translate-address" \
	    "❌️ requires argument.*" \
	    "emoji output"

	gdb_test_no_output "set style error-prefix abcd:" \
	    "set the error prefix"
	gdb_test "maint translate-address" \
	    "abcd:requires argument.*" \
	    "error prefix"

	# Check styling in the 'skip' related commands.
	gdb_test "skip -file xxxx.c" \
	    "File [limited_style xxxx\\.c file] will be skipped when stepping\\."
	gdb_test "skip -gfile zz*.c" \
	    "File\\(s\\) [limited_style zz\\*\\.c file] will be skipped when stepping\\."
	gdb_test "skip -function xxxx" \
	    "Function [limited_style xxxx function] will be skipped when stepping\\."
	gdb_test "skip -rfunction zzz.*" \
	    "Function\\(s\\) [limited_style zzz\\.\\* function] will be skipped when stepping\\."
	gdb_test "skip function qqq" \
	    "Function [limited_style qqq function] will be skipped when stepping\\."
	gdb_test "skip file blah.c" \
	    "File [limited_style blah\\.c file] will be skipped when stepping\\."
	gdb_test "skip ttt" \
	    "Function [limited_style ttt function] will be skipped when stepping\\."
    }
}

# Check that disassembler styling can be disabled.  The function that
# we are disassembling has some minimal styling applied even if the
# Python pygments module is not available, so, when we disable
# disassembler styling, we should always see a change in output.
proc test_disable_disassembler_styling { } {
    save_vars { env(TERM) env(NO_COLOR) } {
	# We need an ANSI-capable terminal to get the output.
	setenv TERM ansi
	setenv NO_COLOR ""

	# Restart GDB with the correct TERM variable setting, this
	# means that GDB will enable styling.
	clean_restart_and_disable "restart 3" $::testfile

	set styled_hex [limited_style $::hex address]
	set main [limited_style main function]

	foreach_with_prefix libopcodes { on off } {

	    set command_failed false
	    gdb_test_multiple "maint set libopcodes-styling enabled ${libopcodes}" "" {
		-re "^maint set libopcodes-styling enabled ${libopcodes}\r\n" {
		    exp_continue
		}

		-re "Use of libopcodes styling not supported on architecture \[^\r\n\]+\r\n" {
		    set command_failed true
		    exp_continue
		}

		-re "^$::gdb_prompt $" {
		    gdb_assert { !$command_failed || [string equal $libopcodes "on"] } \
			$gdb_test_name
		}
	    }

	    if { $libopcodes == "on" && $command_failed } {
		# We tried to turn on libopcodes disassembler styling,
		# but this is not supported on this architecture.
		continue
	    }

	    foreach_with_prefix disasm_styling { on off } {
		gdb_test_no_output "set style disassembler enabled ${disasm_styling}"

		if { $libopcodes == "off" && $disasm_styling == "on" \
			 && !$::python_disassembly_styling} {
		    # We have libopcodes based styling off, but
		    # disassembler styling turned on.  We're expecting
		    # Python Pygments to be used to add styling.
		    #
		    # However, if we get here, then we don't have the
		    # Pygments module, so skip this test.
		    continue
		}

		set saw_header_line false
		set saw_styled_output_line false
		set saw_unstyled_output_line false
		gdb_test_multiple "disassemble main" "" {
		    -re "disassemble main\r\n" {
			exp_continue
		    }
		    -re "^Dump of assembler code for function $main:" {
			set saw_header_line true
			exp_continue
		    }
		    -re "^\\s+${styled_hex}\\s+<\[^>\]+>:\\s+\[^\r\n\033\]+\r\n" {
			set saw_unstyled_output_line true
			exp_continue
		    }
		    -re "^\\s+${styled_hex}\\s+<\[^>\]+>:\\s+\[^\r\n\]+\033\[^\r\n\]+\r\n" {
			set saw_styled_output_line true
			exp_continue
		    }
		    -re "^End of assembler dump\\.\r\n" {
			exp_continue
		    }
		    -re "^$::gdb_prompt $" {
			gdb_assert { $saw_header_line }
			if { $disasm_styling } {
			    gdb_assert { $saw_styled_output_line }
			    gdb_assert { !$saw_unstyled_output_line }
			} else {
			    gdb_assert { !$saw_styled_output_line }
			    gdb_assert { $saw_unstyled_output_line }
			}
		    }
		}
	    }
	}
    }
}

# Disassemble a single isntruction at the start of main, then strip
# off the address and symbol information, returning just the
# disassembled instruction part.
proc get_single_disassembled_insn {} {
    set disasm_line [capture_command_output "x/1i *main" ""]
    set ret [regexp "^\[^:\]+:\\s*(.*)$" $disasm_line whole_match insn]

    if { $ret == 0 } {
	fail "failed to disassemble main"
	return ""
    }

    return $insn
}

# Check that, if the user is using Python Pygments for disassembler
# styling, then the styling correctly switches off when an error is
# detected in the Python code.
proc test_disassembler_error_handling { } {

    # This test requires the Python Pygments module to be installed
    # and used by GDB.
    if { !$::python_disassembly_styling } {
	return
    }

    save_vars { env(TERM) env(NO_COLOR) } {
	# We need an ANSI-capable terminal to get the output.
	setenv TERM ansi
	setenv NO_COLOR ""

	# Restart GDB with the correct TERM variable setting, this
	# means that GDB will enable styling.
	clean_restart_and_disable "restart 4" $::testfile

	# Disable use of libopcodes for styling.  As this function is
	# only called when Python Pygments module is available, we
	# should now be using that module to style the disassembler
	# output.
	gdb_test_no_output "maint set libopcodes-styling enabled off"

	# Disassemble a single instruction and ensure that the output
	# has styling markers in it.
	with_test_prefix "before" {
	    set insn_before [get_single_disassembled_insn]
	}

	gdb_assert { [regexp "\033" $insn_before] } \
	    "have style markers when Pygments is working fine"

	# Now replace the standard function that colorizes the
	# disassembler output, with a new function that always returns
	# None, this should cause GDB to stop using the Pygments
	# module for disassembler styling.
	gdb_py_test_silent_cmd \
	    [multi_line_input \
		 "python" \
		 "def replacement_colorize_disasm(content,gdbarch):" \
		 "  return None" \
		 "gdb.styling.colorize_disasm = replacement_colorize_disasm" \
		 "end"] \
	    "setup replacement colorize_disasm function" \
	    true

	with_test_prefix "after" {
	    set insn_after [get_single_disassembled_insn]
	}

	gdb_assert { ![regexp "\033" $insn_after] } \
	    "have no style markers when Pygments is broken"
    }
}

# A separate test from the above as the styled text this checks can't
# currently be disabled (the text is printed too early in GDB's
# startup process).
proc test_startup_version_string { } {
    gdb_exit

    save_vars { ::INTERNAL_GDBFLAGS } {
	set ::INTERNAL_GDBFLAGS [string map {"-q" ""} $::INTERNAL_GDBFLAGS]

	gdb_spawn
    }

    # Deliberate use of base STYLE proc here as the style of the
    # startup version string can't (currently) be controlled.
    set vers [style "GNU gdb.*" version]
    gdb_test "" "${vers}.*" "version is styled at startup"
}

# Color support is disabled when TERM=dumb and COLORTERM="".
# All colors are approximated to none when set.
proc test_colorsupport_monochrome { } {
    with_test_prefix "colorsupport_monochrome" {
	with_ansi_styling_terminal {
	    setenv TERM dumb
	    gdb_exit
	    gdb_start
	    gdb_test "print \$_colorsupport" \
		"\\\$1 = \"monochrome\"" \
		"color support is monochrome"
	    gdb_test_no_output "set style enabled off"
	    gdb_test_no_output "set style filename foreground none"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: none" \
	      "none without approximation"
	    gdb_test_no_output "set style filename foreground blue"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: none" \
	      "blue approximated to none"
	}
    }
}

# Color support is limited by 8 colors when TERM=ansi and COLORTERM="".
# All colors are approximated to basic colors when set.
proc test_colorsupport_8color { } {
    with_test_prefix "colorsupport_8color" {
	with_ansi_styling_terminal {
	    gdb_exit
	    gdb_start
	    gdb_test "print \$_colorsupport" \
		"\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color\"" \
		"color support is 8 color"
	    gdb_test_no_output "set style enabled off"
	    gdb_test_no_output "set style filename foreground none"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: none" \
	      "none without approximation"
	    gdb_test_no_output "set style filename foreground yellow"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: yellow" \
	      "yellow without approximation"
	    gdb_test_no_output "set style filename foreground 9"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: red" \
	      "9 approximated to red"
	    gdb_test_no_output "set style filename foreground 118"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: green" \
	      "118 approximated to green"
	    gdb_test_no_output "set style filename foreground #000ABC"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: blue" \
	      "#000ABC approximated to blue"
	}
    }
}

# Color support is limited by 256 colors when TERM=xterm-256color and
# COLORTERM="".  All colors are approximated by 256 color palette when set.
proc test_colorsupport_256color { } {
    with_test_prefix "colorsupport_256color" {
	with_ansi_styling_terminal {
	    setenv TERM xterm-256color
	    gdb_exit
	    gdb_start
	    gdb_test "print \$_colorsupport" \
		"\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color\"" \
		"color support is 256 color"
	    gdb_test_no_output "set style enabled off"
	    gdb_test_no_output "set style filename foreground none"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: none" \
	      "none without approximation"
	    gdb_test_no_output "set style filename foreground red"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: red" \
	      "red without approximation"
	    gdb_test_no_output "set style filename foreground 9"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: 9" \
	      "9 without approximation"
	    gdb_test_no_output "set style filename foreground 118"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: 118" \
	      "118 without approximation"
	    gdb_test_no_output "set style filename foreground #CD00CD"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: 5" \
	      "#CD00CD approximated to 5"
	    gdb_test_no_output "set style filename foreground #FFAF12"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: 214" \
	      "#FFAF12 approximated to 214"
	}
    }
}

# Color support is limited by 16777216 colors when TERM=xterm-256color and
# COLORTERM="truecolor".  No approximation needed.
proc test_colorsupport_truecolor { } {
    with_test_prefix "colorsupport_truecolor" {
	with_ansi_styling_terminal {
	    setenv TERM xterm-256color
	    setenv COLORTERM truecolor
	    gdb_exit
	    gdb_start
	    gdb_test "print \$_colorsupport" \
		"\033\\\[.*m\\\$1\033\\\[m = \"monochrome,ansi_8color,aixterm_16color,xterm_256color,rgb_24bit\"" \
		"color support is truecolor"
	    gdb_test_no_output "set style enabled off"
	    gdb_test_no_output "set style filename foreground none"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: none" \
	      "none without approximation"
	    gdb_test_no_output "set style filename foreground red"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: red" \
	      "red without approximation"
	    gdb_test_no_output "set style filename foreground 9"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: 9" \
	      "9 without approximation"
	    gdb_test_no_output "set style filename foreground 118"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: 118" \
	      "118 without approximation"
	    gdb_test_no_output "set style filename foreground #CD00CD"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: #CD00CD" \
	      "#CD00CD without approximation"
	}
    }
}

# Color support is limited by 16777216 colors when TERM=dumb and
# COLORTERM=24bit.  No approximation needed.  Basic colors replaced with RGB.
proc test_colorsupport_truecolor_only { } {
    with_test_prefix "colorsupport_truecolor_only" {
	with_ansi_styling_terminal {
	    setenv TERM dumb
	    setenv COLORTERM truecolor
	    gdb_exit
	    gdb_start
	    gdb_test "print \$_colorsupport" \
		"\\\$1 = \"monochrome,rgb_24bit\"" \
		"color support is truecolor only"
	    gdb_test_no_output "set style enabled off"
	    gdb_test_no_output "set style filename foreground none"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: none" \
	      "none without approximation"
	    gdb_test_no_output "set style filename foreground red"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: #DE382B" \
	      "red replaced by #DE382B"
	    gdb_test_no_output "set style filename foreground 9"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: #FF0000" \
	      "9 replaced by #FF0000"
	    gdb_test_no_output "set style filename foreground 118"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: #87FF00" \
	      "118 replaced by #87FF00"
	    gdb_test_no_output "set style filename foreground #CD00CD"
	    gdb_test "show style filename foreground" \
	      "The \"filename\" style foreground color is: #CD00CD" \
	      "#CD00CD without approximation"
	}
    }
}

# Check that styling is auto-disabled when using a "dumb" terminal,
# and/or when NO_COLOR is set.
#
# Also check that the user gets a warning if they enable styling in a
# "dumb" terminal, but don't get a warning if they are in an "ansi"
# terminal and NO_COLOR is set.
proc test_enable_styling_warning { } {
    save_vars { env(TERM) env(NO_COLOR) } {
	foreach_with_prefix no_color { set unset } {
	    if { $no_color eq "set" } {
		setenv NO_COLOR "xxx"
	    } else {
		unset -nocomplain ::env(NO_COLOR)
	    }

	    foreach_with_prefix term_type { dumb unset ansi } {
		if { $term_type eq "dumb" } {
		    setenv TERM dumb
		} elseif { $term_type eq "unset" } {
		    unset -nocomplain ::env(TERM)
		} elseif { $term_type eq "ansi" } {
		    setenv TERM ansi
		}

		set styling_on_re "CLI output styling is enabled\\."
		set styling_off_re "CLI output styling is disabled\\."

		if { $term_type eq "ansi" } {
		    if { $no_color eq "unset" } {
			set init_re $styling_on_re
		    } else {
			set init_re $styling_off_re
		    }
		    set enable_re ""
		} else {
		    set init_re $styling_off_re
		    set enable_re \
			"warning: The current terminal doesn't support styling\\.  Styled output might not appear as expected\\."
		}


		clean_restart
		gdb_test "show style enabled" $init_re
		gdb_test "set style enabled on" $enable_re
	    }
	}
    }
}

# Run an 'apropos' command.  Each line of output starts with a
# non-default style (command style).  Ensure that pagination triggers
# during the 'apropos' output such that, at the point pagination kicks
# in, a non-default style is in effect.
#
# Then, at the pagination prompt, quit the command.
#
# Next, run a command which switches to a different style, and then
# back to the current style.
#
# At one point, a bug in the pagination code would leave the
# non-default style from the 'apropos' command recorded as the current
# style, such that the second command would switch back to the earlier
# style.
proc test_pagination_cmd_after_quit_styling {} {
    with_ansi_styling_terminal {
	clean_restart
    }

    # We're going to use 'apropos time'.  Check that with a height of
    # 12 lines, each line starts with a non-default style, and that we
    # do see the pagination prompt.  This means that there are more
    # than 12 lines for this command.
    with_test_prefix "validate apropos output" {
	gdb_test_no_output "set height 12"

	set saw_pagination_prompt false
	gdb_test_multiple "apropos time" "" {
	    -re "^apropos time\r\n" {
		exp_continue
	    }
	    -re "^\033\\\[39;49;1;23;24;27m\[^\r\n\]+\r\n" {
		exp_continue
	    }
	    -re "^$::pagination_prompt$" {
		set saw_pagination_prompt true
		send_gdb "q\n"
		exp_continue
	    }
	    -re "^q\r\n" {
		exp_continue
	    }
	    -re "^Quit\r\n" {
		exp_continue
	    }
	    -re "^$::gdb_prompt $" {
		gdb_assert { $saw_pagination_prompt } $gdb_test_name
	    }
	    -re "^\[^\r\n\]+\r\n" {
		exp_continue
	    }
	}
    }

    # Now reduce the height to 10 and re-run 'apropos time'.  Based on
    # the previous check, we know that this is going to present the
    # pagination prompt when a non-default style is in use.
    gdb_test_no_output "set height 10"

    set saw_pagination_prompt false
    gdb_test_multiple "apropos time" "" {
	-re "$::pagination_prompt" {
	    set saw_pagination_prompt true
	    send_gdb "q\n"
	    exp_continue
	}
	-re "\r\n$::gdb_prompt $" {
	    gdb_assert { $saw_pagination_prompt } $gdb_test_name
	}
    }

    # The help output for this maintenance command switches to a
    # different style, and then back to the default.  If the
    # pagination bug still exists, then this would switch back to the
    # non-default style that was in use when pagination kicked in
    # above.
    gdb_test "maintenance time" \
	"^\"\033\\\[39;49;1;23;24;27mmaintenance time\033\\\[m\" takes a numeric argument\\."
}

# Helper for test_pagination_prompt_styling.  Return false if STR, a
# line that appears immediately before a pagination prompt, matches
# the pattern for needing a style reset at the end, but does not have
# the style reset.
#
# In all other cases, return true.  So lines that don't match the
# known pattern for neededing a style reset will always return true,
# as will lines that match the pattern, and do have the style reset.
proc previous_line_is_ok { str } {

    # Create a copy of STR with all the '\033' characters removed.
    # Then compare string lengths to get a count of the '\033'
    # charactes present in STR.
    regsub -all "\033" $str {} stripped
    set count [expr {[string length $str] - [string length $stripped]}]

    # If STR switched styles, then it _must_ switch back again,
    # otherwise the pagination prompt will be in the wrong style.
    # This means that there _must_ be an even number of '\033'
    # characters in STR.  If there is not then we switched style, but
    # failed to switch back.
    if {$count % 2 != 0} {
	return false
    }

    # For lines that don't match this pattern, we cannot comment on
    # where the style reset should occur, so lets just claim the line
    # is fine.
    if { ![regexp "\\s+$::hex - $::hex is \[^\r\n\]+ in \033" $str] } {
	return true
    }

    # This line did match the above pattern, so we know that a style
    # reset _must_ occur at the end of the line.  If it doesn't then
    # this line is not OK.
    if { ![regexp "\033\\\[m$" $str] } {
	return false
    }

    # All tests passed, this line looks OK.
    return true
}

# Test that the pagination prompt is displayed unstyled.  This is done
# by looking at the 'info files' output and selecting a width that
# will mean we should get a pagination prompt part way through a
# styled filename.
#
# Then, re-run 'info files' and check that for every pagination
# prompt, the previous line disables styling as expected.
proc test_pagination_prompt_styling {} {
    with_ansi_styling_terminal {
	clean_restart $::testfile
    }

    if {![runto_main]} {
	return
    }

    # Set height so we actually get a pagination prompt.
    gdb_test_no_output "set height 3"

    # Scan the 'info files' output and set DESIRED_WIDTH such that it
    # will trigger pagination part-way through a styled filename.
    set desired_width 0
    gdb_test_multiple "info files" "find good test width" {
	-re "^info files\r\n" {
	    exp_continue
	}

	-re "^$::pagination_prompt$" {
	    send_gdb "\n"
	    exp_continue
	}

	-re "^$::gdb_prompt $" {
	}

	-re "^((\\s+$::hex - $::hex is \[^\r\n\]+ in )\[^\r\n\]+)\r\n" {
	    if { $desired_width == 0 } {
		set full_line $expect_out(1,string)
		set inner_line $expect_out(2,string)
		set desired_width [expr [string length $inner_line] + ([string length $full_line] - [string length $inner_line]) / 2]
	    }
	    exp_continue
	}

	-re "^\[^\r\n\]*\r\n" {
	    exp_continue
	}
    }

    if { $desired_width < [string length $::pagination_prompt_str] + 2 } {
	# Avoid readline wrapping after printing the pagination prompt.
	return
    }

    # Now setup the screen width.
    gdb_test_no_output "set width $desired_width" \
	"set width to desired width"

    # Re-run 'info files'.  Check that the content before any
    # pagination prompt correctly disables styling.
    set saw_bad_line false
    set prev_line ""
    gdb_test_multiple "info files" "check pagination prompt styling" {
	-re "^info files\r\n" {
	    exp_continue
	}

	-re "^$::pagination_prompt$" {
	    if { ![previous_line_is_ok $prev_line] } {
		set saw_bad_line true
	    }
	    send_gdb "\n"
	    exp_continue
	}

	-re "^(\[^\r\n\]+)$::pagination_prompt$" {
	    set prev_line $expect_out(1,string)
	    if { ![previous_line_is_ok $prev_line] } {
		set saw_bad_line true
	    }
	    send_gdb "\n"
	    exp_continue
	}

	-re "^$::gdb_prompt $" {
	    gdb_assert { !$saw_bad_line } $gdb_test_name
	}

	-re "^(\[^\r\n\]*)\r\n" {
	    set prev_line $expect_out(1,string)
	    exp_continue
	}
    }
}

# Test that GDB can correctly restore the current style after a
# pagination prompt.
#
# Set the logging file to a garbage string based on LENGTH (is
# actually 2x LENGTH), then 'show logging file'.  Press return at the
# pagination prompt, and check that the reset of the filename is
# styled correctly, and that GDB correctly switches back to the
# default style once the logging file has finished.
proc test_pagination_continue_styling_1 { length } {
    with_ansi_styling_terminal {
	clean_restart $::testfile
    }

    set filename [string repeat "ax" $length]

    gdb_test_no_output "set logging file $filename"

    gdb_test_no_output "set height 3"
    gdb_test_no_output "set width 80"

    set saw_bad_styling false
    gdb_test_multiple "show logging file" "" {
	-re "^show logging file\r\n" {
	    exp_continue
	}

	-re "^The current logfile is \"\033\\\[32;49;22;23;24;27m(?:ax)+\033\\\[m" {
	    exp_continue
	}

	-re "^\r\n\033\\\[32;49;22;23;24;27m(?:ax)+\033\\\[m(?=--)" {
	    exp_continue
	}

	-re "^\r\n\033\\\[32;49;22;23;24;27m(?:ax)+(?=--)" {
	    set saw_bad_styling true
	    exp_continue
	}

	-re "^\r\n\033\\\[32;49;22;23;24;27m(?:ax)+\033\\\[m\"\\.\r\n" {
	    exp_continue
	}

	-re "^$::gdb_prompt $" {
	    gdb_assert { !$saw_bad_styling } $gdb_test_name
	}

	-re "^$::pagination_prompt$$" {
	    send_gdb "\n"
	    exp_continue
	}
    }
}

# Wrapper around test_pagination_continue_styling_1, calls that
# function with different lengths.
proc test_pagination_continue_styling { } {
    foreach_with_prefix length { 80 160 } {
	test_pagination_continue_styling_1 $length
    }
}

# Test the styling of "finish".
proc test_finish_styling {} {
    with_ansi_styling_terminal {
	clean_restart $::testfile
    }

    gdb_test "break some_called_function" "Breakpoint $::decimal at .*"
    if {![runto some_called_function]} {
	return
    }

    gdb_test "finish" "Value returned is [style ${::valnum_re} variable] = 0"
}

# Test some logging styling.
proc test_logging_styling {} {
    with_ansi_styling_terminal {
	clean_restart
    }

    set filename [standard_output_file logging.txt]
    gdb_test_no_output "set logging file $filename" \
	"set logging file"
    gdb_test "set logging enabled on" \
	[multi_line \
	     "Copying output to [style $filename file]\\." \
	     "Copying debug output to [style $filename file]\\."] \
	"logging filenames are styled"
    gdb_test "set logging enabled off" \
	"Done logging to [style $filename file]\\." \
	"filename styled when logging disabled"

    # Test that error messages are styled.
    gdb_test "list" \
	"No symbol table is loaded\\.  Use the \"[style file command]\" command\\."
}

# Check to see if the Python styling of disassembler output is
# expected or not, this styling requires Python support in GDB, and
# the Python pygments module to be available.
clean_restart $testfile
if {[allow_python_tests] && [gdb_py_module_available "pygments"]} {
    set python_disassembly_styling true
} else {
    set python_disassembly_styling false
}

# Run tests with all styles in their default state.
with_test_prefix "all styles enabled" {
    run_style_tests
}

# Now, for each style in turn.  Disable that style only and run the
# test again.  Things in that style should NOT now be styled.
foreach style { command title file function highlight variable \
		    address metadata } {
    set currently_disabled_style $style
    with_test_prefix "disable style $style" {
	run_style_tests
    }
}

# Check that the disassembler styling can be disabled.
test_disable_disassembler_styling

# Check that GDB handles an error in the Python Pygments disassembly
# styling code.
test_disassembler_error_handling

# Finally, check the styling of the version string during startup.
test_startup_version_string

test_colorsupport_monochrome
test_colorsupport_8color
test_colorsupport_256color
test_colorsupport_truecolor
test_colorsupport_truecolor_only

test_enable_styling_warning
test_pagination_cmd_after_quit_styling
test_pagination_prompt_styling
test_pagination_continue_styling
test_finish_styling
test_logging_styling
