Branch data Line data Source code
1 : : /* Output colorization.
2 : : Copyright (C) 2011-2024 Free Software Foundation, Inc.
3 : :
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)
7 : : any later version.
8 : :
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.
13 : :
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
17 : : 02110-1301, USA. */
18 : :
19 : : #include "config.h"
20 : : #include "system.h"
21 : : #include "diagnostic-color.h"
22 : : #include "diagnostic-url.h"
23 : :
24 : : #ifdef __MINGW32__
25 : : # define WIN32_LEAN_AND_MEAN
26 : : # include <windows.h>
27 : : #endif
28 : :
29 : : #include "color-macros.h"
30 : :
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. */
75 : : struct color_cap
76 : : {
77 : : const char *name;
78 : : const char *val;
79 : : unsigned char name_len;
80 : : bool free_val;
81 : : };
82 : :
83 : : /* For GCC_COLORS. */
84 : : static struct color_cap color_dict[] =
85 : : {
86 : : { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
87 : : { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
88 : : 7, false },
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 }
107 : : };
108 : :
109 : : const char *
110 : 78959056 : colorize_start (bool show_color, const char *name, size_t name_len)
111 : : {
112 : 78959056 : struct color_cap const *cap;
113 : :
114 : 78959056 : if (!show_color)
115 : : return "";
116 : :
117 : 9746 : for (cap = color_dict; cap->name; cap++)
118 : 9746 : if (cap->name_len == name_len
119 : 2823 : && memcmp (cap->name, name, name_len) == 0)
120 : : break;
121 : 1225 : if (cap->name == NULL)
122 : : return "";
123 : :
124 : 1225 : return cap->val;
125 : : }
126 : :
127 : : const char *
128 : 78602256 : colorize_stop (bool show_color)
129 : : {
130 : 78602256 : return show_color ? SGR_RESET : "";
131 : : }
132 : :
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:\
138 : : type-diff=01;32'
139 : : No character escaping is needed or supported. */
140 : : static bool
141 : 495699 : parse_gcc_colors (void)
142 : : {
143 : 495699 : const char *p, *q, *name, *val;
144 : 495699 : char *b;
145 : 495699 : size_t name_len = 0, val_len = 0;
146 : :
147 : 495699 : p = getenv ("GCC_COLORS"); /* Plural! */
148 : 495699 : if (p == NULL)
149 : : return true;
150 : 0 : if (*p == '\0')
151 : : return false;
152 : :
153 : : name = q = p;
154 : : val = NULL;
155 : : /* From now on, be well-formed or you're gone. */
156 : 0 : for (;;)
157 : 0 : if (*q == ':' || *q == '\0')
158 : : {
159 : 0 : struct color_cap *cap;
160 : :
161 : 0 : if (val)
162 : 0 : val_len = q - val;
163 : : else
164 : 0 : name_len = q - name;
165 : : /* Empty name without val (empty cap)
166 : : won't match and will be ignored. */
167 : 0 : for (cap = color_dict; cap->name; cap++)
168 : 0 : if (cap->name_len == name_len
169 : 0 : && memcmp (cap->name, name, name_len) == 0)
170 : : break;
171 : : /* If name unknown, go on for forward compatibility. */
172 : 0 : if (cap->val && val)
173 : : {
174 : 0 : if (cap->free_val)
175 : 0 : free (CONST_CAST (char *, cap->val));
176 : 0 : b = XNEWVEC (char, val_len + sizeof (SGR_SEQ ("")));
177 : 0 : memcpy (b, SGR_START, strlen (SGR_START));
178 : 0 : memcpy (b + strlen (SGR_START), val, val_len);
179 : 0 : memcpy (b + strlen (SGR_START) + val_len, SGR_END,
180 : : sizeof (SGR_END));
181 : 0 : cap->val = (const char *) b;
182 : 0 : cap->free_val = true;
183 : : }
184 : 0 : if (*q == '\0')
185 : : return true;
186 : 0 : name = ++q;
187 : 0 : val = NULL;
188 : 0 : }
189 : 0 : else if (*q == '=')
190 : : {
191 : 0 : if (q == name || val)
192 : : return true;
193 : :
194 : 0 : name_len = q - name;
195 : 0 : val = ++q; /* Can be the empty string. */
196 : : }
197 : 0 : else if (val == NULL)
198 : 0 : q++; /* Accumulate name. */
199 : 0 : else if (*q == ';' || (*q >= '0' && *q <= '9'))
200 : 0 : q++; /* Accumulate val. Protect the terminal from being sent
201 : : garbage. */
202 : : else
203 : : return true;
204 : : }
205 : :
206 : : /* Return true if we should use color when in auto mode, false otherwise. */
207 : : static bool
208 : 1182650 : should_colorize (void)
209 : : {
210 : : #ifdef __MINGW32__
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... */
216 : : HANDLE h;
217 : : DWORD m;
218 : :
219 : : h = GetStdHandle (STD_ERROR_HANDLE);
220 : : return (h != INVALID_HANDLE_VALUE) && (h != NULL)
221 : : && GetConsoleMode (h, &m);
222 : : #else
223 : 1182650 : char const *t = getenv ("TERM");
224 : : /* emacs M-x shell sets TERM="dumb". */
225 : 1182650 : return t && strcmp (t, "dumb") != 0 && isatty (STDERR_FILENO);
226 : : #endif
227 : : }
228 : :
229 : : bool
230 : 1387715 : colorize_init (diagnostic_color_rule_t rule)
231 : : {
232 : 1387715 : switch (rule)
233 : : {
234 : : case DIAGNOSTICS_COLOR_NO:
235 : : return false;
236 : 91 : case DIAGNOSTICS_COLOR_YES:
237 : 91 : return parse_gcc_colors ();
238 : 591325 : case DIAGNOSTICS_COLOR_AUTO:
239 : 591325 : if (should_colorize ())
240 : 495608 : return parse_gcc_colors ();
241 : : else
242 : : return false;
243 : 0 : default:
244 : 0 : gcc_unreachable ();
245 : : }
246 : : }
247 : :
248 : : /* Return URL_FORMAT_XXX which tells how we should emit urls
249 : : when in always mode.
250 : : We use GCC_URLS and if that is not defined TERM_URLS.
251 : : If neither is defined the feature is enabled by default. */
252 : :
253 : : static diagnostic_url_format
254 : 0 : parse_env_vars_for_urls ()
255 : : {
256 : 0 : const char *p;
257 : :
258 : 0 : p = getenv ("GCC_URLS"); /* Plural! */
259 : 0 : if (p == NULL)
260 : 0 : p = getenv ("TERM_URLS");
261 : :
262 : 0 : if (p == NULL)
263 : : return URL_FORMAT_DEFAULT;
264 : :
265 : 0 : if (*p == '\0')
266 : : return URL_FORMAT_NONE;
267 : :
268 : 0 : if (!strcmp (p, "no"))
269 : : return URL_FORMAT_NONE;
270 : :
271 : 0 : if (!strcmp (p, "st"))
272 : : return URL_FORMAT_ST;
273 : :
274 : : if (!strcmp (p, "bel"))
275 : : return URL_FORMAT_BEL;
276 : :
277 : : return URL_FORMAT_DEFAULT;
278 : : }
279 : :
280 : : /* Return true if we should use urls when in auto mode, false otherwise. */
281 : :
282 : : static bool
283 : 591325 : auto_enable_urls ()
284 : : {
285 : : #ifdef __MINGW32__
286 : : return false;
287 : : #else
288 : 591325 : const char *term, *colorterm;
289 : :
290 : : /* First check the terminal is capable of printing color escapes,
291 : : if not URLs won't work either. */
292 : 591325 : if (!should_colorize ())
293 : : return false;
294 : :
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 : 495608 : colorterm = getenv ("COLORTERM");
301 : 495608 : if (colorterm && !strcmp (colorterm, "xfce4-terminal"))
302 : : return false;
303 : :
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 : 0 : if (colorterm && !strcmp (colorterm, "gnome-terminal"))
308 : : return false;
309 : :
310 : : /* Since the following checks are less specific than the ones
311 : : above, let GCC_URLS and TERM_URLS override the decision. */
312 : 495608 : if (getenv ("GCC_URLS") || getenv ("TERM_URLS"))
313 : 0 : return true;
314 : :
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 : 495608 : term = getenv ("TERM");
319 : 495608 : if (!colorterm && term && !strcmp (term, "xterm"))
320 : : return false;
321 : :
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 : 0 : if (!colorterm && term && !strcmp (term, "linux"))
326 : : return false;
327 : :
328 : : return true;
329 : : #endif
330 : : }
331 : :
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. */
335 : :
336 : : diagnostic_url_format
337 : 1452394 : determine_url_format (diagnostic_url_rule_t rule)
338 : : {
339 : 1452394 : switch (rule)
340 : : {
341 : : case DIAGNOSTICS_URL_NO:
342 : : return URL_FORMAT_NONE;
343 : 0 : case DIAGNOSTICS_URL_YES:
344 : 0 : return parse_env_vars_for_urls ();
345 : 591325 : case DIAGNOSTICS_URL_AUTO:
346 : 591325 : if (auto_enable_urls ())
347 : 0 : return parse_env_vars_for_urls ();
348 : : else
349 : : return URL_FORMAT_NONE;
350 : 0 : default:
351 : 0 : gcc_unreachable ();
352 : : }
353 : : }
|