From fee807c5f6da43ba03f4f92d832fd625ab57b0d7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 22 Dec 2016 18:08:57 +0100 Subject: mingw: adjust is_console() to work with stdin When determining whether a handle corresponds to a *real* Win32 Console (as opposed to, say, a character device such as /dev/null), we use the GetConsoleOutputBufferInfo() function as a tell-tale. However, that does not work for *input* handles associated with a console. Let's just use the GetConsoleMode() function for input handles, and since it does not work on output handles fall back to the previous method for those. This patch prepares for using is_console() instead of my previous misguided attempt in cbb3f3c9b1 (mingw: intercept isatty() to handle /dev/null as Git expects it, 2016-12-11) that broke everything on Windows. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/winansi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'compat') diff --git a/compat/winansi.c b/compat/winansi.c index cb725fb02f..590d61cb1b 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -84,6 +84,7 @@ static void warn_if_raster_font(void) static int is_console(int fd) { CONSOLE_SCREEN_BUFFER_INFO sbi; + DWORD mode; HANDLE hcon; static int initialized = 0; @@ -98,7 +99,10 @@ static int is_console(int fd) return 0; /* check if its a handle to a console output screen buffer */ - if (!GetConsoleScreenBufferInfo(hcon, &sbi)) + if (!fd) { + if (!GetConsoleMode(hcon, &mode)) + return 0; + } else if (!GetConsoleScreenBufferInfo(hcon, &sbi)) return 0; /* initialize attributes */ -- cgit v1.2.1 From 86924838e3d881cda2192fd79ac3a58aad75efd3 Mon Sep 17 00:00:00 2001 From: Alan Davies Date: Thu, 22 Dec 2016 18:09:18 +0100 Subject: mingw: fix colourization on Cygwin pseudo terminals Git only colours the output and uses pagination if isatty() returns 1. MSYS2 and Cygwin emulate pseudo terminals via named pipes, meaning that isatty() returns 0. f7f90e0f4f (mingw: make isatty() recognize MSYS2's pseudo terminals (/dev/pty*), 2016-04-27) fixed this for MSYS2 terminals, but not for Cygwin. The named pipes that Cygwin and MSYS2 use are very similar. MSYS2 PTY pipes are called 'msys-*-pty*' and Cygwin uses 'cygwin-*-pty*'. This commit modifies the existing check to allow both MSYS2 and Cygwin PTY pipes to be identified as TTYs. Note that pagination is still broken when running Git for Windows from within Cygwin, as MSYS2's less.exe is spawned (and does not like to interact with Cygwin's PTY). This partially fixes https://github.com/git-for-windows/git/issues/267 Signed-off-by: Alan Davies Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/winansi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'compat') diff --git a/compat/winansi.c b/compat/winansi.c index 590d61cb1b..fa37695fca 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -562,8 +562,12 @@ static void detect_msys_tty(int fd) name = nameinfo->Name.Buffer; name[nameinfo->Name.Length] = 0; - /* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */ - if (!wcsstr(name, L"msys-") || !wcsstr(name, L"-pty")) + /* + * Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') + * or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX') + */ + if ((!wcsstr(name, L"msys-") && !wcsstr(name, L"cygwin-")) || + !wcsstr(name, L"-pty")) return; /* init ioinfo size if we haven't done so */ -- cgit v1.2.1 From a9b8a09c3c30886c79133da9f48ef9f98c21c3b2 Mon Sep 17 00:00:00 2001 From: Jeff Hostetler Date: Thu, 22 Dec 2016 18:09:23 +0100 Subject: mingw: replace isatty() hack Git for Windows has carried a patch that depended on internals of MSVC runtime, but it does not work correctly with recent MSVC runtime. A replacement was written originally for compiling with VC++. The patch in this message is a backport of that replacement, and it also fixes the previous attempt to make isatty() tell that /dev/null is *not* an interactive terminal. Signed-off-by: Jeff Hostetler Tested-by: Beat Bolli Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/winansi.c | 176 ++++++++++++++++++++++--------------------------------- 1 file changed, 69 insertions(+), 107 deletions(-) (limited to 'compat') diff --git a/compat/winansi.c b/compat/winansi.c index fa37695fca..477209fce7 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -6,9 +6,12 @@ #include "../git-compat-util.h" #include #include +#include "win32.h" -/* In this file, we actually want to use Windows' own isatty(). */ -#undef isatty +static int fd_is_interactive[3] = { 0, 0, 0 }; +#define FD_CONSOLE 0x1 +#define FD_SWAPPED 0x2 +#define FD_MSYS 0x4 /* ANSI codes used by git: m, K @@ -105,6 +108,9 @@ static int is_console(int fd) } else if (!GetConsoleScreenBufferInfo(hcon, &sbi)) return 0; + if (fd >= 0 && fd <= 2) + fd_is_interactive[fd] |= FD_CONSOLE; + /* initialize attributes */ if (!initialized) { console = hcon; @@ -466,76 +472,50 @@ static HANDLE duplicate_handle(HANDLE hnd) return hresult; } +static HANDLE swap_osfhnd(int fd, HANDLE new_handle) +{ + /* + * Create a copy of the original handle associated with fd + * because the original will get closed when we dup2(). + */ + HANDLE handle = (HANDLE)_get_osfhandle(fd); + HANDLE duplicate = duplicate_handle(handle); -/* - * Make MSVCRT's internal file descriptor control structure accessible - * so that we can tweak OS handles and flags directly (we need MSVCRT - * to treat our pipe handle as if it were a console). - * - * We assume that the ioinfo structure (exposed by MSVCRT.dll via - * __pioinfo) starts with the OS handle and the flags. The exact size - * varies between MSVCRT versions, so we try different sizes until - * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in - * isatty(1). - */ -typedef struct { - HANDLE osfhnd; - char osflags; -} ioinfo; - -extern __declspec(dllimport) ioinfo *__pioinfo[]; - -static size_t sizeof_ioinfo = 0; + /* Create a temp fd associated with the already open "new_handle". */ + int new_fd = _open_osfhandle((intptr_t)new_handle, O_BINARY); -#define IOINFO_L2E 5 -#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) + assert((fd == 1) || (fd == 2)); -#define FPIPE 0x08 -#define FDEV 0x40 + /* + * Use stock dup2() to re-bind fd to the new handle. Note that + * this will implicitly close(1) and close both fd=1 and the + * originally associated handle. It will open a new fd=1 and + * call DuplicateHandle() on the handle associated with new_fd. + * It is because of this implicit close() that we created the + * copy of the original. + * + * Note that the OS can recycle HANDLE (numbers) just like it + * recycles fd (numbers), so we must update the cached value + * of "console". You can use GetFileType() to see that + * handle and _get_osfhandle(fd) may have the same number + * value, but they refer to different actual files now. + * + * Note that dup2() when given target := {0,1,2} will also + * call SetStdHandle(), so we don't need to worry about that. + */ + dup2(new_fd, fd); + if (console == handle) + console = duplicate; + handle = INVALID_HANDLE_VALUE; -static inline ioinfo* _pioinfo(int fd) -{ - return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] + - (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo); -} + /* Close the temp fd. This explicitly closes "new_handle" + * (because it has been associated with it). + */ + close(new_fd); -static int init_sizeof_ioinfo(void) -{ - int istty, wastty; - /* don't init twice */ - if (sizeof_ioinfo) - return sizeof_ioinfo >= 256; - - sizeof_ioinfo = sizeof(ioinfo); - wastty = isatty(1); - while (sizeof_ioinfo < 256) { - /* toggle FDEV flag, check isatty, then toggle back */ - _pioinfo(1)->osflags ^= FDEV; - istty = isatty(1); - _pioinfo(1)->osflags ^= FDEV; - /* return if we found the correct size */ - if (istty != wastty) - return 0; - sizeof_ioinfo += sizeof(void*); - } - error("Tweaking file descriptors doesn't work with this MSVCRT.dll"); - return 1; -} + fd_is_interactive[fd] |= FD_SWAPPED; -static HANDLE swap_osfhnd(int fd, HANDLE new_handle) -{ - ioinfo *pioinfo; - HANDLE old_handle; - - /* init ioinfo size if we haven't done so */ - if (init_sizeof_ioinfo()) - return INVALID_HANDLE_VALUE; - - /* get ioinfo pointer and change the handles */ - pioinfo = _pioinfo(fd); - old_handle = pioinfo->osfhnd; - pioinfo->osfhnd = new_handle; - return old_handle; + return duplicate; } #ifdef DETECT_MSYS_TTY @@ -570,45 +550,25 @@ static void detect_msys_tty(int fd) !wcsstr(name, L"-pty")) return; - /* init ioinfo size if we haven't done so */ - if (init_sizeof_ioinfo()) - return; - - /* set FDEV flag, reset FPIPE flag */ - _pioinfo(fd)->osflags &= ~FPIPE; - _pioinfo(fd)->osflags |= FDEV; + fd_is_interactive[fd] |= FD_MSYS; } #endif +/* + * Wrapper for isatty(). Most calls in the main git code + * call isatty(1 or 2) to see if the instance is interactive + * and should: be colored, show progress, paginate output. + * We lie and give results for what the descriptor WAS at + * startup (and ignore any pipe redirection we internally + * do). + */ +#undef isatty int winansi_isatty(int fd) { - int res = isatty(fd); - - if (res) { - /* - * Make sure that /dev/null is not fooling Git into believing - * that we are connected to a terminal, as "_isatty() returns a - * nonzero value if the descriptor is associated with a - * character device."; for more information, see - * - * https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx - */ - HANDLE handle = (HANDLE)_get_osfhandle(fd); - if (fd == STDIN_FILENO) { - DWORD dummy; - - if (!GetConsoleMode(handle, &dummy)) - res = 0; - } else if (fd == STDOUT_FILENO || fd == STDERR_FILENO) { - CONSOLE_SCREEN_BUFFER_INFO dummy; - - if (!GetConsoleScreenBufferInfo(handle, &dummy)) - res = 0; - } - } - - return res; + if (fd >= 0 && fd <= 2) + return fd_is_interactive[fd] != 0; + return isatty(fd); } void winansi_init(void) @@ -619,6 +579,10 @@ void winansi_init(void) /* check if either stdout or stderr is a console output screen buffer */ con1 = is_console(1); con2 = is_console(2); + + /* Also compute console bit for fd 0 even though we don't need the result here. */ + is_console(0); + if (!con1 && !con2) { #ifdef DETECT_MSYS_TTY /* check if stdin / stdout / stderr are MSYS2 pty pipes */ @@ -662,12 +626,10 @@ void winansi_init(void) */ HANDLE winansi_get_osfhandle(int fd) { - HANDLE hnd = (HANDLE) _get_osfhandle(fd); - if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) { - if (fd == 1 && hconsole1) - return hconsole1; - else if (fd == 2 && hconsole2) - return hconsole2; - } - return hnd; + if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED)) + return hconsole1; + if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED)) + return hconsole2; + + return (HANDLE)_get_osfhandle(fd); } -- cgit v1.2.1