Commit 0a4283b38b

Andrew Kelley <superjoe30@gmail.com>
2017-10-02 07:37:05
support terminal colors for cmd.exe and msys pty
See #302
1 parent b3f3db4
Changed files (3)
src/errmsg.cpp
@@ -10,12 +10,6 @@
 
 #include <stdio.h>
 
-#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);
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<char>(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
+}
src/os.hpp
@@ -15,6 +15,14 @@
 #include <stdio.h>
 #include <inttypes.h>
 
+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);