On 09/10/17 23:25, Manuel López-Ibáñez wrote:
Even if the host-specific part is not done, I honestly think it is a good idea to match grep's code as much as possible since we may want to merge bugfixes between the two and eventually this code may end up in gnulib. Moreover, if somebody else implemented color output for another OS in grep, it would be very easy to transplant it to GCC (or viceversa) if the API remains close.

Something like the attached should do the trick (I didn't even try to compile it and completely untested, so it may need some adjustments).

Cheers,

Manuel.
Index: diagnostic-color.c
===================================================================
--- diagnostic-color.c	(revision 253569)
+++ diagnostic-color.c	(working copy)
@@ -19,6 +19,12 @@
 #include "config.h"
 #include "system.h"
 #include "diagnostic-color.h"
+#ifdef __MINGW32__
+#  undef DATADIR /* conflicts with objidl.h, which is included by windows.h */
+#  include <windows.h>
+static HANDLE hstderr = INVALID_HANDLE_VALUE;
+static SHORT norm_attr;
+#endif
 
 /* Select Graphic Rendition (SGR, "\33[...m") strings.  */
 /* Also Erase in Line (EL) to Right ("\33[K") by default.  */
@@ -104,7 +110,125 @@
 #define SGR_SEQ(str)		SGR_START str SGR_END
 #define SGR_RESET		SGR_SEQ("")
 
+#ifdef __MINGW32__
+/* Convert a color spec, a semi-colon separated list of the form
+   SGR_START"NN;MM;KK;..."SGR_END, where each number is a value of the SGR
+   parameter, into the corresponding Windows console text attribute.
 
+   This function supports a subset of the SGR rendition aspects that
+   the Windows console can display.  */
+static int
+w32_sgr2attr (const char *sgr_seq)
+{
+  const char *s, *p;
+  int code, fg = norm_attr & 15, bg = norm_attr & (15 << 4);
+  int bright = 0, inverse = 0;
+  static const int fg_color[] = {
+    0,			/* black */
+    FOREGROUND_RED,	/* red */
+    FOREGROUND_GREEN,	/* green */
+    FOREGROUND_GREEN | FOREGROUND_RED, /* yellow */
+    FOREGROUND_BLUE,		       /* blue */
+    FOREGROUND_BLUE | FOREGROUND_RED,  /* magenta */
+    FOREGROUND_BLUE | FOREGROUND_GREEN, /* cyan */
+    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE /* gray */
+  };
+  static const int bg_color[] = {
+    0,			/* black */
+    BACKGROUND_RED,	/* red */
+    BACKGROUND_GREEN,	/* green */
+    BACKGROUND_GREEN | BACKGROUND_RED, /* yellow */
+    BACKGROUND_BLUE,		       /* blue */
+    BACKGROUND_BLUE | BACKGROUND_RED,  /* magenta */
+    BACKGROUND_BLUE | BACKGROUND_GREEN, /* cyan */
+    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE /* gray */
+  };
+
+  sgr_seq = sqr_seq + strlen(SGR_START);
+  
+  for (s = p = sgr_seq; strcmp(s, SGR_END) != 0; p++)
+    {
+      if (*p == ';' || strcmp(p, SGR_END) == 0)
+        {
+          code = strtol (s, NULL, 10);
+          s = p + (strcmp(p, SGR_END) != 0);
+
+          switch (code)
+            {
+            case 0:	/* all attributes off */
+              fg = norm_attr & 15;
+              bg = norm_attr & (15 << 4);
+              bright = 0;
+              inverse = 0;
+              break;
+            case 1:	/* intensity on */
+              bright = 1;
+              break;
+            case 7:	/* inverse video */
+              inverse = 1;
+              break;
+            case 22:	/* intensity off */
+              bright = 0;
+              break;
+            case 27:	/* inverse off */
+              inverse = 0;
+              break;
+            case 30: case 31: case 32: case 33: /* foreground color */
+            case 34: case 35: case 36: case 37:
+              fg = fg_color[code - 30];
+              break;
+            case 39:	/* default foreground */
+              fg = norm_attr & 15;
+              break;
+            case 40: case 41: case 42: case 43: /* background color */
+            case 44: case 45: case 46: case 47:
+              bg = bg_color[code - 40];
+              break;
+            case 49:	/* default background */
+              bg = norm_attr & (15 << 4);
+              break;
+            default:
+              break;
+            }
+        }
+    }
+  if (inverse)
+    {
+      int t = fg;
+      fg = (bg >> 4);
+      bg = (t << 4);
+    }
+  if (bright)
+    fg |= FOREGROUND_INTENSITY;
+
+  return (bg & (15 << 4)) | (fg & 15);
+}
+
+/* Clear to the end of the current line with the default attribute.
+   This is needed for reasons similar to those that require the "EL to
+   Right after SGR" operation on Posix platforms: if we don't do this,
+   setting the 'mt', 'ms', or 'mc' capabilities to use a non-default
+   background color spills that color to the empty space at the end of
+   the last screen line in a match whose line spans multiple screen
+   lines.  */
+static void
+w32_clreol (void)
+{
+  DWORD nchars;
+  COORD start_pos;
+  DWORD written;
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+  GetConsoleScreenBufferInfo (hstderr, &csbi);
+  start_pos = csbi.dwCursorPosition;
+  nchars = csbi.dwSize.X - start_pos.X;
+
+  FillConsoleOutputAttribute (hstderr, norm_attr, nchars, start_pos,
+                              &written);
+  FillConsoleOutputCharacter (hstderr, ' ', nchars, start_pos, &written);
+}
+#endif
+
 /* The context and logic for choosing default --color screen attributes
    (foreground and background colors, etc.) are the following.
       -- There are eight basic colors available, each with its own
@@ -193,6 +317,20 @@
   if (cap->name == NULL)
     return "";
 
+#ifdef __MINGW32__
+    /* If stdout is connected to a console, set the console text
+     attribute directly instead of using cap->val.  Otherwise, use
+     cap->val to emit the SGR escape sequence as on Posix platforms;
+     this is needed when GCC is invoked as a subprocess of another
+     program, such as Emacs, which will handle the display of the
+     matches.  */
+  if (hstderr != INVALID_HANDLE_VALUE)
+    {
+      SHORT attr = w32_sgr2attr (cap->val);
+      SetConsoleTextAttribute (hstderr, attr);
+      return "";
+    }
+#endif
   return cap->val;
 }
 
