--- make-4.4.1-orig/src/output.c 2023-01-02 01:05:12.000000000 +0300 +++ make-4.4.1/src/output.c 2024-09-18 09:53:12.876719800 +0300 @@ -47,6 +47,215 @@ #define OUTPUT_ISSET(_out) ((_out)->out >= 0 || (_out)->err >= 0) +#ifdef WINDOWS32 +static WORD default_console_attrs; +static int console_attrs_saved = 0; + +/* Print text to console window interpreting ANSI control codes for changing + text colors. If printing to file, just strip off these codes. + + Parameters: + msg - nil-terminated text + f - either stderr or stdout + + Note: may change current console colors. + + Returns remaining text to output. */ +static const char * +puts_in_color (const char *msg, FILE *f) +{ + /* ANSI -> Windows colors mapping. */ + static const DWORD color_map[8] = { + 0, + FOREGROUND_RED, + FOREGROUND_GREEN, + FOREGROUND_RED | FOREGROUND_GREEN, + FOREGROUND_BLUE, + FOREGROUND_RED | FOREGROUND_BLUE, + FOREGROUND_GREEN | FOREGROUND_BLUE, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE + }; + + HANDLE h = INVALID_HANDLE_VALUE; + int writing_to_console = -1; + const char *m = msg; + WORD attrs, cattrs, dattrs; + + attrs = cattrs = dattrs = 0; + + while (1) + { + /* Search for ESC[ sequence. */ + const char *p = strstr (m, "\x1b["); + + if (p == 0) + break; + + if (writing_to_console == -1) + { + CONSOLE_SCREEN_BUFFER_INFO info; + + h = GetStdHandle (stderr == f ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); + + if (GetFileType (h) == FILE_TYPE_CHAR + && GetConsoleScreenBufferInfo (h, &info)) + { + dattrs = cattrs = attrs = info.wAttributes; + + if (console_attrs_saved) + dattrs = default_console_attrs; + + writing_to_console = 1; + } + else + writing_to_console = 0; + } + + if (p != m) + { + m += fwrite (m, 1, (int)(p - m), f); + if (p != m) + break; /* Writing failed, print the rest as is. */ + } + + p += 2; + + /* This code recognizes only next escape sequences: + 0, 1, 2, 30-37, 39, 40-47, 49, 90-97, 100-107. */ + while (1) + { + /* Skip leading zeros. */ + while ('0' == *p) + p++; + + if ('1' == *p) + { + if ('0' == p[1] && '0' <= p[2] && p[2] <= '7') + { + /* 100-107 - set background color, high intensity. */ + attrs &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + attrs |= (color_map[p[2] - '0'] << 4) | BACKGROUND_INTENSITY; + } + else + /* 1 - increased font intensity. */ + attrs |= FOREGROUND_INTENSITY; + } + else if ('2' == *p) + /* 2 - decreased font intensity. */ + attrs &= ~FOREGROUND_INTENSITY; + else if ('3' == *p) + { + /* 30-37 - set font color + else - reset font color to default. */ + attrs &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + if ('0' <= p[1] && p[1] <= '7') + attrs |= color_map[p[1] - '0']; + else + attrs |= dattrs & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + } + else if ('4' == *p) + { + /* 40-47 - set background color + else - reset background color to default. */ + attrs &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + if ('0' <= p[1] && p[1] <= '7') + attrs |= color_map[p[1] - '0'] << 4; + else + attrs |= dattrs & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE); + } + else if ('9' == *p && '0' <= p[1] && p[1] <= '7') + { + /* 90-97 - set font color, high intensity. */ + attrs &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); + attrs |= color_map[p[1] - '0'] | FOREGROUND_INTENSITY; + } + else if ('m' == *p || ';' == *p) + /* Reset attributes. */ + attrs = dattrs; + + /* Search for the end of escape sequence. */ + while ('\0' != *p && 'm' != *p && ';' != *p) + p++; + + if (';' == *p) + p++; + else if ('m' == *p) + break; + else + return m; /* Unterminated escape sequence - print it as is. */ + } + + m = p + 1; + + if (writing_to_console && attrs != cattrs) + { + /* Save default console attributes. */ + if (!console_attrs_saved) + { + console_attrs_saved = 1; + default_console_attrs = dattrs; + } + + /* Flush old text to not color it with new colors. */ + fflush (f); + + cattrs = attrs; + SetConsoleTextAttribute (h, attrs); + } + } + + return m; +} + +static const char * +write_in_color(const char *buffer, const char *end, FILE *to) +{ + char const *m = puts_in_color (buffer, to); + + /* If everything was written, we're done. */ + if ('\0' == *m) + return m; + + /* If there is unterminated escape + sequence - read more later. */ + if ('\x1b' == *m && '[' == m[1]) + return m; + + /* ESC at end may denote a beginning of + escape sequence - process it later. */ + if ('\x1b' == end[-1]) + end--; + + if (m != end && fwrite (m, (int)(end - m), 1, to) < 1) + return 0; + + return end; +} + +static void +restore_console_colors (void) +{ + HANDLE h; + + if (console_attrs_saved) + { + /* Check if stdout is printed to the console. */ + h = GetStdHandle (STD_OUTPUT_HANDLE); + + if (GetFileType (h) != FILE_TYPE_CHAR) + { + /* At last, check if stderr is printed to the console. */ + h = GetStdHandle (STD_ERROR_HANDLE); + + if (GetFileType (h) != FILE_TYPE_CHAR) + return; + } + + SetConsoleTextAttribute (h, default_console_attrs); + } +} +#endif /* WINDOWS32 */ + /* Write a string to the current STDOUT or STDERR. */ static void _outputs (struct output *out, int is_err, const char *msg) @@ -67,6 +276,9 @@ } f = is_err ? stderr : stdout; +#ifdef WINDOWS32 + msg = puts_in_color (msg, f); +#endif fputs (msg, f); fflush (f); } @@ -152,6 +364,11 @@ static char buffer[8192]; #ifdef WINDOWS32 + static int buffer_data = 0; + + /* Assume worst. */ + int success = 0; + int prev_mode; /* "from" is opened by open_tmpfd, which does it in binary mode, so @@ -165,16 +382,62 @@ while (1) { int len; +#ifdef WINDOWS32 + char *dst, *end; + int space; + + dst = buffer + buffer_data; + space = sizeof (buffer) - buffer_data - 1; + EINTRLOOP (len, read (from, dst, space)); + if (len == 0) + success = 1; +#else EINTRLOOP (len, read (from, buffer, sizeof (buffer))); +#endif if (len < 0) perror ("read()"); if (len <= 0) break; + +#ifdef WINDOWS32 + end = dst + len; + *end = '\0'; + + { + const char *w = write_in_color (buffer, end, to); + if (!w) + { + perror ("fwrite()"); + break; + } + + buffer_data = (int)(end - w); + } + + if (buffer_data != sizeof (buffer) - 1) + { + if (buffer_data != 0) + memmove (buffer, end - buffer_data, buffer_data); + } + else + { + /* Buffer is full because of too long escape sequence + or 0 inside text. Dump buffer as is. */ + len = sizeof (buffer) - 1; + buffer_data = 0; + +#endif /* WINDOWS32 */ + if (fwrite (buffer, len, 1, to) < 1) { perror ("fwrite()"); break; } + +#ifdef WINDOWS32 + } +#endif + fflush (to); #ifdef WINDOWS32 /* Check if Make was interrupted. */ @@ -192,6 +455,9 @@ } #ifdef WINDOWS32 + if (!success) + restore_console_colors (); + /* Switch "to" back to its original mode, so that log messages by Make have the same EOL format as without --output-sync. */ _setmode (fileno (to), prev_mode); --- make-4.4.1-orig/src/main.c 2023-01-02 01:05:12.000000000 +0300 +++ make-4.4.1/src/main.c 2024-09-18 10:37:05.069657800 +0300 @@ -1492,6 +1492,9 @@ #ifdef MAKE_LOAD " load" #endif +#ifdef WINDOWS32 + " ansi-colors" +#endif #ifdef HAVE_DOS_PATHS " dospaths" #endif