2 Copyright (C) 2018-2024 Free Software Foundation, Inc.
4 This file is part of GDB.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "gdb_curses.h"
21 #include "gdbsupport/gdb_regex.h"
23 /* A regular expression that is used for matching ANSI terminal escape
26 static const char ansi_regex_text
[] =
30 /* Capture parameter and intermediate bytes. */
32 /* Parameter bytes. */
34 /* Intermediate bytes. */
36 /* End the first capture. */
39 #define FINAL_SUBEXP 2
42 /* The number of subexpressions to allocate space for, including the
43 "0th" whole match subexpression. */
44 #define NUM_SUBEXPRESSIONS 3
46 /* The compiled form of ansi_regex_text. */
48 static regex_t ansi_regex
;
50 /* This maps 8-color palette to RGB triples. The values come from
51 plain linux terminal. */
53 static const uint8_t palette_8colors
[][3] = {
54 { 1, 1, 1 }, /* Black. */
55 { 222, 56, 43 }, /* Red. */
56 { 57, 181, 74 }, /* Green. */
57 { 255, 199, 6 }, /* Yellow. */
58 { 0, 111, 184 }, /* Blue. */
59 { 118, 38, 113 }, /* Magenta. */
60 { 44, 181, 233 }, /* Cyan. */
61 { 204, 204, 204 }, /* White. */
64 /* This maps 16-color palette to RGB triples. The values come from xterm. */
66 static const uint8_t palette_16colors
[][3] = {
67 { 0, 0, 0 }, /* Black. */
68 { 205, 0, 0 }, /* Red. */
69 { 0, 205, 0 }, /* Green. */
70 { 205, 205, 0 }, /* Yellow. */
71 { 0, 0, 238 }, /* Blue. */
72 { 205, 0, 205 }, /* Magenta. */
73 { 0, 205, 205 }, /* Cyan. */
74 { 229, 229, 229 }, /* White. */
75 { 127, 127, 127 }, /* Bright Black. */
76 { 255, 0, 0 }, /* Bright Red. */
77 { 0, 255, 0 }, /* Bright Green. */
78 { 255, 255, 0 }, /* Bright Yellow. */
79 { 92, 92, 255 }, /* Bright Blue. */
80 { 255, 0, 255 }, /* Bright Magenta. */
81 { 0, 255, 255 }, /* Bright Cyan. */
82 { 255, 255, 255 } /* Bright White. */
86 /* Must correspond to ui_file_style::basic_color. */
87 const std::vector
<const char *> ui_file_style::basic_color_enums
= {
100 /* Returns text representation of a basic COLOR. */
103 basic_color_name (int color
)
105 int pos
= color
- ui_file_style::NONE
;
106 if (0 <= pos
&& pos
< ui_file_style::basic_color_enums
.size ())
107 if (const char *s
= ui_file_style::basic_color_enums
[pos
])
109 error (_("Basic color %d has no name."), color
);
112 /* See ui-style.h. */
115 ui_file_style::color::append_ansi (bool is_fg
, std::string
*str
) const
117 if (m_color_space
== color_space::MONOCHROME
)
118 str
->append (is_fg
? "39" : "49");
119 else if (is_basic ())
120 str
->append (std::to_string (m_value
+ (is_fg
? 30 : 40)));
121 else if (m_color_space
== color_space::AIXTERM_16COLOR
)
122 str
->append (std::to_string (m_value
- WHITE
- 1 + (is_fg
? 90 : 100)));
123 else if (m_color_space
== color_space::XTERM_256COLOR
)
124 str
->append (is_fg
? "38;5;" : "48;5;").append (std::to_string (m_value
));
125 else if (m_color_space
== color_space::RGB_24BIT
)
127 // See ISO/IEC 8613-6 (or ITU T.416) 13.1.8 Select Graphic Rendition (SGR)
128 str
->append (is_fg
? "38;2;" : "48;2;");
129 str
->append (std::to_string (m_red
)
130 + ";" + std::to_string (m_green
)
131 + ";" + std::to_string (m_blue
));
134 gdb_assert_not_reached ("no valid ansi representation of the color");
137 /* See ui-style.h. */
139 ui_file_style::color::to_ansi (bool is_fg
) const
141 std::string s
= "\033[";
142 append_ansi (is_fg
, &s
);
147 /* See ui-style.h. */
150 ui_file_style::color::to_string () const
152 if (m_color_space
== color_space::RGB_24BIT
)
155 snprintf (s
, sizeof s
, "#%02X%02X%02X", m_red
, m_green
, m_blue
);
158 else if (is_none () || is_basic ())
159 return basic_color_name (m_value
);
161 return std::to_string (get_value ());
164 /* See ui-style.h. */
167 ui_file_style::color::get_rgb (uint8_t *rgb
) const
169 if (m_color_space
== color_space::RGB_24BIT
)
175 else if (m_color_space
== color_space::ANSI_8COLOR
176 && 0 <= m_value
&& m_value
<= 7)
177 memcpy (rgb
, palette_8colors
[m_value
], 3 * sizeof (uint8_t));
178 else if (m_color_space
== color_space::AIXTERM_16COLOR
179 && 0 <= m_value
&& m_value
<= 15)
180 memcpy (rgb
, palette_16colors
[m_value
], 3 * sizeof (uint8_t));
181 else if (m_color_space
!= color_space::XTERM_256COLOR
)
182 gdb_assert_not_reached ("get_rgb called on invalid color");
183 else if (0 <= m_value
&& m_value
<= 15)
184 memcpy (rgb
, palette_16colors
[m_value
], 3 * sizeof (uint8_t));
185 else if (m_value
>= 16 && m_value
<= 231)
189 /* This obscure formula seems to be what terminals actually
191 int component
= value
/ 36;
192 rgb
[0] = component
== 0 ? 0 : (55 + component
* 40);
194 component
= value
/ 6;
195 rgb
[1] = component
== 0 ? 0 : (55 + component
* 40);
197 rgb
[2] = value
== 0 ? 0 : (55 + value
* 40);
199 else if (232 <= m_value
&& m_value
<= 255)
201 uint8_t v
= (m_value
- 232) * 10 + 8;
207 gdb_assert_not_reached ("get_rgb called on invalid color");
210 /* See ui-style.h. */
213 ui_file_style::color::approximate (const std::vector
<color_space
> &spaces
) const
215 if (spaces
.empty () || is_none ())
218 color_space target_space
= color_space::MONOCHROME
;
219 for (color_space sp
: spaces
)
220 if (sp
== m_color_space
)
222 else if (sp
> target_space
)
225 if (target_space
== color_space::RGB_24BIT
)
229 return color (rgb
[0], rgb
[1], rgb
[2]);
233 switch (target_space
)
235 case color_space::ANSI_8COLOR
:
238 case color_space::AIXTERM_16COLOR
:
241 case color_space::XTERM_256COLOR
:
246 if (is_simple() && m_value
< target_size
)
247 return color (target_space
, m_value
);
250 int best_distance
= std::numeric_limits
<int>::max ();
254 for (int i
= 0; i
< target_size
; ++i
)
257 color
c (target_space
, i
);
259 int d_red
= std::abs (rgb
[0] - c_rgb
[0]);
260 int d_green
= std::abs (rgb
[1] - c_rgb
[1]);
261 int d_blue
= std::abs (rgb
[2] - c_rgb
[2]);
262 int dist
= d_red
* d_red
+ d_green
* d_green
+ d_blue
* d_blue
;
263 if (dist
< best_distance
)
265 best_distance
= dist
;
273 /* See ui-style.h. */
276 ui_file_style::to_ansi () const
278 std::string
result ("\033[");
281 m_foreground
.append_ansi (true, &result
);
282 result
.push_back (';');
283 m_background
.append_ansi (false, &result
);
284 result
.push_back (';');
285 if (m_intensity
== NORMAL
)
286 result
.append ("22");
288 result
.append (std::to_string (m_intensity
));
289 result
.push_back (';');
291 result
.push_back ('7');
293 result
.append ("27");
295 result
.push_back ('m');
299 /* Read a ";" and a number from STRING. Return the number of
300 characters read and put the number into *NUM. */
303 read_semi_number (const char *string
, regoff_t
*idx
, long *num
)
305 if (string
[*idx
] != ';')
308 if (string
[*idx
] < '0' || string
[*idx
] > '9')
311 *num
= strtol (string
+ *idx
, &tail
, 10);
312 *idx
= tail
- string
;
316 /* A helper for ui_file_style::parse that reads an extended color
317 sequence; that is, and 8- or 24- bit color. */
320 extended_color (const char *str
, regoff_t
*idx
, ui_file_style::color
*color
)
324 if (!read_semi_number (str
, idx
, &value
))
330 if (!read_semi_number (str
, idx
, &value
))
333 if (value
>= 0 && value
<= 255)
334 *color
= ui_file_style::color (value
);
342 if (!read_semi_number (str
, idx
, &r
)
344 || !read_semi_number (str
, idx
, &g
)
346 || !read_semi_number (str
, idx
, &b
)
349 *color
= ui_file_style::color (r
, g
, b
);
353 /* Unrecognized sequence. */
360 /* See ui-style.h. */
363 ui_file_style::parse (const char *buf
, size_t *n_read
)
365 regmatch_t subexps
[NUM_SUBEXPRESSIONS
];
367 int match
= regexec (&ansi_regex
, buf
, ARRAY_SIZE (subexps
), subexps
, 0);
368 if (match
== REG_NOMATCH
)
373 /* Other failures mean the regexp is broken. */
374 gdb_assert (match
== 0);
375 /* The regexp is anchored. */
376 gdb_assert (subexps
[0].rm_so
== 0);
377 /* The final character exists. */
378 gdb_assert (subexps
[FINAL_SUBEXP
].rm_eo
- subexps
[FINAL_SUBEXP
].rm_so
== 1);
380 if (buf
[subexps
[FINAL_SUBEXP
].rm_so
] != 'm')
382 /* We don't handle this sequence, so just drop it. */
383 *n_read
= subexps
[0].rm_eo
;
387 /* Examine each setting in the match and apply it to the result.
388 See the Select Graphic Rendition section of
389 https://en.wikipedia.org/wiki/ANSI_escape_code. In essence each
390 code is just a number, separated by ";"; there are some more
391 wrinkles but we don't support them all.. */
393 /* "\033[m" means the same thing as "\033[0m", so handle that
395 if (subexps
[DATA_SUBEXP
].rm_so
== subexps
[DATA_SUBEXP
].rm_eo
)
396 *this = ui_file_style ();
398 for (regoff_t i
= subexps
[DATA_SUBEXP
].rm_so
;
399 i
< subexps
[DATA_SUBEXP
].rm_eo
;
406 else if (buf
[i
] >= '0' && buf
[i
] <= '9')
409 long value
= strtol (buf
+ i
, &tail
, 10);
416 *this = ui_file_style ();
431 m_intensity
= NORMAL
;
435 m_intensity
= NORMAL
;
450 m_foreground
= color (value
- 30);
465 m_background
= color (value
- 40);
480 m_foreground
= color (value
- 90 + 8);
491 m_background
= color (value
- 100 + 8);
495 /* If we can't parse the extended color, fail. */
496 if (!extended_color (buf
, &i
, &m_foreground
))
498 *n_read
= subexps
[0].rm_eo
;
504 /* If we can't parse the extended color, fail. */
505 if (!extended_color (buf
, &i
, &m_background
))
507 *n_read
= subexps
[0].rm_eo
;
513 /* Ignore everything else. */
519 /* Unknown, let's just ignore. */
523 *n_read
= subexps
[0].rm_eo
;
527 /* See ui-style.h. */
530 skip_ansi_escape (const char *buf
, int *n_read
)
532 regmatch_t subexps
[NUM_SUBEXPRESSIONS
];
534 int match
= regexec (&ansi_regex
, buf
, ARRAY_SIZE (subexps
), subexps
, 0);
535 if (match
== REG_NOMATCH
|| buf
[subexps
[FINAL_SUBEXP
].rm_so
] != 'm')
538 *n_read
= subexps
[FINAL_SUBEXP
].rm_eo
;
542 void _initialize_ui_style ();
544 _initialize_ui_style ()
546 int code
= regcomp (&ansi_regex
, ansi_regex_text
, REG_EXTENDED
);
547 /* If the regular expression was incorrect, it was a programming
549 gdb_assert (code
== 0);
552 /* See ui-style.h. */
554 const std::vector
<color_space
> &
557 static const std::vector
<color_space
> value
= []
559 std::vector
<color_space
> result
= {color_space::MONOCHROME
};
561 int colors
= tgetnum ("Co");
563 result
.push_back (color_space::ANSI_8COLOR
);
565 result
.push_back (color_space::AIXTERM_16COLOR
);
567 result
.push_back (color_space::XTERM_256COLOR
);
569 const char *colorterm
= getenv ("COLORTERM");
570 if (colorterm
!= nullptr && (!strcmp (colorterm
, "truecolor")
571 || !strcmp (colorterm
, "24bit")))
572 result
.push_back (color_space::RGB_24BIT
);
580 color_space_name (color_space c
)
584 case color_space::MONOCHROME
: return "monochrome";
585 case color_space::ANSI_8COLOR
: return "ansi_8color";
586 case color_space::AIXTERM_16COLOR
: return "aixterm_16color";
587 case color_space::XTERM_256COLOR
: return "xterm_256color";
588 case color_space::RGB_24BIT
: return "rgb_24bit";
590 gdb_assert_not_reached ("color_space_name called on invalid color");
594 color_space_safe_cast (color_space
*result
, long c
)
596 switch (static_cast<color_space
>(c
))
598 case color_space::MONOCHROME
:
599 case color_space::ANSI_8COLOR
:
600 case color_space::AIXTERM_16COLOR
:
601 case color_space::XTERM_256COLOR
:
602 case color_space::RGB_24BIT
:
603 *result
= static_cast<color_space
>(c
);