diagnostic-color.c revision 1.5
1/* Output colorization.
2   Copyright (C) 2011-2019 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