Skip to content

Commit a9b8a09

Browse files
jeffhostetlergitster
authored andcommitted
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 <[email protected]> Tested-by: Beat Bolli <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8692483 commit a9b8a09

File tree

1 file changed

+69
-107
lines changed

1 file changed

+69
-107
lines changed

compat/winansi.c

Lines changed: 69 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
#include "../git-compat-util.h"
77
#include <wingdi.h>
88
#include <winreg.h>
9+
#include "win32.h"
910

10-
/* In this file, we actually want to use Windows' own isatty(). */
11-
#undef isatty
11+
static int fd_is_interactive[3] = { 0, 0, 0 };
12+
#define FD_CONSOLE 0x1
13+
#define FD_SWAPPED 0x2
14+
#define FD_MSYS 0x4
1215

1316
/*
1417
ANSI codes used by git: m, K
@@ -105,6 +108,9 @@ static int is_console(int fd)
105108
} else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
106109
return 0;
107110

111+
if (fd >= 0 && fd <= 2)
112+
fd_is_interactive[fd] |= FD_CONSOLE;
113+
108114
/* initialize attributes */
109115
if (!initialized) {
110116
console = hcon;
@@ -466,76 +472,50 @@ static HANDLE duplicate_handle(HANDLE hnd)
466472
return hresult;
467473
}
468474

475+
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
476+
{
477+
/*
478+
* Create a copy of the original handle associated with fd
479+
* because the original will get closed when we dup2().
480+
*/
481+
HANDLE handle = (HANDLE)_get_osfhandle(fd);
482+
HANDLE duplicate = duplicate_handle(handle);
469483

470-
/*
471-
* Make MSVCRT's internal file descriptor control structure accessible
472-
* so that we can tweak OS handles and flags directly (we need MSVCRT
473-
* to treat our pipe handle as if it were a console).
474-
*
475-
* We assume that the ioinfo structure (exposed by MSVCRT.dll via
476-
* __pioinfo) starts with the OS handle and the flags. The exact size
477-
* varies between MSVCRT versions, so we try different sizes until
478-
* toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
479-
* isatty(1).
480-
*/
481-
typedef struct {
482-
HANDLE osfhnd;
483-
char osflags;
484-
} ioinfo;
485-
486-
extern __declspec(dllimport) ioinfo *__pioinfo[];
487-
488-
static size_t sizeof_ioinfo = 0;
484+
/* Create a temp fd associated with the already open "new_handle". */
485+
int new_fd = _open_osfhandle((intptr_t)new_handle, O_BINARY);
489486

490-
#define IOINFO_L2E 5
491-
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
487+
assert((fd == 1) || (fd == 2));
492488

493-
#define FPIPE 0x08
494-
#define FDEV 0x40
489+
/*
490+
* Use stock dup2() to re-bind fd to the new handle. Note that
491+
* this will implicitly close(1) and close both fd=1 and the
492+
* originally associated handle. It will open a new fd=1 and
493+
* call DuplicateHandle() on the handle associated with new_fd.
494+
* It is because of this implicit close() that we created the
495+
* copy of the original.
496+
*
497+
* Note that the OS can recycle HANDLE (numbers) just like it
498+
* recycles fd (numbers), so we must update the cached value
499+
* of "console". You can use GetFileType() to see that
500+
* handle and _get_osfhandle(fd) may have the same number
501+
* value, but they refer to different actual files now.
502+
*
503+
* Note that dup2() when given target := {0,1,2} will also
504+
* call SetStdHandle(), so we don't need to worry about that.
505+
*/
506+
dup2(new_fd, fd);
507+
if (console == handle)
508+
console = duplicate;
509+
handle = INVALID_HANDLE_VALUE;
495510

496-
static inline ioinfo* _pioinfo(int fd)
497-
{
498-
return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
499-
(fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
500-
}
511+
/* Close the temp fd. This explicitly closes "new_handle"
512+
* (because it has been associated with it).
513+
*/
514+
close(new_fd);
501515

502-
static int init_sizeof_ioinfo(void)
503-
{
504-
int istty, wastty;
505-
/* don't init twice */
506-
if (sizeof_ioinfo)
507-
return sizeof_ioinfo >= 256;
508-
509-
sizeof_ioinfo = sizeof(ioinfo);
510-
wastty = isatty(1);
511-
while (sizeof_ioinfo < 256) {
512-
/* toggle FDEV flag, check isatty, then toggle back */
513-
_pioinfo(1)->osflags ^= FDEV;
514-
istty = isatty(1);
515-
_pioinfo(1)->osflags ^= FDEV;
516-
/* return if we found the correct size */
517-
if (istty != wastty)
518-
return 0;
519-
sizeof_ioinfo += sizeof(void*);
520-
}
521-
error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
522-
return 1;
523-
}
516+
fd_is_interactive[fd] |= FD_SWAPPED;
524517

525-
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
526-
{
527-
ioinfo *pioinfo;
528-
HANDLE old_handle;
529-
530-
/* init ioinfo size if we haven't done so */
531-
if (init_sizeof_ioinfo())
532-
return INVALID_HANDLE_VALUE;
533-
534-
/* get ioinfo pointer and change the handles */
535-
pioinfo = _pioinfo(fd);
536-
old_handle = pioinfo->osfhnd;
537-
pioinfo->osfhnd = new_handle;
538-
return old_handle;
518+
return duplicate;
539519
}
540520

541521
#ifdef DETECT_MSYS_TTY
@@ -570,45 +550,25 @@ static void detect_msys_tty(int fd)
570550
!wcsstr(name, L"-pty"))
571551
return;
572552

573-
/* init ioinfo size if we haven't done so */
574-
if (init_sizeof_ioinfo())
575-
return;
576-
577-
/* set FDEV flag, reset FPIPE flag */
578-
_pioinfo(fd)->osflags &= ~FPIPE;
579-
_pioinfo(fd)->osflags |= FDEV;
553+
fd_is_interactive[fd] |= FD_MSYS;
580554
}
581555

