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
@@ -84,6 +87,7 @@ static void warn_if_raster_font(void)
8487static int is_console (int fd )
8588{
8689 CONSOLE_SCREEN_BUFFER_INFO sbi ;
90+ DWORD mode ;
8791 HANDLE hcon ;
8892
8993 static int initialized = 0 ;
@@ -98,9 +102,15 @@ static int is_console(int fd)
98102 return 0 ;
99103
100104 /* check if its a handle to a console output screen buffer */
101- if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
105+ if (!fd ) {
106+ if (!GetConsoleMode (hcon , & mode ))
107+ return 0 ;
108+ } else if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
102109 return 0 ;
103110
111+ if (fd >= 0 && fd <= 2 )
112+ fd_is_interactive [fd ] |= FD_CONSOLE ;
113+
104114 /* initialize attributes */
105115 if (!initialized ) {
106116 console = hcon ;
@@ -462,76 +472,50 @@ static HANDLE duplicate_handle(HANDLE hnd)
462472 return hresult ;
463473}
464474
465-
466- /*
467- * Make MSVCRT's internal file descriptor control structure accessible
468- * so that we can tweak OS handles and flags directly (we need MSVCRT
469- * to treat our pipe handle as if it were a console).
470- *
471- * We assume that the ioinfo structure (exposed by MSVCRT.dll via
472- * __pioinfo) starts with the OS handle and the flags. The exact size
473- * varies between MSVCRT versions, so we try different sizes until
474- * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
475- * isatty(1).
476- */
477- typedef struct {
478- HANDLE osfhnd ;
479- char osflags ;
480- } ioinfo ;
481-
482- extern __declspec(dllimport ) ioinfo * __pioinfo [];
483-
484- static size_t sizeof_ioinfo = 0 ;
485-
486- #define IOINFO_L2E 5
487- #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
488-
489- #define FPIPE 0x08
490- #define FDEV 0x40
491-
492- static inline ioinfo * _pioinfo (int fd )
493- {
494- return (ioinfo * )((char * )__pioinfo [fd >> IOINFO_L2E ] +
495- (fd & (IOINFO_ARRAY_ELTS - 1 )) * sizeof_ioinfo );
496- }
497-
498- static int init_sizeof_ioinfo (void )
499- {
500- int istty , wastty ;
501- /* don't init twice */
502- if (sizeof_ioinfo )
503- return sizeof_ioinfo >= 256 ;
504-
505- sizeof_ioinfo = sizeof (ioinfo );
506- wastty = isatty (1 );
507- while (sizeof_ioinfo < 256 ) {
508- /* toggle FDEV flag, check isatty, then toggle back */
509- _pioinfo (1 )-> osflags ^= FDEV ;
510- istty = isatty (1 );
511- _pioinfo (1 )-> osflags ^= FDEV ;
512- /* return if we found the correct size */
513- if (istty != wastty )
514- return 0 ;
515- sizeof_ioinfo += sizeof (void * );
516- }
517- error ("Tweaking file descriptors doesn't work with this MSVCRT.dll" );
518- return 1 ;
519- }
520-
521475static HANDLE swap_osfhnd (int fd , HANDLE new_handle )
522476{
523- ioinfo * pioinfo ;
524- HANDLE old_handle ;
525-
526- /* init ioinfo size if we haven't done so */
527- if (init_sizeof_ioinfo ())
528- return INVALID_HANDLE_VALUE ;
529-
530- /* get ioinfo pointer and change the handles */
531- pioinfo = _pioinfo (fd );
532- old_handle = pioinfo -> osfhnd ;
533- pioinfo -> osfhnd = new_handle ;
534- 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 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 ;
510+
511+ /* Close the temp fd. This explicitly closes "new_handle"
512+ * (because it has been associated with it).
513+ */
514+ close (new_fd );
515+
516+ fd_is_interactive [fd ] |= FD_SWAPPED ;
517+
518+ return duplicate ;
535519}
536520
537521#ifdef DETECT_MSYS_TTY
@@ -558,49 +542,33 @@ static void detect_msys_tty(int fd)
558542 name = nameinfo -> Name .Buffer ;
559543 name [nameinfo -> Name .Length / sizeof (* name )] = 0 ;
560544
561- /* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */
562- if (!wcsstr (name , L"msys-" ) || !wcsstr (name , L"-pty" ))
545+ /*
546+ * Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX')
547+ * or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX')
548+ */
549+ if ((!wcsstr (name , L"msys-" ) && !wcsstr (name , L"cygwin-" )) ||
550+ !wcsstr (name , L"-pty" ))
563551 return ;
564552
565- /* init ioinfo size if we haven't done so */
566- if (init_sizeof_ioinfo ())
567- return ;
568-
569- /* set FDEV flag, reset FPIPE flag */
570- _pioinfo (fd )-> osflags &= ~FPIPE ;
571- _pioinfo (fd )-> osflags |= FDEV ;
553+ fd_is_interactive [fd ] |= FD_MSYS ;
572554}
573555
574556#endif
575557
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
576567int winansi_isatty (int fd )
577568{
578- int res = isatty (fd );
579-
580- if (res ) {
581- /*
582- * Make sure that /dev/null is not fooling Git into believing
583- * that we are connected to a terminal, as "_isatty() returns a
584- * nonzero value if the descriptor is associated with a
585- * character device."; for more information, see
586- *
587- * https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
588- */
589- HANDLE handle = (HANDLE )_get_osfhandle (fd );
590- if (fd == STDIN_FILENO ) {
591- DWORD dummy ;
592-
593- if (!GetConsoleMode (handle , & dummy ))
594- res = 0 ;
595- } else if (fd == STDOUT_FILENO || fd == STDERR_FILENO ) {
596- CONSOLE_SCREEN_BUFFER_INFO dummy ;
597-
598- if (!GetConsoleScreenBufferInfo (handle , & dummy ))
599- res = 0 ;
600- }
601- }
602-
603- return res ;
569+ if (fd >= 0 && fd <= 2 )
570+ return fd_is_interactive [fd ] != 0 ;
571+ return isatty (fd );
604572}
605573
606574void winansi_init (void )
@@ -611,6 +579,10 @@ void winansi_init(void)
611579 /* check if either stdout or stderr is a console output screen buffer */
612580 con1 = is_console (1 );
613581 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+
614586 if (!con1 && !con2 ) {
615587#ifdef DETECT_MSYS_TTY
616588 /* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -654,12 +626,10 @@ void winansi_init(void)
654626 */
655627HANDLE winansi_get_osfhandle (int fd )
656628{
657- HANDLE hnd = (HANDLE ) _get_osfhandle (fd );
658- if (isatty (fd ) && GetFileType (hnd ) == FILE_TYPE_PIPE ) {
659- if (fd == 1 && hconsole1 )
660- return hconsole1 ;
661- else if (fd == 2 && hconsole2 )
662- return hconsole2 ;
663- }
664- 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 );
665635}
0 commit comments