1 /* Output colorization.
2    Copyright (C) 2011-2018 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 
23 #ifdef __MINGW32__
24 #  include <windows.h>
25 #endif
26 
27 #include "color-macros.h"
28 
29 /* The context and logic for choosing default --color screen attributes
30    (foreground and background colors, etc.) are the following.
31       -- There are eight basic colors available, each with its own
32 	 nominal luminosity to the human eye and foreground/background
33 	 codes (black [0 %, 30/40], blue [11 %, 34/44], red [30 %, 31/41],
34 	 magenta [41 %, 35/45], green [59 %, 32/42], cyan [70 %, 36/46],
35 	 yellow [89 %, 33/43], and white [100 %, 37/47]).
36       -- Sometimes, white as a background is actually implemented using
37 	 a shade of light gray, so that a foreground white can be visible
38 	 on top of it (but most often not).
39       -- Sometimes, black as a foreground is actually implemented using
40 	 a shade of dark gray, so that it can be visible on top of a
41 	 background black (but most often not).
42       -- Sometimes, more colors are available, as extensions.
43       -- Other attributes can be selected/deselected (bold [1/22],
44 	 underline [4/24], standout/inverse [7/27], blink [5/25], and
45 	 invisible/hidden [8/28]).  They are sometimes implemented by
46 	 using colors instead of what their names imply; e.g., bold is
47 	 often achieved by using brighter colors.  In practice, only bold
48 	 is really available to us, underline sometimes being mapped by
49 	 the terminal to some strange color choice, and standout best
50 	 being left for use by downstream programs such as less(1).
51       -- We cannot assume that any of the extensions or special features
52 	 are available for the purpose of choosing defaults for everyone.
53       -- The most prevalent default terminal backgrounds are pure black
54 	 and pure white, and are not necessarily the same shades of
55 	 those as if they were selected explicitly with SGR sequences.
56 	 Some terminals use dark or light pictures as default background,
57 	 but those are covered over by an explicit selection of background
58 	 color with an SGR sequence; their users will appreciate their
59 	 background pictures not be covered like this, if possible.
60       -- Some uses of colors attributes is to make some output items
61 	 more understated (e.g., context lines); this cannot be achieved
62 	 by changing the background color.
63       -- For these reasons, the GCC color defaults should strive not
64 	 to change the background color from its default, unless it's
65 	 for a short item that should be highlighted, not understated.
66       -- The GCC foreground color defaults (without an explicitly set
67 	 background) should provide enough contrast to be readable on any
68 	 terminal with either a black (dark) or white (light) background.
69 	 This only leaves red, magenta, green, and cyan (and their bold
70 	 counterparts) and possibly bold blue.  */
71 /* Default colors. The user can overwrite them using environment
72    variable GCC_COLORS.  */
73 struct color_cap
74 {
75   const char *name;
76   const char *val;
77   unsigned char name_len;
78   bool free_val;
79 };
80 
81 /* For GCC_COLORS.  */
82 static struct color_cap color_dict[] =
83 {
84   { "error", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_RED), 5, false },
85   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
86 	       7, false },
87   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
88   { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
89   { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
90   { "locus", SGR_SEQ (COLOR_BOLD), 5, false },
91   { "quote", SGR_SEQ (COLOR_BOLD), 5, false },
92   { "fixit-insert", SGR_SEQ (COLOR_FG_GREEN), 12, false },
93   { "fixit-delete", SGR_SEQ (COLOR_FG_RED), 12, false },
94   { "diff-filename", SGR_SEQ (COLOR_BOLD), 13, false },
95   { "diff-hunk", SGR_SEQ (COLOR_FG_CYAN), 9, false },
96   { "diff-delete", SGR_SEQ (COLOR_FG_RED), 11, false },
97   { "diff-insert", SGR_SEQ (COLOR_FG_GREEN), 11, false },
98   { "type-diff", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 9, false },
99   { NULL, NULL, 0, false }
100 };
101 
102 const char *
colorize_start(bool show_color,const char * name,size_t name_len)103 colorize_start (bool show_color, const char *name, size_t name_len)
104 {
105   struct color_cap const *cap;
106 
107   if (!show_color)
108     return "";
109 
110   for (cap = color_dict; cap->name; cap++)
111     if (cap->name_len == name_len
112 	&& memcmp (cap->name, name, name_len) == 0)
113       break;
114   if (cap->name == NULL)
115     return "";
116 
117   return cap->val;
118 }
119 
120 const char *
colorize_stop(bool show_color)121 colorize_stop (bool show_color)
122 {
123   return show_color ? SGR_RESET : "";
124 }
125 
126 /* Parse GCC_COLORS.  The default would look like:
127    GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
128    range1=32:range2=34:locus=01:quote=01:\
129    fixit-insert=32:fixit-delete=31:'\
130    diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
131    type-diff=01;32'
132    No character escaping is needed or supported.  */
133 static bool
parse_gcc_colors(void)134 parse_gcc_colors (void)
135 {
136   const char *p, *q, *name, *val;
137   char *b;
138   size_t name_len = 0, val_len = 0;
139 
140   p = getenv ("GCC_COLORS"); /* Plural! */
141   if (p == NULL)
142     return true;
143   if (*p == '\0')
144     return false;
145 
146   name = q = p;
147   val = NULL;
148   /* From now on, be well-formed or you're gone.  */
149   for (;;)
150     if (*q == ':' || *q == '\0')
151       {
152 	struct color_cap *cap;
153 
154 	if (val)
155 	  val_len = q - val;
156 	else
157 	  name_len = q - name;
158 	/* Empty name without val (empty cap)
159 	   won't match and will be ignored.  */
160 	for (cap = color_dict; cap->name; cap++)
161 	  if (cap->name_len == name_len
162 	      && memcmp (cap->name, name, name_len) == 0)
163 	    break;
164 	/* If name unknown, go on for forward compatibility.  */
165 	if (cap->val && val)
166 	  {
167 	    if (cap->free_val)
168 	      free (CONST_CAST (char *, cap->val));
169 	    b = XNEWVEC (char, val_len + sizeof (SGR_SEQ ("")));
170 	    memcpy (b, SGR_START, strlen (SGR_START));
171 	    memcpy (b + strlen (SGR_START), val, val_len);
172 	    memcpy (b + strlen (SGR_START) + val_len, SGR_END,
173 		    sizeof (SGR_END));
174 	    cap->val = (const char *) b;
175 	    cap->free_val = true;
176 	  }
177 	if (*q == '\0')
178 	  return true;
179 	name = ++q;
180 	val = NULL;
181       }
182     else if (*q == '=')
183       {
184 	if (q == name || val)
185 	  return true;
186 
187 	name_len = q - name;
188 	val = ++q; /* Can be the empty string.  */
189       }
190     else if (val == NULL)
191       q++; /* Accumulate name.  */
192     else if (*q == ';' || (*q >= '0' && *q <= '9'))
193       q++; /* Accumulate val.  Protect the terminal from being sent
194 	      garbage.  */
195     else
196       return true;
197 }
198 
199 /* Return true if we should use color when in auto mode, false otherwise. */
200 static bool
should_colorize(void)201 should_colorize (void)
202 {
203 #ifdef __MINGW32__
204   /* For consistency reasons, one should check the handle returned by
205      _get_osfhandle(_fileno(stderr)) because the function
206      pp_write_text_to_stream() in pretty-print.c calls fputs() on
207      that stream.  However, the code below for non-Windows doesn't seem
208      to care about it either...  */
209   HANDLE h;
210   DWORD m;
211 
212   h = GetStdHandle (STD_ERROR_HANDLE);
213   return (h != INVALID_HANDLE_VALUE) && (h != NULL)
214 	  && GetConsoleMode (h, &m);
215 #else
216   char const *t = getenv ("TERM");
217   return t && strcmp (t, "dumb") != 0 && isatty (STDERR_FILENO);
218 #endif
219 }
220 
221 bool
colorize_init(diagnostic_color_rule_t rule)222 colorize_init (diagnostic_color_rule_t rule)
223 {
224   switch (rule)
225     {
226     case DIAGNOSTICS_COLOR_NO:
227       return false;
228     case DIAGNOSTICS_COLOR_YES:
229       return parse_gcc_colors ();
230     case DIAGNOSTICS_COLOR_AUTO:
231       if (should_colorize ())
232 	return parse_gcc_colors ();
233       else
234 	return false;
235     default:
236       gcc_unreachable ();
237     }
238 }
239