]>
Commit | Line | Data |
---|---|---|
f3065bdb | 1 | /* Output colorization. |
a945c346 | 2 | Copyright (C) 2011-2024 Free Software Foundation, Inc. |
f3065bdb JJ |
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" | |
d2608235 | 22 | #include "diagnostic-url.h" |
f3065bdb | 23 | |
db0d1bae | 24 | #ifdef __MINGW32__ |
902c7559 | 25 | # define WIN32_LEAN_AND_MEAN |
db0d1bae LH |
26 | # include <windows.h> |
27 | #endif | |
28 | ||
28f4a4a8 | 29 | #include "color-macros.h" |
f3065bdb JJ |
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 }, | |
8a645150 DM |
90 | { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false }, |
91 | { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false }, | |
f3065bdb JJ |
92 | { "locus", SGR_SEQ (COLOR_BOLD), 5, false }, |
93 | { "quote", SGR_SEQ (COLOR_BOLD), 5, false }, | |
4bc1899b | 94 | { "path", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false }, |
69dd5ca3 JM |
95 | { "fnname", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 6, false }, |
96 | { "targs", SGR_SEQ (COLOR_FG_MAGENTA), 5, false }, | |
d41e76cf DM |
97 | { "fixit-insert", SGR_SEQ (COLOR_FG_GREEN), 12, false }, |
98 | { "fixit-delete", SGR_SEQ (COLOR_FG_RED), 12, false }, | |
c65236d6 DM |
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 }, | |
f012c8ef | 103 | { "type-diff", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 9, false }, |
4b02dd48 DM |
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 }, | |
f3065bdb JJ |
106 | { NULL, NULL, 0, false } |
107 | }; | |
108 | ||
109 | const char * | |
110 | colorize_start (bool show_color, const char *name, size_t name_len) | |
111 | { | |
112 | struct color_cap const *cap; | |
113 | ||
114 | if (!show_color) | |
115 | return ""; | |
116 | ||
117 | for (cap = color_dict; cap->name; cap++) | |
118 | if (cap->name_len == name_len | |
119 | && memcmp (cap->name, name, name_len) == 0) | |
120 | break; | |
121 | if (cap->name == NULL) | |
122 | return ""; | |
123 | ||
124 | return cap->val; | |
125 | } | |
126 | ||
127 | const char * | |
128 | colorize_stop (bool show_color) | |
129 | { | |
130 | return show_color ? SGR_RESET : ""; | |
131 | } | |
132 | ||
133 | /* Parse GCC_COLORS. The default would look like: | |
d41e76cf | 134 | GCC_COLORS='error=01;31:warning=01;35:note=01;36:\ |
4bc1899b | 135 | range1=32:range2=34:locus=01:quote=01:path=01;36:\ |
f012c8ef DM |
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' | |
f3065bdb JJ |
139 | No character escaping is needed or supported. */ |
140 | static bool | |
141 | parse_gcc_colors (void) | |
142 | { | |
143 | const char *p, *q, *name, *val; | |
144 | char *b; | |
145 | size_t name_len = 0, val_len = 0; | |
146 | ||
147 | p = getenv ("GCC_COLORS"); /* Plural! */ | |
148 | if (p == NULL) | |
149 | return true; | |
150 | 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 | for (;;) | |
157 | if (*q == ':' || *q == '\0') | |
158 | { | |
159 | struct color_cap *cap; | |
160 | ||
161 | if (val) | |
162 | val_len = q - val; | |
163 | else | |
164 | name_len = q - name; | |
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) | |
170 | break; | |
171 | /* If name unknown, go on for forward compatibility. */ | |
172 | if (cap->val && val) | |
173 | { | |
174 | if (cap->free_val) | |
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, | |
180 | sizeof (SGR_END)); | |
181 | cap->val = (const char *) b; | |
182 | cap->free_val = true; | |
183 | } | |
184 | if (*q == '\0') | |
185 | return true; | |
186 | name = ++q; | |
187 | val = NULL; | |
188 | } | |
189 | else if (*q == '=') | |
190 | { | |
191 | if (q == name || val) | |
192 | return true; | |
193 | ||
194 | name_len = q - name; | |
195 | val = ++q; /* Can be the empty string. */ | |
196 | } | |
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 | |
201 | garbage. */ | |
202 | else | |
203 | return true; | |
204 | } | |
205 | ||
f3065bdb JJ |
206 | /* Return true if we should use color when in auto mode, false otherwise. */ |
207 | static bool | |
208 | should_colorize (void) | |
209 | { | |
db0d1bae LH |
210 | #ifdef __MINGW32__ |
211 | /* For consistency reasons, one should check the handle returned by | |
212 | _get_osfhandle(_fileno(stderr)) because the function | |
e53b6e56 | 213 | pp_write_text_to_stream() in pretty-print.cc calls fputs() on |
db0d1bae LH |
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 | |
f3065bdb | 223 | char const *t = getenv ("TERM"); |
458c8d64 | 224 | /* emacs M-x shell sets TERM="dumb". */ |
f3065bdb | 225 | return t && strcmp (t, "dumb") != 0 && isatty (STDERR_FILENO); |
db0d1bae | 226 | #endif |
f3065bdb JJ |
227 | } |
228 | ||
f3065bdb JJ |
229 | bool |
230 | colorize_init (diagnostic_color_rule_t rule) | |
231 | { | |
232 | switch (rule) | |
233 | { | |
234 | case DIAGNOSTICS_COLOR_NO: | |
235 | return false; | |
236 | case DIAGNOSTICS_COLOR_YES: | |
237 | return parse_gcc_colors (); | |
238 | case DIAGNOSTICS_COLOR_AUTO: | |
239 | if (should_colorize ()) | |
240 | return parse_gcc_colors (); | |
241 | else | |
242 | return false; | |
243 | default: | |
244 | gcc_unreachable (); | |
245 | } | |
246 | } | |
d2608235 | 247 | |
458c8d64 BE |
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 | parse_env_vars_for_urls () | |
255 | { | |
256 | const char *p; | |
257 | ||
258 | p = getenv ("GCC_URLS"); /* Plural! */ | |
259 | if (p == NULL) | |
260 | p = getenv ("TERM_URLS"); | |
261 | ||
262 | if (p == NULL) | |
263 | return URL_FORMAT_DEFAULT; | |
264 | ||
265 | if (*p == '\0') | |
266 | return URL_FORMAT_NONE; | |
267 | ||
268 | if (!strcmp (p, "no")) | |
269 | return URL_FORMAT_NONE; | |
270 | ||
271 | 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 | auto_enable_urls () | |
284 | { | |
285 | #ifdef __MINGW32__ | |
286 | return false; | |
287 | #else | |
288 | 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 | 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 | colorterm = getenv ("COLORTERM"); | |
301 | 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 | 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 | if (getenv ("GCC_URLS") || getenv ("TERM_URLS")) | |
313 | 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 | term = getenv ("TERM"); | |
319 | 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 | 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. | |
d2608235 DM |
334 | This reuses the logic for colorization. */ |
335 | ||
458c8d64 BE |
336 | diagnostic_url_format |
337 | determine_url_format (diagnostic_url_rule_t rule) | |
d2608235 DM |
338 | { |
339 | switch (rule) | |
340 | { | |
341 | case DIAGNOSTICS_URL_NO: | |
458c8d64 | 342 | return URL_FORMAT_NONE; |
d2608235 | 343 | case DIAGNOSTICS_URL_YES: |
458c8d64 | 344 | return parse_env_vars_for_urls (); |
d2608235 | 345 | case DIAGNOSTICS_URL_AUTO: |
458c8d64 BE |
346 | if (auto_enable_urls ()) |
347 | return parse_env_vars_for_urls (); | |
348 | else | |
349 | return URL_FORMAT_NONE; | |
d2608235 DM |
350 | default: |
351 | gcc_unreachable (); | |
352 | } | |
353 | } |