@@ -199,7 +337,19 @@
 const char *
 colorize_stop (bool show_color)
 {
-  return show_color ? SGR_RESET : "";
+  if (!show_color)
+    return "";
+
+#ifdef __MINGW32__
+  if (hstderr != INVALID_HANDLE_VALUE)
+    {
+      SetConsoleTextAttribute (hstderr, norm_attr);
+      w32_clreol ();
+      return "";
+    }
+#else
+  return SGR_RESET;
+#endif
 }
 
 /* Parse GCC_COLORS.  The default would look like:
@@ -275,13 +425,22 @@
       return true;
 }
 
-#if defined(_WIN32)
-bool
-colorize_init (diagnostic_color_rule_t)
+static bool
+host_color_init (void)
 {
-  return false;
+#ifdef __MINGW32__
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+
+  hstderr = GetStdHandle (STD_ERROR_HANDLE);
+  /* Initialize the normal text attribute used by the console.  */
+  if (hstderr != INVALID_HANDLE_VALUE
+      && GetConsoleScreenBufferInfo (hstderr, &csbi))
+     norm_attr = csbi.wAttributes;
+  else
+    hstderr = INVALID_HANDLE_VALUE;
+#endif
+  return true;
 }
-#else
 
 /* Return true if we should use color when in auto mode, false otherwise. */
 static bool
@@ -288,7 +447,15 @@
 should_colorize (void)
 {
   char const *t = getenv ("TERM");
+#ifdef __MINGW32__
+  /* $TERM is not normally defined on DOS/Windows, so don't require
+     it for highlighting.  But some programs, like Emacs, do define
+     it when running GCC as a subprocess, so make sure they don't
+     set TERM=dumb.  */
+  return !(t && strcmp (t, "dumb") == 0) && isatty (STDERR_FILENO);
+#else
   return t && strcmp (t, "dumb") != 0 && isatty (STDERR_FILENO);
+#endif
 }
 
 
@@ -300,10 +467,10 @@
     case DIAGNOSTICS_COLOR_NO:
       return false;
     case DIAGNOSTICS_COLOR_YES:
-      return parse_gcc_colors ();
+      return parse_gcc_colors () && host_color_init ();
     case DIAGNOSTICS_COLOR_AUTO:
       if (should_colorize ())
-	return parse_gcc_colors ();
+	return parse_gcc_colors () && host_color_init ();
       else
 	return false;
     default:
@@ -310,4 +477,3 @@
       gcc_unreachable ();
     }
 }
-#endif

Reply via email to