Skip to content

Commit b195442

Browse files
committed
Merge branch 'mingw-isatty-fixup-v3'
This is an evil merge: it changes more than the merged commits, as the merged branch replaces part of the MSVC patches. This mess will need to be cleaned up in the next merging rebase, by moving the mingw-isatty-fixup patches in front of the MSVC patches. Signed-off-by: Johannes Schindelin <[email protected]>
2 parents b16a93e + ef598f7 commit b195442

File tree

2 files changed

+91
-88
lines changed

2 files changed

+91
-88
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: 88 additions & 88 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
@@ -81,6 +87,7 @@ static void warn_if_raster_font(void)
8187
static int is_console(int fd)
8288
{
8389
CONSOLE_SCREEN_BUFFER_INFO sbi;
90+
DWORD mode;
8491
HANDLE hcon;
8592

8693
static int initialized = 0;
@@ -95,9 +102,15 @@ static int is_console(int fd)
95102
return 0;
96103

97104
/* check if its a handle to a console output screen buffer */
98-
if (!GetConsoleScreenBufferInfo(hcon, &sbi))
105+
if (!fd) {
106+
if (!GetConsoleMode(hcon, &mode))
107+
return 0;
108+
} else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
99109
return 0;
100110

111+
if (fd >= 0 && fd <= 2)
112+
fd_is_interactive[fd] |= FD_CONSOLE;
113+
101114
/* initialize attributes */
102115
if (!initialized) {
103116
console = hcon;
@@ -459,76 +472,47 @@ static HANDLE duplicate_handle(HANDLE hnd)
459472
return hresult;
460473
}
461474

462-
463-
/*
464-
* Make MSVCRT's internal file descriptor control structure accessible
465-
* so that we can tweak OS handles and flags directly (we need MSVCRT
466-
* to treat our pipe handle as if it were a console).
467-
*
468-
* We assume that the ioinfo structure (exposed by MSVCRT.dll via
469-
* __pioinfo) starts with the OS handle and the flags. The exact size
470-
* varies between MSVCRT versions, so we try different sizes until
471-
* toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
472-
* isatty(1).
473-
*/
474-
typedef struct {
475-
HANDLE osfhnd;
476-
char osflags;
477-
} ioinfo;
478-
479-
extern __declspec(dllimport) ioinfo *__pioinfo[];
480-
481-
static size_t sizeof_ioinfo = 0;
482-
483-
#define IOINFO_L2E 5
484-
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
485-
486-
#define FPIPE 0x08
487-
#define FDEV 0x40
488-
489-
static inline ioinfo* _pioinfo(int fd)
490-
{
491-
return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
492-
(fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
493-
}
494-
495-
static int init_sizeof_ioinfo(void)
496-
{
497-
int istty, wastty;
498-
/* don't init twice */
499-
if (sizeof_ioinfo)
500-
return sizeof_ioinfo >= 256;
501-
502-
sizeof_ioinfo = sizeof(ioinfo);
503-
wastty = isatty(1);
504-
while (sizeof_ioinfo < 256) {
505-
/* toggle FDEV flag, check isatty, then toggle back */
506-
_pioinfo(1)->osflags ^= FDEV;
507-
istty = isatty(1);
508-
_pioinfo(1)->osflags ^= FDEV;
509-
/* return if we found the correct size */
510-
if (istty != wastty)
511-
return 0;
512-
sizeof_ioinfo += sizeof(void*);
513-
}
514-
error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
515-
return 1;
516-
}
517-
518475
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
519476
{
520-
ioinfo *pioinfo;
521-
HANDLE old_handle;
522-
523-
/* init ioinfo size if we haven't done so */
524-
if (init_sizeof_ioinfo())
525-
return INVALID_HANDLE_VALUE;
526-
527-
/* get ioinfo pointer and change the handles */
528-
pioinfo = _pioinfo(fd);
529-
old_handle = pioinfo->osfhnd;
530-
pioinfo->osfhnd = new_handle;
531-
return old_handle;
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);
483+
484+
/* Create a temp fd associated with the already open "new_handle". */
485+
int new_fd = _open_osfhandle((intptr_t)new_handle, O_BINARY);
486+
487+
assert((fd == 1) || (fd == 2));
488+
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);
507+
508+
/* Close the temp fd. This explicitly closes "new_handle"
509+
* (because it has been associated with it).
510+
*/
511+
close(new_fd);
512+
513+
fd_is_interactive[fd] |= FD_SWAPPED;
514+
515+
return duplicate;
532516
}
533517

534518
#ifdef DETECT_MSYS_TTY
@@ -553,23 +537,37 @@ static void detect_msys_tty(int fd)
553537
buffer, sizeof(buffer) - 2, &result)))
554538
return;
555539
name = nameinfo->Name.Buffer;
556-
name[nameinfo->Name.Length] = 0;
557-
558-
/* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */
559-
if (!wcsstr(name, L"msys-") || !wcsstr(name, L"-pty"))
560-
return;
561-
562-
/* init ioinfo size if we haven't done so */
563-
if (init_sizeof_ioinfo())
540+
name[nameinfo->Name.Length / sizeof(*name)] = 0;
541+
542+
/*
543+
* Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX')
544+
* or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX')
545+
*/
546+
if ((!wcsstr(name, L"msys-") && !wcsstr(name, L"cygwin-")) ||
547+
!wcsstr(name, L"-pty"))
564548
return;
565549

566-
/* set FDEV flag, reset FPIPE flag */
567-
_pioinfo(fd)->osflags &= ~FPIPE;
568-
_pioinfo(fd)->osflags |= FDEV;
550+
fd_is_interactive[fd] |= FD_MSYS;
569551
}
570552

571553
#endif
572554

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+
573571
void winansi_init(void)
574572
{
575573
int con1, con2;
@@ -578,6 +576,10 @@ void winansi_init(void)
578576
/* check if either stdout or stderr is a console output screen buffer */
579577
con1 = is_console(1);
580578
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+
581583
if (!con1 && !con2) {
582584
#ifdef DETECT_MSYS_TTY
583585
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -621,12 +623,10 @@ void winansi_init(void)
621623
*/
622624
HANDLE winansi_get_osfhandle(int fd)
623625
{
624-
HANDLE hnd = (HANDLE) _get_osfhandle(fd);
625-
if (isatty(fd) && GetFileType(hnd) == FILE_TYPE_PIPE) {
626-
if (fd == 1 && hconsole1)
627-
return hconsole1;
628-
else if (fd == 2 && hconsole2)
629-
return hconsole2;
630-
}
631-
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);
632632
}

0 commit comments

Comments
 (0)