6
6
#include "../git-compat-util.h"
7
7
#include <wingdi.h>
8
8
#include <winreg.h>
9
+ #include "win32.h"
9
10
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
12
15
13
16
/*
14
17
ANSI codes used by git: m, K
@@ -84,6 +87,7 @@ static void warn_if_raster_font(void)
84
87
static int is_console (int fd )
85
88
{
86
89
CONSOLE_SCREEN_BUFFER_INFO sbi ;
90
+ DWORD mode ;
87
91
HANDLE hcon ;
88
92
89
93
static int initialized = 0 ;
@@ -98,9 +102,15 @@ static int is_console(int fd)
98
102
return 0 ;
99
103
100
104
/* 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 ))
102
109
return 0 ;
103
110
111
+ if (fd >= 0 && fd <= 2 )
112
+ fd_is_interactive [fd ] |= FD_CONSOLE ;
113
+
104
114
/* initialize attributes */
105
115
if (!initialized ) {
106
116
console = hcon ;
@@ -462,76 +472,50 @@ static HANDLE duplicate_handle(HANDLE hnd)
462
472
return hresult ;
463
473
}
464
474
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
-
521
475
static HANDLE swap_osfhnd (int fd , HANDLE new_handle )
522
476
{
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 ;
535
519
}
536
520
537
521
#ifdef DETECT_MSYS_TTY
@@ -558,49 +542,33 @@ static void detect_msys_tty(int fd)
558
542
name = nameinfo -> Name .Buffer ;
559
543
name [nameinfo -> Name .Length / sizeof (* name )] = 0 ;
560
544
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" ))
563
551
return ;
564
552
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 ;
572
554
}
573
555
574
556
#endif
575
557
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
576
567
int winansi_isatty (int fd )
577
568
{
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 );
604
572
}
605
573
606
574
void winansi_init (void )
@@ -611,6 +579,10 @@ void winansi_init(void)
611
579
/* check if either stdout or stderr is a console output screen buffer */
612
580
con1 = is_console (1 );
613
581
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
+
614
586
if (!con1 && !con2 ) {
615
587
#ifdef DETECT_MSYS_TTY
616
588
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -654,12 +626,10 @@ void winansi_init(void)
654
626
*/
655
627
HANDLE winansi_get_osfhandle (int fd )
656
628
{
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 );
665
635
}
0 commit comments