From 0a4283b38ba40242f8f9302a63e8c4d664a50799 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Oct 2017 01:37:05 -0400 Subject: [PATCH] support terminal colors for cmd.exe and msys pty See #302 --- src/errmsg.cpp | 33 ++++++++---- src/os.cpp | 134 ++++++++++++++++++++++++++++++++++++++++++++++++- src/os.hpp | 9 ++++ 3 files changed, 164 insertions(+), 12 deletions(-) diff --git a/src/errmsg.cpp b/src/errmsg.cpp index 91a12fda4e..bef4d7548d 100644 --- a/src/errmsg.cpp +++ b/src/errmsg.cpp @@ -10,12 +10,6 @@ #include -#define RED "\x1b[31;1m" -#define GREEN "\x1b[32;1m" -#define CYAN "\x1b[36;1m" -#define WHITE "\x1b[37;1m" -#define RESET "\x1b[0m" - enum ErrType { ErrTypeError, ErrTypeNote, @@ -27,12 +21,26 @@ static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) size_t col = err->column_start + 1; const char *text = buf_ptr(err->msg); - - if (color == ErrColorOn || (color == ErrColorAuto && os_stderr_tty())) { + bool is_tty = os_stderr_tty(); + if (color == ErrColorOn || (color == ErrColorAuto && is_tty)) { if (err_type == ErrTypeError) { - fprintf(stderr, WHITE "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": " RED "error:" WHITE " %s" RESET "\n", path, line, col, text); + os_stderr_set_color(TermColorWhite); + fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); + os_stderr_set_color(TermColorRed); + fprintf(stderr, "error:"); + os_stderr_set_color(TermColorWhite); + fprintf(stderr, " %s", text); + os_stderr_set_color(TermColorReset); + fprintf(stderr, "\n"); } else if (err_type == ErrTypeNote) { - fprintf(stderr, WHITE "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": " CYAN "note:" WHITE " %s" RESET "\n", path, line, col, text); + os_stderr_set_color(TermColorWhite); + fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); + os_stderr_set_color(TermColorCyan); + fprintf(stderr, "note:"); + os_stderr_set_color(TermColorWhite); + fprintf(stderr, " %s", text); + os_stderr_set_color(TermColorReset); + fprintf(stderr, "\n"); } else { zig_unreachable(); } @@ -41,7 +49,10 @@ static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) for (size_t i = 0; i < err->column_start; i += 1) { fprintf(stderr, " "); } - fprintf(stderr, GREEN "^" RESET "\n"); + os_stderr_set_color(TermColorGreen); + fprintf(stderr, "^"); + os_stderr_set_color(TermColorReset); + fprintf(stderr, "\n"); } else { if (err_type == ErrTypeError) { fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": error: %s\n", path, line, col, text); diff --git a/src/os.cpp b/src/os.cpp index 5f884485db..5f115c859a 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -639,9 +639,68 @@ int os_get_cwd(Buf *out_cwd) { #endif } +#if defined(ZIG_OS_WINDOWS) +#define is_wprefix(s, prefix) \ + (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) +static bool is_stderr_cyg_pty(void) { + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + if (stderr_handle == INVALID_HANDLE_VALUE) + return false; + + HANDLE h; + int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH; + FILE_NAME_INFO *nameinfo; + WCHAR *p = NULL; + + // Cygwin/msys's pty is a pipe. + if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) { + return 0; + } + nameinfo = (FILE_NAME_INFO *)allocate(size); + if (nameinfo == NULL) { + return 0; + } + // Check the name of the pipe: + // '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' + if (GetFileInformationByHandleEx(stderr_handle, FileNameInfo, nameinfo, size)) { + nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; + p = nameinfo->FileName; + if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ + p += 8; + } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */ + p += 6; + } else { + p = NULL; + } + if (p != NULL) { + while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */ + ++p; + if (is_wprefix(p, L"-pty")) { + p += 4; + } else { + p = NULL; + } + } + if (p != NULL) { + while (*p && isdigit(*p)) /* Skip pty number. */ + ++p; + if (is_wprefix(p, L"-from-master")) { + //p += 12; + } else if (is_wprefix(p, L"-to-master")) { + //p += 10; + } else { + p = NULL; + } + } + } + free(nameinfo); + return (p != NULL); +} +#endif + bool os_stderr_tty(void) { #if defined(ZIG_OS_WINDOWS) - return _isatty(_fileno(stderr)) != 0; + return _isatty(_fileno(stderr)) != 0 || is_stderr_cyg_pty(); #elif defined(ZIG_OS_POSIX) return isatty(STDERR_FILENO) != 0; #else @@ -859,3 +918,76 @@ int os_self_exe_path(Buf *out_path) { #endif return ErrorFileNotFound; } + +#define VT_RED "\x1b[31;1m" +#define VT_GREEN "\x1b[32;1m" +#define VT_CYAN "\x1b[36;1m" +#define VT_WHITE "\x1b[37;1m" +#define VT_RESET "\x1b[0m" + +static void set_color_posix(TermColor color) { + switch (color) { + case TermColorRed: + fprintf(stderr, VT_RED); + break; + case TermColorGreen: + fprintf(stderr, VT_GREEN); + break; + case TermColorCyan: + fprintf(stderr, VT_CYAN); + break; + case TermColorWhite: + fprintf(stderr, VT_WHITE); + break; + case TermColorReset: + fprintf(stderr, VT_RESET); + break; + } +} + +void os_stderr_set_color(TermColor color) { +#if defined(ZIG_OS_WINDOWS) + if (is_stderr_cyg_pty()) { + set_color_posix(color); + return; + } + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + if (stderr_handle == INVALID_HANDLE_VALUE) + zig_panic("unable to get stderr handle"); + fflush(stderr); + DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; + if (color != TermColorReset) { + DWORD mode_flags = 0; + GetConsoleMode(stderr_handle, &mode_flags); + mode_flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(stderr_handle, mode_flags); + } + DWORD chars_written; + switch (color) { + case TermColorRed: + WriteConsole(stderr_handle, VT_RED, strlen(VT_RED), &chars_written, NULL); + break; + case TermColorGreen: + WriteConsole(stderr_handle, VT_GREEN, strlen(VT_GREEN), &chars_written, NULL); + break; + case TermColorCyan: + WriteConsole(stderr_handle, VT_CYAN, strlen(VT_CYAN), &chars_written, NULL); + break; + case TermColorWhite: + WriteConsole(stderr_handle, VT_WHITE, strlen(VT_WHITE), &chars_written, NULL); + break; + case TermColorReset: + { + WriteConsole(stderr_handle, VT_RESET, strlen(VT_RESET), &chars_written, NULL); + + DWORD mode_flags = 0; + GetConsoleMode(stderr_handle, &mode_flags); + mode_flags &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(stderr_handle, mode_flags); + break; + } + } +#else + set_color_posix(color); +#endif +} diff --git a/src/os.hpp b/src/os.hpp index da106c4377..293f6dcdf3 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -15,6 +15,14 @@ #include #include +enum TermColor { + TermColorRed, + TermColorGreen, + TermColorCyan, + TermColorWhite, + TermColorReset, +}; + enum TerminationId { TerminationIdClean, TerminationIdSignaled, @@ -53,6 +61,7 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents); int os_get_cwd(Buf *out_cwd); bool os_stderr_tty(void); +void os_stderr_set_color(TermColor color); int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); int os_delete_file(Buf *path);