Skip to content

Commit 0a4283b

Browse files
committed
support terminal colors for cmd.exe and msys pty
See #302
1 parent b3f3db4 commit 0a4283b

File tree

3 files changed

+164
-12
lines changed

3 files changed

+164
-12
lines changed

src/errmsg.cpp

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010

1111
#include <stdio.h>
1212

13-
#define RED "\x1b[31;1m"
14-
#define GREEN "\x1b[32;1m"
15-
#define CYAN "\x1b[36;1m"
16-
#define WHITE "\x1b[37;1m"
17-
#define RESET "\x1b[0m"
18-
1913
enum ErrType {
2014
ErrTypeError,
2115
ErrTypeNote,
@@ -27,12 +21,26 @@ static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type)
2721
size_t col = err->column_start + 1;
2822
const char *text = buf_ptr(err->msg);
2923

30-
31-
if (color == ErrColorOn || (color == ErrColorAuto && os_stderr_tty())) {
24+
bool is_tty = os_stderr_tty();
25+
if (color == ErrColorOn || (color == ErrColorAuto && is_tty)) {
3226
if (err_type == ErrTypeError) {
33-
fprintf(stderr, WHITE "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": " RED "error:" WHITE " %s" RESET "\n", path, line, col, text);
27+
os_stderr_set_color(TermColorWhite);
28+
fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col);
29+
os_stderr_set_color(TermColorRed);
30+
fprintf(stderr, "error:");
31+
os_stderr_set_color(TermColorWhite);
32+
fprintf(stderr, " %s", text);
33+
os_stderr_set_color(TermColorReset);
34+
fprintf(stderr, "\n");
3435
} else if (err_type == ErrTypeNote) {
35-
fprintf(stderr, WHITE "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": " CYAN "note:" WHITE " %s" RESET "\n", path, line, col, text);
36+
os_stderr_set_color(TermColorWhite);
37+
fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col);
38+
os_stderr_set_color(TermColorCyan);
39+
fprintf(stderr, "note:");
40+
os_stderr_set_color(TermColorWhite);
41+
fprintf(stderr, " %s", text);
42+
os_stderr_set_color(TermColorReset);
43+
fprintf(stderr, "\n");
3644
} else {
3745
zig_unreachable();
3846
}
@@ -41,7 +49,10 @@ static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type)
4149
for (size_t i = 0; i < err->column_start; i += 1) {
4250
fprintf(stderr, " ");
4351
}
44-
fprintf(stderr, GREEN "^" RESET "\n");
52+
os_stderr_set_color(TermColorGreen);
53+
fprintf(stderr, "^");
54+
os_stderr_set_color(TermColorReset);
55+
fprintf(stderr, "\n");
4556
} else {
4657
if (err_type == ErrTypeError) {
4758
fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": error: %s\n", path, line, col, text);

src/os.cpp

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,9 +639,68 @@ int os_get_cwd(Buf *out_cwd) {
639639
#endif
640640
}
641641

642+
#if defined(ZIG_OS_WINDOWS)
643+
#define is_wprefix(s, prefix) \
644+
(wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0)
645+
static bool is_stderr_cyg_pty(void) {
646+
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
647+
if (stderr_handle == INVALID_HANDLE_VALUE)
648+
return false;
649+
650+
HANDLE h;
651+
int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH;
652+
FILE_NAME_INFO *nameinfo;
653+
WCHAR *p = NULL;
654+
655+
// Cygwin/msys's pty is a pipe.
656+
if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) {
657+
return 0;
658+
}
659+
nameinfo = (FILE_NAME_INFO *)allocate<char>(size);
660+
if (nameinfo == NULL) {
661+
return 0;
662+
}
663+
// Check the name of the pipe:
664+
// '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master'
665+
if (GetFileInformationByHandleEx(stderr_handle, FileNameInfo, nameinfo, size)) {
666+
nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0';
667+
p = nameinfo->FileName;
668+
if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */
669+
p += 8;
670+
} else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */
671+
p += 6;
672+
} else {
673+
p = NULL;
674+
}
675+
if (p != NULL) {
676+
while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */
677+
++p;
678+
if (is_wprefix(p, L"-pty")) {
679+
p += 4;
680+
} else {
681+
p = NULL;
682+
}
683+
}
684+
if (p != NULL) {
685+
while (*p && isdigit(*p)) /* Skip pty number. */
686+
++p;
687+
if (is_wprefix(p, L"-from-master")) {
688+
//p += 12;
689+
} else if (is_wprefix(p, L"-to-master")) {
690+
//p += 10;
691+
} else {
692+
p = NULL;
693+
}
694+
}
695+
}
696+
free(nameinfo);
697+
return (p != NULL);
698+
}
699+
#endif
700+
642701
bool os_stderr_tty(void) {
643702
#if defined(ZIG_OS_WINDOWS)
644-
return _isatty(_fileno(stderr)) != 0;
703+
return _isatty(_fileno(stderr)) != 0 || is_stderr_cyg_pty();
645704
#elif defined(ZIG_OS_POSIX)
646705
return isatty(STDERR_FILENO) != 0;
647706
#else
@@ -859,3 +918,76 @@ int os_self_exe_path(Buf *out_path) {
859918
#endif
860919
return ErrorFileNotFound;
861920
}
921+
922+
#define VT_RED "\x1b[31;1m"
923+
#define VT_GREEN "\x1b[32;1m"
924+
#define VT_CYAN "\x1b[36;1m"
925+
#define VT_WHITE "\x1b[37;1m"
926+
#define VT_RESET "\x1b[0m"
927+
928+
static void set_color_posix(TermColor color) {
929+
switch (color) {
930+
case TermColorRed:
931+
fprintf(stderr, VT_RED);
932+
break;
933+
case TermColorGreen:
934+
fprintf(stderr, VT_GREEN);
935+
break;
936+
case TermColorCyan:
937+
fprintf(stderr, VT_CYAN);
938+
break;
939+
case TermColorWhite:
940+
fprintf(stderr, VT_WHITE);
941+
break;
942+
case TermColorReset:
943+
fprintf(stderr, VT_RESET);
944+
break;
945+
}
946+
}
947+
948+
void os_stderr_set_color(TermColor color) {
949+
#if defined(ZIG_OS_WINDOWS)
950+
if (is_stderr_cyg_pty()) {
951+
set_color_posix(color);
952+
return;
953+
}
954+
HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE);
955+
if (stderr_handle == INVALID_HANDLE_VALUE)
956+
zig_panic("unable to get stderr handle");
957+
fflush(stderr);
958+
DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
959+
if (color != TermColorReset) {
960+
DWORD mode_flags = 0;
961+
GetConsoleMode(stderr_handle, &mode_flags);
962+
mode_flags |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
963+
SetConsoleMode(stderr_handle, mode_flags);
964+
}
965+
DWORD chars_written;
966+
switch (color) {
967+
case TermColorRed:
968+
WriteConsole(stderr_handle, VT_RED, strlen(VT_RED), &chars_written, NULL);
969+
break;
970+
case TermColorGreen:
971+
WriteConsole(stderr_handle, VT_GREEN, strlen(VT_GREEN), &chars_written, NULL);
972+
break;
973+
case TermColorCyan:
974+
WriteConsole(stderr_handle, VT_CYAN, strlen(VT_CYAN), &chars_written, NULL);
975+
break;
976+
case TermColorWhite:
977+
WriteConsole(stderr_handle, VT_WHITE, strlen(VT_WHITE), &chars_written, NULL);
978+
break;
979+
case TermColorReset:
980+
{
981+
WriteConsole(stderr_handle, VT_RESET, strlen(VT_RESET), &chars_written, NULL);
982+
983+
DWORD mode_flags = 0;
984+
GetConsoleMode(stderr_handle, &mode_flags);
985+
mode_flags &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
986+
SetConsoleMode(stderr_handle, mode_flags);
987+
break;
988+
}
989+
}
990+
#else
991+
set_color_posix(color);
992+
#endif
993+
}

src/os.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
#include <stdio.h>
1616
#include <inttypes.h>
1717

18+
enum TermColor {
19+
TermColorRed,
20+
TermColorGreen,
21+
TermColorCyan,
22+
TermColorWhite,
23+
TermColorReset,
24+
};
25+
1826
enum TerminationId {
1927
TerminationIdClean,
2028
TerminationIdSignaled,
@@ -53,6 +61,7 @@ int os_fetch_file_path(Buf *full_path, Buf *out_contents);
5361
int os_get_cwd(Buf *out_cwd);
5462

5563
bool os_stderr_tty(void);
64+
void os_stderr_set_color(TermColor color);
5665

5766
int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path);
5867
int os_delete_file(Buf *path);

0 commit comments

Comments
 (0)