1 /* Output colorization.
2 Copyright (C) 2011-2024 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
21 #include "diagnostic-color.h"
22 #include "diagnostic-url.h"
25 # define WIN32_LEAN_AND_MEAN
29 #include "color-macros.h"
31 /* The context and logic for choosing default --color screen attributes
32 (foreground and background colors, etc.) are the following.
33 -- There are eight basic colors available, each with its own
34 nominal luminosity to the human eye and foreground/background
35 codes (black [0 %, 30/40], blue [11 %, 34/44], red [30 %, 31/41],
36 magenta [41 %, 35/45], green [59 %, 32/42], cyan [70 %, 36/46],
37 yellow [89 %, 33/43], and white [100 %, 37/47]).
38 -- Sometimes, white as a background is actually implemented using
39 a shade of light gray, so that a foreground white can be visible
40 on top of it (but most often not).
41 -- Sometimes, black as a foreground is actually implemented using
42 a shade of dark gray, so that it can be visible on top of a
43 background black (but most often not).
44 -- Sometimes, more colors are available, as extensions.
45 -- Other attributes can be selected/deselected (bold [1/22],
46 underline [4/24], standout/inverse [7/27], blink [5/25], and
47 invisible/hidden [8/28]). They are sometimes implemented by
48 using colors instead of what their names imply; e.g., bold is
49 often achieved by using brighter colors. In practice, only bold
50 is really available to us, underline sometimes being mapped by
51 the terminal to some strange color choice, and standout best
52 being left for use by downstream programs such as less(1).
53 -- We cannot assume that any of the extensions or special features
54 are available for the purpose of choosing defaults for everyone.
55 -- The most prevalent default terminal backgrounds are pure black
56 and pure white, and are not necessarily the same shades of
57 those as if they were selected explicitly with SGR sequences.
58 Some terminals use dark or light pictures as default background,
59 but those are covered over by an explicit selection of background
60 color with an SGR sequence; their users will appreciate their
61 background pictures not be covered like this, if possible.
62 -- Some uses of colors attributes is to make some output items
63 more understated (e.g., context lines); this cannot be achieved
64 by changing the background color.
65 -- For these reasons, the GCC color defaults should strive not
66 to change the background color from its default, unless it's
67 for a short item that should be highlighted, not understated.
68 -- The GCC foreground color defaults (without an explicitly set
69 background) should provide enough contrast to be readable on any
70 terminal with either a black (dark) or white (light) background.
71 This only leaves red, magenta, green, and cyan (and their bold
72 counterparts) and possibly bold blue. */
73 /* Default colors. The user can overwrite them using environment
74 variable GCC_COLORS. */
79 unsigned char name_len
;
84 static struct color_cap color_dict
[] =
86 { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED
), 5, false },
87 { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA
),
89 { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN
), 4, false },
90 { "range1", SGR_SEQ (COLOR_FG_GREEN
), 6, false },
91 { "range2", SGR_SEQ (COLOR_FG_BLUE
), 6, false },
92 { "locus", SGR_SEQ (COLOR_BOLD
), 5, false },
93 { "quote", SGR_SEQ (COLOR_BOLD
), 5, false },
94 { "path", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN
), 4, false },
95 { "fnname", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN
), 6, false },
96 { "targs", SGR_SEQ (COLOR_FG_MAGENTA
), 5, false },
97 { "fixit-insert", SGR_SEQ (COLOR_FG_GREEN
), 12, false },
98 { "fixit-delete", SGR_SEQ (COLOR_FG_RED
), 12, false },
99 { "diff-filename", SGR_SEQ (COLOR_BOLD
), 13, false },
100 { "diff-hunk", SGR_SEQ (COLOR_FG_CYAN
), 9, false },
101 { "diff-delete", SGR_SEQ (COLOR_FG_RED
), 11, false },
102 { "diff-insert", SGR_SEQ (COLOR_FG_GREEN
), 11, false },
103 { "type-diff", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN
), 9, false },
104 { "valid", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN
), 5, false },
105 { "invalid", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED
), 7, false },
106 { NULL
, NULL
, 0, false }
110 colorize_start (bool show_color
, const char *name
, size_t name_len
)
112 struct color_cap
const *cap
;
117 for (cap
= color_dict
; cap
->name
; cap
++)
118 if (cap
->name_len
== name_len
119 && memcmp (cap
->name
, name
, name_len
) == 0)
121 if (cap
->name
== NULL
)
128 colorize_stop (bool show_color
)
130 return show_color
? SGR_RESET
: "";
133 /* Parse GCC_COLORS. The default would look like:
134 GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
135 range1=32:range2=34:locus=01:quote=01:path=01;36:\
136 fixit-insert=32:fixit-delete=31:'\
137 diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
139 No character escaping is needed or supported. */
141 parse_gcc_colors (void)
143 const char *p
, *q
, *name
, *val
;
145 size_t name_len
= 0, val_len
= 0;
147 p
= getenv ("GCC_COLORS"); /* Plural! */
155 /* From now on, be well-formed or you're gone. */
157 if (*q
== ':' || *q
== '\0')
159 struct color_cap
*cap
;
165 /* Empty name without val (empty cap)
166 won't match and will be ignored. */
167 for (cap
= color_dict
; cap
->name
; cap
++)
168 if (cap
->name_len
== name_len
169 && memcmp (cap
->name
, name
, name_len
) == 0)
171 /* If name unknown, go on for forward compatibility. */
175 free (CONST_CAST (char *, cap
->val
));
176 b
= XNEWVEC (char, val_len
+ sizeof (SGR_SEQ ("")));
177 memcpy (b
, SGR_START
, strlen (SGR_START
));
178 memcpy (b
+ strlen (SGR_START
), val
, val_len
);
179 memcpy (b
+ strlen (SGR_START
) + val_len
, SGR_END
,
181 cap
->val
= (const char *) b
;
182 cap
->free_val
= true;
191 if (q
== name
|| val
)
195 val
= ++q
; /* Can be the empty string. */
197 else if (val
== NULL
)
198 q
++; /* Accumulate name. */
199 else if (*q
== ';' || (*q
>= '0' && *q
<= '9'))
200 q
++; /* Accumulate val. Protect the terminal from being sent
206 /* Return true if we should use color when in auto mode, false otherwise. */
208 should_colorize (void)
211 /* For consistency reasons, one should check the handle returned by
212 _get_osfhandle(_fileno(stderr)) because the function
213 pp_write_text_to_stream() in pretty-print.cc calls fputs() on
214 that stream. However, the code below for non-Windows doesn't seem
215 to care about it either... */
219 h
= GetStdHandle (STD_ERROR_HANDLE
);
220 return (h
!= INVALID_HANDLE_VALUE
) && (h
!= NULL
)
221 && GetConsoleMode (h
, &m
);
223 char const *t
= getenv ("TERM");
224 /* emacs M-x shell sets TERM="dumb". */
225 return t
&& strcmp (t
, "dumb") != 0 && isatty (STDERR_FILENO
);
230 colorize_init (diagnostic_color_rule_t rule
)
234 case DIAGNOSTICS_COLOR_NO
:
236 case DIAGNOSTICS_COLOR_YES
:
237 return parse_gcc_colors ();
238 case DIAGNOSTICS_COLOR_AUTO
:
239 if (should_colorize ())
240 return parse_gcc_colors ();
248 /* Return URL_FORMAT_XXX which tells how we should emit urls
250 We use GCC_URLS and if that is not defined TERM_URLS.
251 If neither is defined the feature is enabled by default. */
253 static diagnostic_url_format
254 parse_env_vars_for_urls ()
258 p
= getenv ("GCC_URLS"); /* Plural! */
260 p
= getenv ("TERM_URLS");
263 return URL_FORMAT_DEFAULT
;
266 return URL_FORMAT_NONE
;
268 if (!strcmp (p
, "no"))
269 return URL_FORMAT_NONE
;
271 if (!strcmp (p
, "st"))
272 return URL_FORMAT_ST
;
274 if (!strcmp (p
, "bel"))
275 return URL_FORMAT_BEL
;
277 return URL_FORMAT_DEFAULT
;
280 /* Return true if we should use urls when in auto mode, false otherwise. */
288 const char *term
, *colorterm
;
290 /* First check the terminal is capable of printing color escapes,
291 if not URLs won't work either. */
292 if (!should_colorize ())
295 /* xfce4-terminal is known to not implement URLs at this time.
296 Recently new installations (0.8) will safely ignore the URL escape
297 sequences, but a large number of legacy installations (0.6.3) print
298 garbage when URLs are printed. Therefore we lose nothing by
299 disabling this feature for that specific terminal type. */
300 colorterm
= getenv ("COLORTERM");
301 if (colorterm
&& !strcmp (colorterm
, "xfce4-terminal"))
304 /* Old versions of gnome-terminal where URL escapes cause screen
305 corruptions set COLORTERM="gnome-terminal", recent versions
306 with working URL support set this to "truecolor". */
307 if (colorterm
&& !strcmp (colorterm
, "gnome-terminal"))
310 /* Since the following checks are less specific than the ones
311 above, let GCC_URLS and TERM_URLS override the decision. */
312 if (getenv ("GCC_URLS") || getenv ("TERM_URLS"))
315 /* In an ssh session the COLORTERM is not there, but TERM=xterm
316 can be used as an indication of a incompatible terminal while
317 TERM=xterm-256color appears to be a working terminal. */
318 term
= getenv ("TERM");
319 if (!colorterm
&& term
&& !strcmp (term
, "xterm"))
322 /* When logging in a linux over serial line, we see TERM=linux
323 and no COLORTERM, it is unlikely that the URL escapes will
324 work in that environmen either. */
325 if (!colorterm
&& term
&& !strcmp (term
, "linux"))
332 /* Determine if URLs should be enabled, based on RULE,
333 and, if so, which format to use.
334 This reuses the logic for colorization. */
336 diagnostic_url_format
337 determine_url_format (diagnostic_url_rule_t rule
)
341 case DIAGNOSTICS_URL_NO
:
342 return URL_FORMAT_NONE
;
343 case DIAGNOSTICS_URL_YES
:
344 return parse_env_vars_for_urls ();
345 case DIAGNOSTICS_URL_AUTO
:
346 if (auto_enable_urls ())
347 return parse_env_vars_for_urls ();
349 return URL_FORMAT_NONE
;