8
8
#include <winreg.h>
9
9
#include "win32.h"
10
10
11
+ #if defined(_MSC_VER )
12
+
11
13
static int fd_is_interactive [3 ] = { 0 , 0 , 0 };
12
- #define FD_CONSOLE 0x1
13
- #define FD_SWAPPED 0x2
14
- #define FD_MSYS 0x4
14
+ #define MY_INTERACTIVE_CONSOLE 0x1
15
+ #define MY_INTERACTIVE_SWAPPED 0x2
16
+ #define MY_INTERACTIVE_MSYS 0x4
17
+
18
+ /* Accumulate what we know about the inherited console descriptors. */
19
+ static void set_interactive (int fd , int bit )
20
+ {
21
+ if (fd >=0 && fd <= 2 )
22
+ fd_is_interactive [fd ] |= bit ;
23
+ }
24
+
25
+ #endif
26
+
27
+ /* In this file, we actually want to use Windows' own isatty(). */
28
+ #undef isatty
15
29
16
30
/*
17
31
ANSI codes used by git: m, K
@@ -82,7 +96,6 @@ static void warn_if_raster_font(void)
82
96
static int is_console (int fd )
83
97
{
84
98
CONSOLE_SCREEN_BUFFER_INFO sbi ;
85
- DWORD mode ;
86
99
HANDLE hcon ;
87
100
88
101
static int initialized = 0 ;
@@ -97,14 +110,12 @@ static int is_console(int fd)
97
110
return 0 ;
98
111
99
112
/* check if its a handle to a console output screen buffer */
100
- if (!fd ) {
101
- if (!GetConsoleMode (hcon , & mode ))
102
- return 0 ;
103
- } else if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
113
+ if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
104
114
return 0 ;
105
115
106
- if (fd >= 0 && fd <= 2 )
107
- fd_is_interactive [fd ] |= FD_CONSOLE ;
116
+ #if defined(_MSC_VER )
117
+ set_interactive (fd , MY_INTERACTIVE_CONSOLE );
118
+ #endif
108
119
109
120
/* initialize attributes */
110
121
if (!initialized ) {
@@ -467,52 +478,131 @@ static HANDLE duplicate_handle(HANDLE hnd)
467
478
return hresult ;
468
479
}
469
480
481
+ #if defined(_MSC_VER )
470
482
static HANDLE swap_osfhnd (int fd , HANDLE new_handle )
471
483
{
484
+ DWORD key_std = ((fd == 1 ) ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE );
485
+
472
486
/*
473
487
* Create a copy of the original handle associated with fd
474
488
* because the original will get closed when we dup2().
475
489
*/
476
- HANDLE handle = (HANDLE )_get_osfhandle (fd );
477
- HANDLE duplicate = duplicate_handle (handle );
490
+ HANDLE h_original = (HANDLE )_get_osfhandle (fd );
491
+ HANDLE h_copy_original = duplicate_handle (h_original );
478
492
479
493
/* Create a temp fd associated with the already open "new_handle". */
480
- int new_fd = _open_osfhandle ((intptr_t )new_handle , O_BINARY );
494
+ int fd_temp = _open_osfhandle ((intptr_t )new_handle , O_BINARY );
481
495
482
496
assert ((fd == 1 ) || (fd == 2 ));
483
497
484
498
/*
485
499
* Use stock dup2() to re-bind fd to the new handle. Note that
486
500
* this will implicitly close(1) and close both fd=1 and the
487
501
* originally associated handle. It will open a new fd=1 and
488
- * call DuplicateHandle() on the handle associated with new_fd .
502
+ * call DuplicateHandle() on the handle associated with fd_temp .
489
503
* It is because of this implicit close() that we created the
490
504
* copy of the original.
491
505
*
492
506
* Note that the OS can recycle HANDLE (numbers) just like it
493
507
* recycles fd (numbers), so we must update the cached value
494
508
* of "console". You can use GetFileType() to see that
495
- * handle and _get_osfhandle(fd) may have the same number
509
+ * h_original and _get_osfhandle(fd) may have the same number
496
510
* value, but they refer to different actual files now.
497
511
*
498
512
* Note that dup2() when given target := {0,1,2} will also
499
513
* call SetStdHandle(), so we don't need to worry about that.
500
514
*/
501
- dup2 (new_fd , fd );
502
- if (console == handle )
503
- console = duplicate ;
504
- handle = INVALID_HANDLE_VALUE ;
515
+ dup2 (fd_temp , fd );
516
+ if (console == h_original )
517
+ console = h_copy_original ;
518
+ h_original = INVALID_HANDLE_VALUE ;
505
519
506
520
/* Close the temp fd. This explicitly closes "new_handle"
507
521
* (because it has been associated with it).
508
522
*/
509
- close (new_fd );
523
+ close (fd_temp );
524
+
525
+ fd_is_interactive [fd ] |= MY_INTERACTIVE_SWAPPED ;
526
+
527
+ return h_copy_original ;
528
+ }
529
+
530
+ #else
531
+
532
+ /*
533
+ * Make MSVCRT's internal file descriptor control structure accessible
534
+ * so that we can tweak OS handles and flags directly (we need MSVCRT
535
+ * to treat our pipe handle as if it were a console).
536
+ *
537
+ * We assume that the ioinfo structure (exposed by MSVCRT.dll via
538
+ * __pioinfo) starts with the OS handle and the flags. The exact size
539
+ * varies between MSVCRT versions, so we try different sizes until
540
+ * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
541
+ * isatty(1).
542
+ */
543
+ typedef struct {
544
+ HANDLE osfhnd ;
545
+ char osflags ;
546
+ } ioinfo ;
547
+
548
+ extern __declspec(dllimport ) ioinfo * __pioinfo [];
549
+
550
+ static size_t sizeof_ioinfo = 0 ;
551
+
552
+ #define IOINFO_L2E 5
553
+ #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
554
+
555
+ #define FPIPE 0x08
556
+ #define FDEV 0x40
557
+
558
+ static inline ioinfo * _pioinfo (int fd )
559
+ {
560
+ return (ioinfo * )((char * )__pioinfo [fd >> IOINFO_L2E ] +
561
+ (fd & (IOINFO_ARRAY_ELTS - 1 )) * sizeof_ioinfo );
562
+ }
510
563
511
- fd_is_interactive [fd ] |= FD_SWAPPED ;
564
+ static int init_sizeof_ioinfo (void )
565
+ {
566
+ int istty , wastty ;
567
+ /* don't init twice */
568
+ if (sizeof_ioinfo )
569
+ return sizeof_ioinfo >= 256 ;
570
+
571
+ sizeof_ioinfo = sizeof (ioinfo );
572
+ wastty = isatty (1 );
573
+ while (sizeof_ioinfo < 256 ) {
574
+ /* toggle FDEV flag, check isatty, then toggle back */
575
+ _pioinfo (1 )-> osflags ^= FDEV ;
576
+ istty = isatty (1 );
577
+ _pioinfo (1 )-> osflags ^= FDEV ;
578
+ /* return if we found the correct size */
579
+ if (istty != wastty )
580
+ return 0 ;
581
+ sizeof_ioinfo += sizeof (void * );
582
+ }
583
+ error ("Tweaking file descriptors doesn't work with this MSVCRT.dll" );
584
+ return 1 ;
585
+ }
512
586
513
- return duplicate ;
587
+ static HANDLE swap_osfhnd (int fd , HANDLE new_handle )
588
+ {
589
+ ioinfo * pioinfo ;
590
+ HANDLE old_handle ;
591
+
592
+ /* init ioinfo size if we haven't done so */
593
+ if (init_sizeof_ioinfo ())
594
+ return INVALID_HANDLE_VALUE ;
595
+
596
+ /* get ioinfo pointer and change the handles */
597
+ pioinfo = _pioinfo (fd );
598
+ old_handle = pioinfo -> osfhnd ;
599
+ pioinfo -> osfhnd = new_handle ;
600
+ return old_handle ;
514
601
}
515
602
603
+ #endif
604
+
605
+
516
606
#ifdef DETECT_MSYS_TTY
517
607
518
608
#include <winternl.h>
@@ -558,25 +648,49 @@ static void detect_msys_tty(int fd)
558
648
!wcsstr (name , L"-pty" ))
559
649
return ;
560
650
561
- fd_is_interactive [fd ] |= FD_MSYS ;
651
+ #if defined(_MSC_VER )
652
+ fd_is_interactive [fd ] |= MY_INTERACTIVE_MSYS ;
653
+ #else
654
+ /* init ioinfo size if we haven't done so */
655
+ if (init_sizeof_ioinfo ())
656
+ return ;
657
+
658
+ /* set FDEV flag, reset FPIPE flag */
659
+ _pioinfo (fd )-> osflags &= ~FPIPE ;
660
+ _pioinfo (fd )-> osflags |= FDEV ;
661
+ #endif
562
662
}
563
663
564
664
#endif
565
665
566
- /*
567
- * Wrapper for isatty(). Most calls in the main git code
568
- * call isatty(1 or 2) to see if the instance is interactive
569
- * and should: be colored, show progress, paginate output.
570
- * We lie and give results for what the descriptor WAS at
571
- * startup (and ignore any pipe redirection we internally
572
- * do).
573
- */
574
- #undef isatty
575
666
int winansi_isatty (int fd )
576
667
{
577
- if (fd >= 0 && fd <= 2 )
578
- return fd_is_interactive [fd ] != 0 ;
579
- return isatty (fd );
668
+ int res = isatty (fd );
669
+
670
+ if (res ) {
671
+ /*
672
+ * Make sure that /dev/null is not fooling Git into believing
673
+ * that we are connected to a terminal, as "_isatty() returns a
674
+ * nonzero value if the descriptor is associated with a
675
+ * character device."; for more information, see
676
+ *
677
+ * https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx
678
+ */
679
+ HANDLE handle = (HANDLE )_get_osfhandle (fd );
680
+ if (fd == STDIN_FILENO ) {
681
+ DWORD dummy ;
682
+
683
+ if (!GetConsoleMode (handle , & dummy ))
684
+ res = 0 ;
685
+ } else if (fd == STDOUT_FILENO || fd == STDERR_FILENO ) {
686
+ CONSOLE_SCREEN_BUFFER_INFO dummy ;
687
+
688
+ if (!GetConsoleScreenBufferInfo (handle , & dummy ))
689
+ res = 0 ;
690
+ }
691
+ }
692
+
693
+ return res ;
580
694
}
581
695
582
696
void winansi_init (void )
@@ -588,8 +702,10 @@ void winansi_init(void)
588
702
con1 = is_console (1 );
589
703
con2 = is_console (2 );
590
704
705
+ #if defined(_MSC_VER )
591
706
/* Also compute console bit for fd 0 even though we don't need the result here. */
592
707
is_console (0 );
708
+ #endif
593
709
594
710
if (!con1 && !con2 ) {
595
711
#ifdef DETECT_MSYS_TTY
@@ -634,10 +750,32 @@ void winansi_init(void)
634
750
*/
635
751
HANDLE winansi_get_osfhandle (int fd )
636
752
{
637
- if (fd == 1 && (fd_is_interactive [1 ] & FD_SWAPPED ))
638
- return hconsole1 ;
639
- if (fd == 2 && (fd_is_interactive [2 ] & FD_SWAPPED ))
640
- return hconsole2 ;
753
+ HANDLE hnd = (HANDLE ) _get_osfhandle (fd );
754
+ if (isatty (fd ) && GetFileType (hnd ) == FILE_TYPE_PIPE ) {
755
+ if (fd == 1 && hconsole1 )
756
+ return hconsole1 ;
757
+ else if (fd == 2 && hconsole2 )
758
+ return hconsole2 ;
759
+ }
760
+ return hnd ;
761
+ }
762
+
763
+ #ifdef _MSC_VER
641
764
642
- return (HANDLE )_get_osfhandle (fd );
765
+ /* Wrapper for isatty(). Most calls in the main git code
766
+ * call isatty(1 or 2) to see if the instance is interactive
767
+ * and should: be colored, show progress, paginate output.
768
+ * We lie and give results for what the descriptor WAS at
769
+ * startup (and ignore any pipe redirection we internally
770
+ * do).
771
+ */
772
+ #undef isatty
773
+ int msc_isatty (fd )
774
+ {
775
+ if (fd >=0 && fd <= 2 )
776
+ return fd_is_interactive [fd ] != 0 ;
777
+ else
778
+ return isatty (fd );
643
779
}
780
+
781
+ #endif
0 commit comments