Skip to content

Commit aa4aafb

Browse files
committed
Merge remote-tracking branch 'kblees/kb/fix-broken-pipe-detection-v2' into devel
2 parents d084f44 + 3e20962 commit aa4aafb

File tree

2 files changed

+70
-46
lines changed

2 files changed

+70
-46
lines changed

compat/mingw.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,7 @@ sig_handler_t mingw_signal(int sig, sig_handler_t handler);
305305
*/
306306

307307
void winansi_init(void);
308-
int winansi_isatty(int fd);
309308
HANDLE winansi_get_osfhandle(int fd);
310-
#define isatty winansi_isatty
311309

312310
/*
313311
* git specific compatibility

compat/winansi.c

Lines changed: 70 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@
77
#include <wingdi.h>
88
#include <winreg.h>
99

10-
/*
11-
Functions to be wrapped:
12-
*/
13-
#undef isatty
14-
1510
/*
1611
ANSI codes used by git: m, K
1712
@@ -103,6 +98,7 @@ static int is_console(int fd)
10398

10499
/* initialize attributes */
105100
if (!initialized) {
101+
console = hcon;
106102
attr = plain_attr = sbi.wAttributes;
107103
negative = 0;
108104
initialized = 1;
@@ -463,29 +459,80 @@ static HANDLE duplicate_handle(HANDLE hnd)
463459
return hresult;
464460
}
465461

466-
static HANDLE redirect_console(FILE *stream, HANDLE *phcon, int new_fd)
467-
{
468-
/* get original console handle */
469-
int fd = _fileno(stream);
470-
HANDLE hcon = (HANDLE) _get_osfhandle(fd);
471-
if (hcon == INVALID_HANDLE_VALUE)
472-
die_errno("_get_osfhandle(%i) failed", fd);
473462

474-
/* save a copy to phcon and console (used by the background thread) */
475-
console = *phcon = duplicate_handle(hcon);
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[];
476480

477-
/* duplicate new_fd over fd (closes fd and associated handle (hcon)) */
478-
if (_dup2(new_fd, fd))
479-
die_errno("_dup2(%i, %i) failed", new_fd, fd);
481+
static size_t sizeof_ioinfo = 0;
480482

481-
/* no buffering, or stdout / stderr will be out of sync */
482-
setbuf(stream, NULL);
483-
return (HANDLE) _get_osfhandle(fd);
483+
#define IOINFO_L2E 5
484+
#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
485+
486+
#define FDEV 0x40
487+
488+
static inline ioinfo* _pioinfo(int fd)
489+
{
490+
return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
491+
(fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
492+
}
493+
494+
static int init_sizeof_ioinfo()
495+
{
496+
int istty, wastty;
497+
/* don't init twice */
498+
if (sizeof_ioinfo)
499+
return sizeof_ioinfo >= 256;
500+
501+
sizeof_ioinfo = sizeof(ioinfo);
502+
wastty = isatty(1);
503+
while (sizeof_ioinfo < 256) {
504+
/* toggle FDEV flag, check isatty, then toggle back */
505+
_pioinfo(1)->osflags ^= FDEV;
506+
istty = isatty(1);
507+
_pioinfo(1)->osflags ^= FDEV;
508+
/* return if we found the correct size */
509+
if (istty != wastty)
510+
return 0;
511+
sizeof_ioinfo += sizeof(void*);
512+
}
513+
error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
514+
return 1;
515+
}
516+
517+
static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
518+
{
519+
ioinfo *pioinfo;
520+
HANDLE old_handle;
521+
522+
/* init ioinfo size if we haven't done so */
523+
if (init_sizeof_ioinfo())
524+
return INVALID_HANDLE_VALUE;
525+
526+
/* get ioinfo pointer and change the handles */
527+
pioinfo = _pioinfo(fd);
528+
old_handle = pioinfo->osfhnd;
529+
pioinfo->osfhnd = new_handle;
530+
return old_handle;
484531
}
485532

486533
void winansi_init(void)
487534
{
488-
int con1, con2, hwrite_fd;
535+
int con1, con2;
489536
char name[32];
490537

491538
/* check if either stdout or stderr is a console output screen buffer */
@@ -514,39 +561,18 @@ void winansi_init(void)
514561
if (atexit(winansi_exit))
515562
die_errno("atexit(winansi_exit) failed");
516563

517-
/* create a file descriptor for the write end of the pipe */
518-
hwrite_fd = _open_osfhandle((long) duplicate_handle(hwrite), _O_BINARY);
519-
if (hwrite_fd == -1)
520-
die_errno("_open_osfhandle(%li) failed", (long) hwrite);
521-
522564
/* redirect stdout / stderr to the pipe */
523565
if (con1)
524-
hwrite1 = redirect_console(stdout, &hconsole1, hwrite_fd);
566+
hconsole1 = swap_osfhnd(1, hwrite1 = duplicate_handle(hwrite));
525567
if (con2)
526-
hwrite2 = redirect_console(stderr, &hconsole2, hwrite_fd);
527-
528-
/* close pipe file descriptor (also closes the duped hwrite) */
529-
close(hwrite_fd);
568+
hconsole2 = swap_osfhnd(2, hwrite2 = duplicate_handle(hwrite));
530569
}
531570

532571
static int is_same_handle(HANDLE hnd, int fd)
533572
{
534573
return hnd != INVALID_HANDLE_VALUE && hnd == (HANDLE) _get_osfhandle(fd);
535574
}
536575

537-
/*
538-
* Return true if stdout / stderr is a pipe redirecting to the console.
539-
*/
540-
int winansi_isatty(int fd)
541-
{
542-
if (fd == 1 && is_same_handle(hwrite1, 1))
543-
return 1;
544-
else if (fd == 2 && is_same_handle(hwrite2, 2))
545-
return 1;
546-
else
547-
return isatty(fd);
548-
}
549-
550576
/*
551577
* Returns the real console handle if stdout / stderr is a pipe redirecting
552578
* to the console. Allows spawn / exec to pass the console to the next process.

0 commit comments

Comments
 (0)