From db0d1bae4a36e255bdf676e5030294c27e4b6c86 Mon Sep 17 00:00:00 2001 From: Liu Hao Date: Wed, 11 Oct 2017 13:34:44 +0000 Subject: [PATCH] pretty-print.c [_WIN32] (colorize_init): Remove. 2017-10-11 Liu Hao * pretty-print.c [_WIN32] (colorize_init): Remove. Use the generic version below instead. (should_colorize): Recognize Windows consoles as terminals for MinGW targets. * pretty-print.c [__MINGW32__] (write_all): New function. [__MINGW32__] (find_esc_head): Likewise. [__MINGW32__] (find_esc_terminator): Likewise. [__MINGW32__] (eat_esc_sequence): Likewise. [__MINGW32__] (mingw_ansi_fputs): New function that handles ANSI escape codes. (pp_write_text_to_stream): Use mingw_ansi_fputs instead of fputs for MinGW targets. From-SVN: r253645 --- gcc/ChangeLog | 15 + gcc/diagnostic-color.c | 28 +- gcc/pretty-print.c | 664 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 697 insertions(+), 10 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e5745b466bd..3d888e3b645 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,18 @@ +2017-10-11 Liu Hao + + * pretty-print.c [_WIN32] (colorize_init): Remove. Use + the generic version below instead. + (should_colorize): Recognize Windows consoles as terminals + for MinGW targets. + * pretty-print.c [__MINGW32__] (write_all): New function. + [__MINGW32__] (find_esc_head): Likewise. + [__MINGW32__] (find_esc_terminator): Likewise. + [__MINGW32__] (eat_esc_sequence): Likewise. + [__MINGW32__] (mingw_ansi_fputs): New function that handles + ANSI escape codes. + (pp_write_text_to_stream): Use mingw_ansi_fputs instead of fputs + for MinGW targets. + 2017-10-11 Richard Biener * tree-ssa-loop-niter.c (infer_loop_bounds_from_pointer_arith): diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c index 6adb872146b..b8cf6f2c045 100644 --- a/gcc/diagnostic-color.c +++ b/gcc/diagnostic-color.c @@ -20,6 +20,10 @@ #include "system.h" #include "diagnostic-color.h" +#ifdef __MINGW32__ +# include +#endif + /* Select Graphic Rendition (SGR, "\33[...m") strings. */ /* Also Erase in Line (EL) to Right ("\33[K") by default. */ /* Why have EL to Right after SGR? @@ -275,23 +279,28 @@ parse_gcc_colors (void) return true; } -#if defined(_WIN32) -bool -colorize_init (diagnostic_color_rule_t) -{ - return false; -} -#else - /* Return true if we should use color when in auto mode, false otherwise. */ static bool should_colorize (void) { +#ifdef __MINGW32__ + /* For consistency reasons, one should check the handle returned by + _get_osfhandle(_fileno(stderr)) because the function + pp_write_text_to_stream() in pretty-print.c calls fputs() on + that stream. However, the code below for non-Windows doesn't seem + to care about it either... */ + HANDLE h; + DWORD m; + + h = GetStdHandle (STD_ERROR_HANDLE); + return (h != INVALID_HANDLE_VALUE) && (h != NULL) + && GetConsoleMode (h, &m); +#else char const *t = getenv ("TERM"); return t && strcmp (t, "dumb") != 0 && isatty (STDERR_FILENO); +#endif } - bool colorize_init (diagnostic_color_rule_t rule) { @@ -310,4 +319,3 @@ colorize_init (diagnostic_color_rule_t rule) gcc_unreachable (); } } -#endif diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c index 7340cd4a565..e66d898a645 100644 --- a/gcc/pretty-print.c +++ b/gcc/pretty-print.c @@ -30,6 +30,666 @@ along with GCC; see the file COPYING3. If not see #include #endif +#ifdef __MINGW32__ + +/* Replacement for fputs() that handles ANSI escape codes on Windows NT. + Contributed by: Liu Hao (lh_mouse at 126 dot com) + + XXX: This file is compiled into libcommon.a that will be self-contained. + It looks like that these functions can be put nowhere else. */ + +#include +#define WIN32_LEAN_AND_MEAN 1 +#include + +/* Write all bytes in [s,s+n) into the specified stream. + Errors are ignored. */ +static void +write_all (HANDLE h, const char *s, size_t n) +{ + size_t rem = n; + DWORD step; + + while (rem != 0) + { + if (rem <= UINT_MAX) + step = rem; + else + step = UINT_MAX; + if (!WriteFile (h, s + n - rem, step, &step, NULL)) + break; + rem -= step; + } +} + +/* Find the beginning of an escape sequence. + There are two cases: + 1. If the sequence begins with an ESC character (0x1B) and a second + character X in [0x40,0x5F], returns X and stores a pointer to + the third character into *head. + 2. If the sequence begins with a character X in [0x80,0x9F], returns + (X-0x40) and stores a pointer to the second character into *head. + Stores the number of ESC character(s) in *prefix_len. + Returns 0 if no such sequence can be found. */ +static int +find_esc_head (int *prefix_len, const char **head, const char *str) +{ + int c; + const char *r = str; + int escaped = 0; + + for (;;) + { + c = (unsigned char) *r; + if (c == 0) + { + /* Not found. */ + return 0; + } + if (escaped && 0x40 <= c && c <= 0x5F) + { + /* Found (case 1). */ + *prefix_len = 2; + *head = r + 1; + return c; + } + if (0x80 <= c && c <= 0x9F) + { + /* Found (case 2). */ + *prefix_len = 1; + *head = r + 1; + return c - 0x40; + } + ++r; + escaped = c == 0x1B; + } +} + +/* Find the terminator of an escape sequence. + str should be the value stored in *head by a previous successful + call to find_esc_head(). + Returns 0 if no such sequence can be found. */ +static int +find_esc_terminator (const char **term, const char *str) +{ + int c; + const char *r = str; + + for (;;) + { + c = (unsigned char) *r; + if (c == 0) + { + /* Not found. */ + return 0; + } + if (0x40 <= c && c <= 0x7E) + { + /* Found. */ + *term = r; + return c; + } + ++r; + } +} + +/* Handle a sequence of codes. Sequences that are invalid, reserved, + unrecognized or unimplemented are ignored silently. + There isn't much we can do because of lameness of Windows consoles. */ +static void +eat_esc_sequence (HANDLE h, int esc_code, + const char *esc_head, const char *esc_term) +{ + /* Numbers in an escape sequence cannot be negative, because + a minus sign in the middle of it would have terminated it. */ + long n1, n2; + char *eptr, *delim; + CONSOLE_SCREEN_BUFFER_INFO sb; + COORD cr; + /* ED and EL parameters. */ + DWORD cnt, step; + long rows; + /* SGR parameters. */ + WORD attrib_add, attrib_rm; + const char *param; + + switch (MAKEWORD (esc_code, *esc_term)) + { + /* ESC [ n1 'A' + Move the cursor up by n1 characters. */ + case MAKEWORD ('[', 'A'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the topmost boundary. */ + if (cr.Y > n1) + cr.Y -= n1; + else + cr.Y = 0; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'B' + Move the cursor down by n1 characters. */ + case MAKEWORD ('[', 'B'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the bottommost boundary. */ + if (sb.dwSize.Y - cr.Y > n1) + cr.Y += n1; + else + cr.Y = sb.dwSize.Y; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'C' + Move the cursor right by n1 characters. */ + case MAKEWORD ('[', 'C'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the rightmost boundary. */ + if (sb.dwSize.X - cr.X > n1) + cr.X += n1; + else + cr.X = sb.dwSize.X; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'D' + Move the cursor left by n1 characters. */ + case MAKEWORD ('[', 'D'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + /* Stop at the leftmost boundary. */ + if (cr.X > n1) + cr.X -= n1; + else + cr.X = 0; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'E' + Move the cursor to the beginning of the n1-th line downwards. */ + case MAKEWORD ('[', 'E'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + cr.X = 0; + /* Stop at the bottommost boundary. */ + if (sb.dwSize.Y - cr.Y > n1) + cr.Y += n1; + else + cr.Y = sb.dwSize.Y; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'F' + Move the cursor to the beginning of the n1-th line upwards. */ + case MAKEWORD ('[', 'F'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + cr.X = 0; + /* Stop at the topmost boundary. */ + if (cr.Y > n1) + cr.Y -= n1; + else + cr.Y = 0; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'G' + Move the cursor to the (1-based) n1-th column. */ + case MAKEWORD ('[', 'G'): + if (esc_head == esc_term) + n1 = 1; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + n1 -= 1; + /* Stop at the leftmost or rightmost boundary. */ + if (n1 < 0) + cr.X = 0; + else if (n1 > sb.dwSize.X) + cr.X = sb.dwSize.X; + else + cr.X = n1; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 ';' n2 'H' + ESC [ n1 ';' n2 'f' + Move the cursor to the (1-based) n1-th row and + (also 1-based) n2-th column. */ + case MAKEWORD ('[', 'H'): + case MAKEWORD ('[', 'f'): + if (esc_head == esc_term) + { + /* Both parameters are omitted and set to 1 by default. */ + n1 = 1; + n2 = 1; + } + else if (!(delim = (char *) memchr (esc_head, ';', + esc_term - esc_head))) + { + /* Only the first parameter is given. The second one is + set to 1 by default. */ + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + n2 = 1; + } + else + { + /* Both parameters are given. The first one shall be + terminated by the semicolon. */ + n1 = strtol (esc_head, &eptr, 10); + if (eptr != delim) + break; + n2 = strtol (delim + 1, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + cr = sb.dwCursorPosition; + n1 -= 1; + n2 -= 1; + /* The cursor position shall be relative to the view coord of + the console window, which is usually smaller than the actual + buffer. FWIW, the 'appropriate' solution will be shrinking + the buffer to match the size of the console window, + destroying scrollback in the process. */ + n1 += sb.srWindow.Top; + n2 += sb.srWindow.Left; + /* Stop at the topmost or bottommost boundary. */ + if (n1 < 0) + cr.Y = 0; + else if (n1 > sb.dwSize.Y) + cr.Y = sb.dwSize.Y; + else + cr.Y = n1; + /* Stop at the leftmost or rightmost boundary. */ + if (n2 < 0) + cr.X = 0; + else if (n2 > sb.dwSize.X) + cr.X = sb.dwSize.X; + else + cr.X = n2; + SetConsoleCursorPosition (h, cr); + } + break; + + /* ESC [ n1 'J' + Erase display. */ + case MAKEWORD ('[', 'J'): + if (esc_head == esc_term) + /* This is one of the very few codes whose parameters have + a default value of zero. */ + n1 = 0; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + /* The cursor is not necessarily in the console window, which + makes the behavior of this code harder to define. */ + switch (n1) + { + case 0: + /* If the cursor is in or above the window, erase from + it to the bottom of the window; otherwise, do nothing. */ + cr = sb.dwCursorPosition; + cnt = sb.dwSize.X - sb.dwCursorPosition.X; + rows = sb.srWindow.Bottom - sb.dwCursorPosition.Y; + break; + case 1: + /* If the cursor is in or under the window, erase from + it to the top of the window; otherwise, do nothing. */ + cr.X = 0; + cr.Y = sb.srWindow.Top; + cnt = sb.dwCursorPosition.X + 1; + rows = sb.dwCursorPosition.Y - sb.srWindow.Top; + break; + case 2: + /* Erase the entire window. */ + cr.X = sb.srWindow.Left; + cr.Y = sb.srWindow.Top; + cnt = 0; + rows = sb.srWindow.Bottom - sb.srWindow.Top + 1; + break; + default: + /* Erase the entire buffer. */ + cr.X = 0; + cr.Y = 0; + cnt = 0; + rows = sb.dwSize.Y; + break; + } + if (rows < 0) + break; + cnt += rows * sb.dwSize.X; + FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step); + FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step); + } + break; + + /* ESC [ n1 'K' + Erase line. */ + case MAKEWORD ('[', 'K'): + if (esc_head == esc_term) + /* This is one of the very few codes whose parameters have + a default value of zero. */ + n1 = 0; + else + { + n1 = strtol (esc_head, &eptr, 10); + if (eptr != esc_term) + break; + } + + if (GetConsoleScreenBufferInfo (h, &sb)) + { + switch (n1) + { + case 0: + /* Erase from the cursor to the end. */ + cr = sb.dwCursorPosition; + cnt = sb.dwSize.X - sb.dwCursorPosition.X; + break; + case 1: + /* Erase from the cursor to the beginning. */ + cr = sb.dwCursorPosition; + cr.X = 0; + cnt = sb.dwCursorPosition.X + 1; + break; + default: + /* Erase the entire line. */ + cr = sb.dwCursorPosition; + cr.X = 0; + cnt = sb.dwSize.X; + break; + } + FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step); + FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step); + } + break; + + /* ESC [ n1 ';' n2 'm' + Set SGR parameters. Zero or more parameters will follow. */ + case MAKEWORD ('[', 'm'): + attrib_add = 0; + attrib_rm = 0; + if (esc_head == esc_term) + { + /* When no parameter is given, reset the console. */ + attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + attrib_rm = -1; /* Removes everything. */ + goto sgr_set_it; + } + param = esc_head; + do + { + /* Parse a parameter. */ + n1 = strtol (param, &eptr, 10); + if (*eptr != ';' && eptr != esc_term) + goto sgr_set_it; + + switch (n1) + { + case 0: + /* Reset. */ + attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + attrib_rm = -1; /* Removes everything. */ + break; + case 1: + /* Bold. */ + attrib_add |= FOREGROUND_INTENSITY; + break; + case 4: + /* Underline. */ + attrib_add |= COMMON_LVB_UNDERSCORE; + break; + case 5: + /* Blink. */ + /* XXX: It is not BLINKING at all! */ + attrib_add |= BACKGROUND_INTENSITY; + break; + case 7: + /* Reverse. */ + attrib_add |= COMMON_LVB_REVERSE_VIDEO; + break; + case 22: + /* No bold. */ + attrib_add &= ~FOREGROUND_INTENSITY; + attrib_rm |= FOREGROUND_INTENSITY; + break; + case 24: + /* No underline. */ + attrib_add &= ~COMMON_LVB_UNDERSCORE; + attrib_rm |= COMMON_LVB_UNDERSCORE; + break; + case 25: + /* No blink. */ + /* XXX: It is not BLINKING at all! */ + attrib_add &= ~BACKGROUND_INTENSITY; + attrib_rm |= BACKGROUND_INTENSITY; + break; + case 27: + /* No reverse. */ + attrib_add &= ~COMMON_LVB_REVERSE_VIDEO; + attrib_rm |= COMMON_LVB_REVERSE_VIDEO; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + /* Foreground color. */ + attrib_add &= ~(FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + n1 -= 30; + if (n1 & 1) + attrib_add |= FOREGROUND_RED; + if (n1 & 2) + attrib_add |= FOREGROUND_GREEN; + if (n1 & 4) + attrib_add |= FOREGROUND_BLUE; + attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + break; + case 38: + /* Reserved for extended foreground color. + Don't know how to handle parameters remaining. + Bail out. */ + goto sgr_set_it; + case 39: + /* Reset foreground color. */ + /* Set to grey. */ + attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN + | FOREGROUND_BLUE); + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + /* Background color. */ + attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + n1 -= 40; + if (n1 & 1) + attrib_add |= BACKGROUND_RED; + if (n1 & 2) + attrib_add |= BACKGROUND_GREEN; + if (n1 & 4) + attrib_add |= BACKGROUND_BLUE; + attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + break; + case 48: + /* Reserved for extended background color. + Don't know how to handle parameters remaining. + Bail out. */ + goto sgr_set_it; + case 49: + /* Reset background color. */ + /* Set to black. */ + attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN + | BACKGROUND_BLUE); + break; + } + + /* Prepare the next parameter. */ + param = eptr + 1; + } + while (param != esc_term); + +sgr_set_it: + /* 0xFFFF removes everything. If it is not the case, + care must be taken to preserve old attributes. */ + if (attrib_rm != 0xFFFF && GetConsoleScreenBufferInfo (h, &sb)) + { + attrib_add |= sb.wAttributes & ~attrib_rm; + } + SetConsoleTextAttribute (h, attrib_add); + break; + } +} + +int +mingw_ansi_fputs (const char *str, FILE *fp) +{ + const char *read = str; + HANDLE h; + DWORD mode; + int esc_code, prefix_len; + const char *esc_head, *esc_term; + + h = (HANDLE) _get_osfhandle (_fileno (fp)); + if (h == INVALID_HANDLE_VALUE) + return EOF; + + /* Don't mess up stdio functions with Windows APIs. */ + fflush (fp); + + if (GetConsoleMode (h, &mode)) + /* If it is a console, translate ANSI escape codes as needed. */ + for (;;) + { + if ((esc_code = find_esc_head (&prefix_len, &esc_head, read)) == 0) + { + /* Write all remaining characters, then exit. */ + write_all (h, read, strlen (read)); + break; + } + if (find_esc_terminator (&esc_term, esc_head) == 0) + /* Ignore incomplete escape sequences at the moment. + FIXME: The escape state shall be cached for further calls + to this function. */ + break; + write_all (h, read, esc_head - prefix_len - read); + eat_esc_sequence (h, esc_code, esc_head, esc_term); + read = esc_term + 1; + } + else + /* If it is not a console, write everything as-is. */ + write_all (h, read, strlen (read)); + + _close ((intptr_t) h); + return 1; +} + +#endif /* __MINGW32__ */ + static void pp_quoted_string (pretty_printer *, const char *, size_t = -1); /* Overwrite the given location/range within this text_info's rich_location. @@ -140,7 +800,11 @@ void pp_write_text_to_stream (pretty_printer *pp) { const char *text = pp_formatted_text (pp); +#ifdef __MINGW32__ + mingw_ansi_fputs (text, pp_buffer (pp)->stream); +#else fputs (text, pp_buffer (pp)->stream); +#endif pp_clear_output_area (pp); } -- 2.30.2