582556
#endif
583557

558+
/*
559+
* Wrapper for isatty(). Most calls in the main git code
560+
* call isatty(1 or 2) to see if the instance is interactive
561+
* and should: be colored, show progress, paginate output.
562+
* We lie and give results for what the descriptor WAS at
563+
* startup (and ignore any pipe redirection we internally
564+
* do).
565+
*/
566+
#undef isatty
584567
int winansi_isatty(int fd)
585568
{
586-
int res = isatty(fd);
587-
588-
if (res) {
589-
/*
590-
* Make sure that /dev/null is not fooling Git into believing
591-
* that we are connected to a terminal, as "_isatty() returns a
592-
* nonzero value if the descriptor is associated with a
593-
* character device."; for more information, see
594-
*
595-
* https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
596-
*/
597-
HANDLE handle = (HANDLE)_get_osfhandle(fd);
598-
if (fd == STDIN_FILENO) {
599-
DWORD dummy;
600-
601-
if (!GetConsoleMode(handle, &dummy))
602-
res = 0;
603-
} else if (fd == STDOUT_FILENO || fd == STDERR_FILENO) {
604-
CONSOLE_SCREEN_BUFFER_INFO dummy;
605-
606-
if (!GetConsoleScreenBufferInfo(handle, &dummy))
607-
res = 0;
608-
}
609-
}
610-
611-
return res;
569+
if (fd >= 0 && fd <= 2)
570+
return fd_is_interactive[fd] != 0;
571+
return isatty(fd);
612572
}
613573

614574
void winansi_init(void)
@@ -619,6 +579,10 @@ void winansi_init(void)
619579
/* check if either stdout or stderr is a console output screen buffer */
620580
con1 = is_console(1);
621581
con2 = is_console(2);
582+
583+
/* Also compute console bit for fd 0 even though we don't need the result here. */
584+
is_console(0);
585+
622586
if (!con1 && !con2) {
623587
#ifdef DETECT_MSYS_TTY
624588
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -662,12 +626,10 @@ void winansi_init(void)
662626
*/
663627
HANDLE winansi_get_osfhandle(int fd)
664628
{
665-
HANDLE hnd = (HANDLE) _get_osfhandle(fd);
666-
if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
667-
if (fd == 1 && hconsole1)
668-
return hconsole1;
669-
else if (fd == 2 && hconsole2)
670-
return hconsole2;
671-
}
672-
return hnd;
629+
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
630+
return hconsole1;
631+
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
632+
return hconsole2;
633+
634+
return (HANDLE)_get_osfhandle(fd);
673635
}

0 commit comments

Comments
 (0)