1 /* Output colorization.
2    Copyright (C) 2011-2021 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 #  include <windows.h>
26 #endif
27 
28 #include "color-macros.h"
29 
30 /* The context and logic for choosing default --color screen attributes
31    (foreground and background colors, etc.) are the following.
32       -- There are eight basic colors available, each with its own
33 	 nominal luminosity to the human eye and foreground/background
34 	 codes (black [0 %, 30/40], blue [11 %, 34/44], red [30 %, 31/41],
35 	 magenta [41 %, 35/45], green [59 %, 32/42], cyan [70 %, 36/46],
36 	 yellow [89 %, 33/43], and white [100 %, 37/47]).
37       -- Sometimes, white as a background is actually implemented using
38 	 a shade of light gray, so that a foreground white can be visible
39 	 on top of it (but most often not).
40       -- Sometimes, black as a foreground is actually implemented using
41 	 a shade of dark gray, so that it can be visible on top of a
42 	 background black (but most often not).
43       -- Sometimes, more colors are available, as extensions.
44       -- Other attributes can be selected/deselected (bold [1/22],
45 	 underline [4/24], standout/inverse [7/27], blink [5/25], and
46 	 invisible/hidden [8/28]).  They are sometimes implemented by
47 	 using colors instead of what their names imply; e.g., bold is
48 	 often achieved by using brighter colors.  In practice, only bold
49 	 is really available to us, underline sometimes being mapped by
50 	 the terminal to some strange color choice, and standout best
51 	 being left for use by downstream programs such as less(1).
52       -- We cannot assume that any of the extensions or special features
53 	 are available for the purpose of choosing defaults for everyone.
54       -- The most prevalent default terminal backgrounds are pure black
55 	 and pure white, and are not necessarily the same shades of
56 	 those as if they were selected explicitly with SGR sequences.
57 	 Some terminals use dark or light pictures as default background,
58 	 but those are covered over by an explicit selection of background
59 	 color with an SGR sequence; their users will appreciate their
60 	 background pictures not be covered like this, if possible.
61       -- Some uses of colors attributes is to make some output items
62 	 more understated (e.g., context lines); this cannot be achieved
63 	 by changing the background color.
64       -- For these reasons, the GCC color defaults should strive not
65 	 to change the background color from its default, unless it's
66 	 for a short item that should be highlighted, not understated.
67       -- The GCC foreground color defaults (without an explicitly set
68 	 background) should provide enough contrast to be readable on any
69 	 terminal with either a black (dark) or white (light) background.
70 	 This only leaves red, magenta, green, and cyan (and their bold
71 	 counterparts) and possibly bold blue.  */
72 /* Default colors. The user can overwrite them using environment
73    variable GCC_COLORS.  */
74 struct color_cap
75 {
76   const char *name;
77   const char *val;
78   unsigned char name_len;
79   bool free_val;
80 };
81 
82 /* For GCC_COLORS.  */
83 static struct color_cap color_dict[] =
84 {
85   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
86   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
87 	       7, false },
88   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
89   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
90   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
91   { "locus", SGR_SEQ (COLOR_BOLD), 5, false },
92   { "quote", SGR_SEQ (COLOR_BOLD), 5, false },
93   { "path", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
94   { "fixit-insert", SGR_SEQ (COLOR_FG_GREEN), 12, false },
95   { "fixit-delete", SGR_SEQ (COLOR_FG_RED), 12, false },
96   { "diff-filename", SGR_SEQ (COLOR_BOLD), 13, false },
97   { "diff-hunk", SGR_SEQ (COLOR_FG_CYAN), 9, false },
98   { "diff-delete", SGR_SEQ (COLOR_FG_RED), 11, false },
99   { "diff-insert", SGR_SEQ (COLOR_FG_GREEN), 11, false },
100   { "type-diff", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 9, false },
101   { NULL, NULL, 0, false }
102 };
103 
104 const char *
colorize_start(bool show_color,const char * name,size_t name_len)105 colorize_start (bool show_color, const char *name, size_t name_len)
106 {
107   struct color_cap const *cap;
108 
109   if (!show_color)
110     return "";
111 
112   for (cap = color_dict; cap->name; cap++)
113     if (cap->name_len == name_len
114 	&& memcmp (cap->name, name, name_len) == 0)
115       break;
116   if (cap->name == NULL)
117     return "";
118 
119   return cap->val;
120 }
121 
122 const char *
colorize_stop(bool show_color)123 colorize_stop (bool show_color)
124 {
125   return show_color ? SGR_RESET : "";
126 }
127 
128 /* Parse GCC_COLORS.  The default would look like:
129    GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
130    range1=32:range2=34:locus=01:quote=01:path=01;36:\
131    fixit-insert=32:fixit-delete=31:'\
132    diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
133    type-diff=01;32'
134    No character escaping is needed or supported.  */
135 static bool
parse_gcc_colors(void)136 parse_gcc_colors (void)
137 {
138   const char *p, *q, *name, *val;
139   char *b;
140   size_t name_len = 0, val_len = 0;
141 
142   p = getenv ("GCC_COLORS"); /* Plural! */
143   if (p == NULL)
144     return true;
145   if (*p == '\0')
146     return false;
147 
148   name = q = p;
149   val = NULL;
150   /* From now on, be well-formed or you're gone.  */
151   for (;;)
152     if (*q == ':' || *q == '\0')
153       {
154 	struct color_cap *cap;
155 
156 	if (val)
157 	  val_len = q - val;
158 	else
159 	  name_len = q - name;
160 	/* Empty name without val (empty cap)
161 	   won't match and will be ignored.  */
162 	for (cap = color_dict; cap->name; cap++)
163 	  if (cap->name_len == name_len
164 	      && memcmp (cap->name, name, name_len) == 0)
165 	    break;
166 	/* If name unknown, go on for forward compatibility.  */
167 	if (cap->val && val)
168 	  {
169 	    if (cap->free_val)
170 	      free (CONST_CAST (char *, cap->val));
171 	    b = XNEWVEC (char, val_len + sizeof (SGR_SEQ ("")));
172 	    memcpy (b, SGR_START, strlen (SGR_START));
173 	    memcpy (b + strlen (SGR_START), val, val_len);
174 	    memcpy (b + strlen (SGR_START) + val_len, SGR_END,
175 		    sizeof (SGR_END));
176 	    cap->val = (const char *) b;
177 	    cap->free_val = true;
178 	  }
179 	if (*q == '\0')
180 	  return true;
181 	name = ++q;
182 	val = NULL;
183       }
184     else if (*q == '=')
185       {
186 	if (q == name || val)
187 	  return true;
188 
189 	name_len = q - name;
190 	val = ++q; /* Can be the empty string.  */
191       }
192     else if (val == NULL)
193       q++; /* Accumulate name.  */
194     else if (*q == ';' || (*q >= '0' && *q <= '9'))
195       q++; /* Accumulate val.  Protect the terminal from being sent
196 	      garbage.  */
197     else
198       return true;
199 }
200 
201 /* Return true if we should use color when in auto mode, false otherwise. */
202 static bool
should_colorize(void)203 should_colorize (void)
204 {
205 #ifdef __MINGW32__
206   /* For consistency reasons, one should check the handle returned by
207      _get_osfhandle(_fileno(stderr)) because the function
208      pp_write_text_to_stream() in pretty-print.c calls fputs() on
209      that stream.  However, the code below for non-Windows doesn't seem
210      to care about it either...  */
211   HANDLE h;
212   DWORD m;
213 
214   h = GetStdHandle (STD_ERROR_HANDLE);
215   return (h != INVALID_HANDLE_VALUE) && (h != NULL)
216 	  && GetConsoleMode (h, &m);
217 #else
218   char const *t = getenv ("TERM");
219   /* emacs M-x shell sets TERM="dumb".  */
220   return t && strcmp (t, "dumb") != 0 && isatty (STDERR_FILENO);
221 #endif
222 }
223 
224 bool
colorize_init(diagnostic_color_rule_t rule)225 colorize_init (diagnostic_color_rule_t rule)
226 {
227   switch (rule)
228     {
229     case DIAGNOSTICS_COLOR_NO:
230       return false;
231     case DIAGNOSTICS_COLOR_YES:
232       return parse_gcc_colors ();
233     case DIAGNOSTICS_COLOR_AUTO:
234       if (should_colorize ())
235 	return parse_gcc_colors ();
236       else
237 	return false;
238     default:
239       gcc_unreachable ();
240     }
241 }
242 
243 /* Return URL_FORMAT_XXX which tells how we should emit urls
244    when in always mode.
245    We use GCC_URLS and if that is not defined TERM_URLS.
246    If neither is defined the feature is enabled by default.  */
247 
248 static diagnostic_url_format
parse_env_vars_for_urls()249 parse_env_vars_for_urls ()
250 {
251   const char *p;
252 
253   p = getenv ("GCC_URLS"); /* Plural! */
254   if (p == NULL)
255     p = getenv ("TERM_URLS");
256 
257   if (p == NULL)
258     return URL_FORMAT_DEFAULT;
259 
260   if (*p == '\0')
261     return URL_FORMAT_NONE;
262 
263   if (!strcmp (p, "no"))
264     return URL_FORMAT_NONE;
265 
266   if (!strcmp (p, "st"))
267     return URL_FORMAT_ST;
268 
269   if (!strcmp (p, "bel"))
270     return URL_FORMAT_BEL;
271 
272   return URL_FORMAT_DEFAULT;
273 }
274 
275 /* Return true if we should use urls when in auto mode, false otherwise.  */
276 
277 static bool
auto_enable_urls()278 auto_enable_urls ()
279 {
280 #ifdef __MINGW32__
281   return false;
282 #else
283   const char *term, *colorterm;
284 
285   /* First check the terminal is capable of printing color escapes,
286      if not URLs won't work either.  */
287   if (!should_colorize ())
288     return false;
289 
290   /* xfce4-terminal is known to not implement URLs at this time.
291      Recently new installations (0.8) will safely ignore the URL escape
292      sequences, but a large number of legacy installations (0.6.3) print
293      garbage when URLs are printed.  Therefore we lose nothing by
294      disabling this feature for that specific terminal type.  */
295   colorterm = getenv ("COLORTERM");
296   if (colorterm && !strcmp (colorterm, "xfce4-terminal"))
297     return false;
298 
299   /* Old versions of gnome-terminal where URL escapes cause screen
300      corruptions set COLORTERM="gnome-terminal", recent versions
301      with working URL support set this to "truecolor".  */
302   if (colorterm && !strcmp (colorterm, "gnome-terminal"))
303     return false;
304 
305   /* Since the following checks are less specific than the ones
306      above, let GCC_URLS and TERM_URLS override the decision.  */
307   if (getenv ("GCC_URLS") || getenv ("TERM_URLS"))
308     return true;
309 
310   /* In an ssh session the COLORTERM is not there, but TERM=xterm
311      can be used as an indication of a incompatible terminal while
312      TERM=xterm-256color appears to be a working terminal.  */
313   term = getenv ("TERM");
314   if (!colorterm && term && !strcmp (term, "xterm"))
315     return false;
316 
317   /* When logging in a linux over serial line, we see TERM=linux
318      and no COLORTERM, it is unlikely that the URL escapes will
319      work in that environmen either.  */
320   if (!colorterm && term && !strcmp (term, "linux"))
321     return false;
322 
323   return true;
324 #endif
325 }
326 
327 /* Determine if URLs should be enabled, based on RULE,
328    and, if so, which format to use.
329    This reuses the logic for colorization.  */
330 
331 diagnostic_url_format
determine_url_format(diagnostic_url_rule_t rule)332 determine_url_format (diagnostic_url_rule_t rule)
333 {
334   switch (rule)
335     {
336     case DIAGNOSTICS_URL_NO:
337       return URL_FORMAT_NONE;
338     case DIAGNOSTICS_URL_YES:
339       return parse_env_vars_for_urls ();
340     case DIAGNOSTICS_URL_AUTO:
341       if (auto_enable_urls ())
342 	return parse_env_vars_for_urls ();
343       else
344 	return URL_FORMAT_NONE;
345     default:
346       gcc_unreachable ();
347     }
348 }
349