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
@@ -105,6 +108,9 @@ static int is_console(int fd)
105
108
} else if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
106
109
return 0 ;
107
110
111
+ if (fd >= 0 && fd <= 2 )
112
+ fd_is_interactive [fd ] |= FD_CONSOLE ;
113
+
108
114
/* initialize attributes */
109
115
if (!initialized ) {
110
116
console = hcon ;
@@ -466,76 +472,50 @@ static HANDLE duplicate_handle(HANDLE hnd)
466
472
return hresult ;
467
473
}
468
474
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 );
469
483
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 );
489
486
490
- #define IOINFO_L2E 5
491
- #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
487
+ assert ((fd == 1 ) || (fd == 2 ));
492
488
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 ;
495
510
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 );
501
515
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 ;
524
517
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 ;
539
519
}
540
520
541
521
#ifdef DETECT_MSYS_TTY
@@ -570,45 +550,25 @@ static void detect_msys_tty(int fd)
570
550
!wcsstr (name , L"-pty" ))
571
551
return ;
572
552
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 ;
580
554
}
581
555
582
556
#endif
583
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
584
567
int winansi_isatty (int fd )
585
568
{
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 );
612
572
}
613
573
614
574
void winansi_init (void )
@@ -619,6 +579,10 @@ void winansi_init(void)
619
579
/* check if either stdout or stderr is a console output screen buffer */
620
580
con1 = is_console (1 );
621
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
+
622
586
if (!con1 && !con2 ) {
623
587
#ifdef DETECT_MSYS_TTY
624
588
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -662,12 +626,10 @@ void winansi_init(void)
662
626
*/
663
627
HANDLE winansi_get_osfhandle (int fd )
664
628
{
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 );
673
635
}
0 commit comments