Skip to content

Commit ef598f7

Browse files
jeffhostetlerdscho
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]>
1 parent 87d65ab commit ef598f7

File tree

2 files changed

+74
-79
lines changed

2 files changed

+74
-79
lines changed

compat/mingw.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,9 @@ int mingw_raise(int sig);
384384
* ANSI emulation wrappers
385385
*/
386386

387+
int winansi_isatty(int fd);
388+
#define isatty winansi_isatty
389+
387390
void winansi_init(void);
388391
HANDLE winansi_get_osfhandle(int fd);
389392

compat/winansi.c

Lines changed: 71 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
#include "../git-compat-util.h"
77
#include <wingdi.h>
88
#include <winreg.h>
9+
#include "win32.h"
10+
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
915

1016
/*
1117
ANSI codes used by git: m, K
@@ -102,6 +108,9 @@ static int is_console(int fd)
102108
} else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
103109
return 0;
104110

111+
if (fd >= 0 && fd <= 2)
112+
fd_is_interactive[fd] |= FD_CONSOLE;
113+
105114
/* initialize attributes */
106115
if (!initialized) {
107116
console = hcon;
@@ -463,76 +472,47 @@ static HANDLE duplicate_handle(HANDLE hnd)
463472
return hresult;
464473
}
465474

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);
466483

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

483-
extern __declspec(dllimport) ioinfo *__pioinfo[];
487+
assert((fd == 1) || (fd == 2));
484488

485-
static size_t sizeof_ioinfo = 0;
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 we need to update the cached console handle to the
498+
* duplicated one because the dup2() call will implicitly close
499+
* the original one.
500+
*
501+
* Note that dup2() when given target := {0,1,2} will also
502+
* call SetStdHandle(), so we don't need to worry about that.
503+
*/
504+
if (console == handle)
505+
console = duplicate;
506+
dup2(new_fd, fd);
486507

487-
#define IOINFO_L2E 5
488-
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
508+
/* Close the temp fd. This explicitly closes "new_handle"
509+
* (because it has been associated with it).
510+
*/
511+
close(new_fd);
489512

490-
#define FPIPE 0x08
491-
#define FDEV 0x40
513+
fd_is_interactive[fd] |= FD_SWAPPED;
492514

493-
static inline ioinfo* _pioinfo(int fd)
494-
{
495-
return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
496-
(fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
497-
}
498-
499-
static int init_sizeof_ioinfo(void)
500-
{
501-
int istty, wastty;
502-
/* don't init twice */
503-
if (sizeof_ioinfo)
504-
return sizeof_ioinfo >= 256;
505-
506-
sizeof_ioinfo = sizeof(ioinfo);
507-
wastty = isatty(1);
508-
while (sizeof_ioinfo < 256) {
509-
/* toggle FDEV flag, check isatty, then toggle back */
510-
_pioinfo(1)->osflags ^= FDEV;
511-
istty = isatty(1);
512-
_pioinfo(1)->osflags ^= FDEV;
513-
/* return if we found the correct size */
514-
if (istty != wastty)
515-
return 0;
516-
sizeof_ioinfo += sizeof(void*);
517-
}
518-
error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
519-
return 1;
520-
}
521-
522-
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
523-
{
524-
ioinfo *pioinfo;
525-
HANDLE old_handle;
526-
527-
/* init ioinfo size if we haven't done so */
528-
if (init_sizeof_ioinfo())
529-
return INVALID_HANDLE_VALUE;
530-
531-
/* get ioinfo pointer and change the handles */
532-
pioinfo = _pioinfo(fd);
533-
old_handle = pioinfo->osfhnd;
534-
pioinfo->osfhnd = new_handle;
535-
return old_handle;
515+
return duplicate;
536516
}
537517

538518
#ifdef DETECT_MSYS_TTY
@@ -567,17 +547,27 @@ static void detect_msys_tty(int fd)
567547
!wcsstr(name, L"-pty"))
568548
return;
569549

570-
/* init ioinfo size if we haven't done so */
571-
if (init_sizeof_ioinfo())
572-
return;
573-
574-
/* set FDEV flag, reset FPIPE flag */
575-
_pioinfo(fd)->osflags &= ~FPIPE;
576-
_pioinfo(fd)->osflags |= FDEV;
550+
fd_is_interactive[fd] |= FD_MSYS;
577551
}
578552

579553
#endif
580554

555+
/*
556+
* Wrapper for isatty(). Most calls in the main git code
557+
* call isatty(1 or 2) to see if the instance is interactive
558+
* and should: be colored, show progress, paginate output.
559+
* We lie and give results for what the descriptor WAS at
560+
* startup (and ignore any pipe redirection we internally
561+
* do).
562+
*/
563+
#undef isatty
564+
int winansi_isatty(int fd)
565+
{
566+
if (fd >= 0 && fd <= 2)
567+
return fd_is_interactive[fd] != 0;
568+
return isatty(fd);
569+
}
570+
581571
void winansi_init(void)
582572
{
583573
int con1, con2;
@@ -586,6 +576,10 @@ void winansi_init(void)
586576
/* check if either stdout or stderr is a console output screen buffer */
587577
con1 = is_console(1);
588578
con2 = is_console(2);
579+
580+
/* Also compute console bit for fd 0 even though we don't need the result here. */
581+
is_console(0);
582+
589583
if (!con1 && !con2) {
590584
#ifdef DETECT_MSYS_TTY
591585
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -629,12 +623,10 @@ void winansi_init(void)
629623
*/
630624
HANDLE winansi_get_osfhandle(int fd)
631625
{
632-
HANDLE hnd = (HANDLE) _get_osfhandle(fd);
633-
if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
634-
if (fd == 1 && hconsole1)
635-
return hconsole1;
636-
else if (fd == 2 && hconsole2)
637-
return hconsole2;
638-
}
639-
return hnd;
626+
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
627+
return hconsole1;
628+
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
629+
return hconsole2;
630+
631+
return (HANDLE)_get_osfhandle(fd);
640632
}

0 commit comments

Comments
 (0)