1/* Output colorization.
2 Copyright (C) 2011-2017 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. */
73struct 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. */
82static 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
102const char *
103colorize_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
120const char *
121colorize_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. */
133static bool
134parse_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. */
200static bool
201should_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
221bool
222colorize_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