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 <jeffhost@microsoft.com> Tested-by: Beat Bolli <dev+git@drbeat.li> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
86924838e3
commit
a9b8a09c3c
180
compat/winansi.c
180
compat/winansi.c
@ -6,9 +6,12 @@
|
|||||||
#include "../git-compat-util.h"
|
#include "../git-compat-util.h"
|
||||||
#include <wingdi.h>
|
#include <wingdi.h>
|
||||||
#include <winreg.h>
|
#include <winreg.h>
|
||||||
|
#include "win32.h"
|
||||||
|
|
||||||
/* In this file, we actually want to use Windows' own isatty(). */
|
static int fd_is_interactive[3] = { 0, 0, 0 };
|
||||||
#undef isatty
|
#define FD_CONSOLE 0x1
|
||||||
|
#define FD_SWAPPED 0x2
|
||||||
|
#define FD_MSYS 0x4
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ANSI codes used by git: m, K
|
ANSI codes used by git: m, K
|
||||||
@ -105,6 +108,9 @@ static int is_console(int fd)
|
|||||||
} else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
|
} else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (fd >= 0 && fd <= 2)
|
||||||
|
fd_is_interactive[fd] |= FD_CONSOLE;
|
||||||
|
|
||||||
/* initialize attributes */
|
/* initialize attributes */
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
console = hcon;
|
console = hcon;
|
||||||
@ -466,76 +472,50 @@ static HANDLE duplicate_handle(HANDLE hnd)
|
|||||||
return hresult;
|
return hresult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
#define IOINFO_L2E 5
|
|
||||||
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
|
|
||||||
|
|
||||||
#define FPIPE 0x08
|
|
||||||
#define FDEV 0x40
|
|
||||||
|
|
||||||
static inline ioinfo* _pioinfo(int fd)
|
|
||||||
{
|
|
||||||
return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
|
|
||||||
(fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
|
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
|
||||||
{
|
{
|
||||||
ioinfo *pioinfo;
|
/*
|
||||||
HANDLE old_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);
|
||||||
|
|
||||||
/* init ioinfo size if we haven't done so */
|
/* Create a temp fd associated with the already open "new_handle". */
|
||||||
if (init_sizeof_ioinfo())
|
int new_fd = _open_osfhandle((intptr_t)new_handle, O_BINARY);
|
||||||
return INVALID_HANDLE_VALUE;
|
|
||||||
|
|
||||||
/* get ioinfo pointer and change the handles */
|
assert((fd == 1) || (fd == 2));
|
||||||
pioinfo = _pioinfo(fd);
|
|
||||||
old_handle = pioinfo->osfhnd;
|
/*
|
||||||
pioinfo->osfhnd = new_handle;
|
* Use stock dup2() to re-bind fd to the new handle. Note that
|
||||||
return old_handle;
|
* 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;
|
||||||
|
|
||||||
|
/* Close the temp fd. This explicitly closes "new_handle"
|
||||||
|
* (because it has been associated with it).
|
||||||
|
*/
|
||||||
|
close(new_fd);
|
||||||
|
|
||||||
|
fd_is_interactive[fd] |= FD_SWAPPED;
|
||||||
|
|
||||||
|
return duplicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DETECT_MSYS_TTY
|
#ifdef DETECT_MSYS_TTY
|
||||||
@ -570,45 +550,25 @@ static void detect_msys_tty(int fd)
|
|||||||
!wcsstr(name, L"-pty"))
|
!wcsstr(name, L"-pty"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* init ioinfo size if we haven't done so */
|
fd_is_interactive[fd] |= FD_MSYS;
|
||||||
if (init_sizeof_ioinfo())
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* set FDEV flag, reset FPIPE flag */
|
|
||||||
_pioinfo(fd)->osflags &= ~FPIPE;
|
|
||||||
_pioinfo(fd)->osflags |= FDEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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 winansi_isatty(int fd)
|
||||||
{
|
{
|
||||||
int res = isatty(fd);
|
if (fd >= 0 && fd <= 2)
|
||||||
|
return fd_is_interactive[fd] != 0;
|
||||||
if (res) {
|
return isatty(fd);
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void winansi_init(void)
|
void winansi_init(void)
|
||||||
@ -619,6 +579,10 @@ void winansi_init(void)
|
|||||||
/* check if either stdout or stderr is a console output screen buffer */
|
/* check if either stdout or stderr is a console output screen buffer */
|
||||||
con1 = is_console(1);
|
con1 = is_console(1);
|
||||||
con2 = is_console(2);
|
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) {
|
if (!con1 && !con2) {
|
||||||
#ifdef DETECT_MSYS_TTY
|
#ifdef DETECT_MSYS_TTY
|
||||||
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
|
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
|
||||||
@ -662,12 +626,10 @@ void winansi_init(void)
|
|||||||
*/
|
*/
|
||||||
HANDLE winansi_get_osfhandle(int fd)
|
HANDLE winansi_get_osfhandle(int fd)
|
||||||
{
|
{
|
||||||
HANDLE hnd = (HANDLE) _get_osfhandle(fd);
|
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
|
||||||
if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
|
return hconsole1;
|
||||||
if (fd == 1 && hconsole1)
|
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
|
||||||
return hconsole1;
|
return hconsole2;
|
||||||
else if (fd == 2 && hconsole2)
|
|
||||||
return hconsole2;
|
return (HANDLE)_get_osfhandle(fd);
|
||||||
}
|
|
||||||
return hnd;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user