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
584567int 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
614574void 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 */
663627HANDLE 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