6
6
#include "../git-compat-util.h"
7
7
#include <wingdi.h>
8
8
#include <winreg.h>
9
+ #include "win32.h"
10
+
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
9
15
10
16
/*
11
17
ANSI codes used by git: m, K
@@ -81,6 +87,7 @@ static void warn_if_raster_font(void)
81
87
static int is_console (int fd )
82
88
{
83
89
CONSOLE_SCREEN_BUFFER_INFO sbi ;
90
+ DWORD mode ;
84
91
HANDLE hcon ;
85
92
86
93
static int initialized = 0 ;
@@ -95,9 +102,15 @@ static int is_console(int fd)
95
102
return 0 ;
96
103
97
104
/* check if its a handle to a console output screen buffer */
98
- if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
105
+ if (!fd ) {
106
+ if (!GetConsoleMode (hcon , & mode ))
107
+ return 0 ;
108
+ } else if (!GetConsoleScreenBufferInfo (hcon , & sbi ))
99
109
return 0 ;
100
110
111
+ if (fd >= 0 && fd <= 2 )
112
+ fd_is_interactive [fd ] |= FD_CONSOLE ;
113
+
101
114
/* initialize attributes */
102
115
if (!initialized ) {
103
116
console = hcon ;
@@ -459,76 +472,47 @@ static HANDLE duplicate_handle(HANDLE hnd)
459
472
return hresult ;
460
473
}
461
474
462
-
463
- /*
464
- * Make MSVCRT's internal file descriptor control structure accessible
465
- * so that we can tweak OS handles and flags directly (we need MSVCRT
466
- * to treat our pipe handle as if it were a console).
467
- *
468
- * We assume that the ioinfo structure (exposed by MSVCRT.dll via
469
- * __pioinfo) starts with the OS handle and the flags. The exact size
470
- * varies between MSVCRT versions, so we try different sizes until
471
- * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
472
- * isatty(1).
473
- */
474
- typedef struct {
475
- HANDLE osfhnd ;
476
- char osflags ;
477
- } ioinfo ;
478
-
479
- extern __declspec(dllimport ) ioinfo * __pioinfo [];
480
-
481
- static size_t sizeof_ioinfo = 0 ;
482
-
483
- #define IOINFO_L2E 5
484
- #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
485
-
486
- #define FPIPE 0x08
487
- #define FDEV 0x40
488
-
489
- static inline ioinfo * _pioinfo (int fd )
490
- {
491
- return (ioinfo * )((char * )__pioinfo [fd >> IOINFO_L2E ] +
492
- (fd & (IOINFO_ARRAY_ELTS - 1 )) * sizeof_ioinfo );
493
- }
494
-
495
- static int init_sizeof_ioinfo (void )
496
- {
497
- int istty , wastty ;
498
- /* don't init twice */
499
- if (sizeof_ioinfo )
500
- return sizeof_ioinfo >= 256 ;
501
-
502
- sizeof_ioinfo = sizeof (ioinfo );
503
- wastty = isatty (1 );
504
- while (sizeof_ioinfo < 256 ) {
505
- /* toggle FDEV flag, check isatty, then toggle back */
506
- _pioinfo (1 )-> osflags ^= FDEV ;
507
- istty = isatty (1 );
508
- _pioinfo (1 )-> osflags ^= FDEV ;
509
- /* return if we found the correct size */
510
- if (istty != wastty )
511
- return 0 ;
512
- sizeof_ioinfo += sizeof (void * );
513
- }
514
- error ("Tweaking file descriptors doesn't work with this MSVCRT.dll" );
515
- return 1 ;
516
- }
517
-
518
475
static HANDLE swap_osfhnd (int fd , HANDLE new_handle )
519
476
{
520
- ioinfo * pioinfo ;
521
- HANDLE old_handle ;
522
-
523
- /* init ioinfo size if we haven't done so */
524
- if (init_sizeof_ioinfo ())
525
- return INVALID_HANDLE_VALUE ;
526
-
527
- /* get ioinfo pointer and change the handles */
528
- pioinfo = _pioinfo (fd );
529
- old_handle = pioinfo -> osfhnd ;
530
- pioinfo -> osfhnd = new_handle ;
531
- 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 we need to update the cached console handle to the
498
+ * duplicated one because the dup2() call will implicitly close
499
+ * the original one.
500
+ *
501
+ * Note that dup2() when given target := {0,1,2} will also
502
+ * call SetStdHandle(), so we don't need to worry about that.
503
+ */
504
+ if (console == handle )
505
+ console = duplicate ;
506
+ dup2 (new_fd , fd );
507
+
508
+ /* Close the temp fd. This explicitly closes "new_handle"
509
+ * (because it has been associated with it).
510
+ */
511
+ close (new_fd );
512
+
513
+ fd_is_interactive [fd ] |= FD_SWAPPED ;
514
+
515
+ return duplicate ;
532
516
}
533
517
534
518
#ifdef DETECT_MSYS_TTY
@@ -553,23 +537,37 @@ static void detect_msys_tty(int fd)
553
537
buffer , sizeof (buffer ) - 2 , & result )))
554
538
return ;
555
539
name = nameinfo -> Name .Buffer ;
556
- name [nameinfo -> Name .Length ] = 0 ;
557
-
558
- /* check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') */
559
- if (! wcsstr ( name , L"msys-" ) || ! wcsstr ( name , L"- pty" ) )
560
- return ;
561
-
562
- /* init ioinfo size if we haven't done so */
563
- if ( init_sizeof_ioinfo ( ))
540
+ name [nameinfo -> Name .Length / sizeof ( * name ) ] = 0 ;
541
+
542
+ /*
543
+ * Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX' )
544
+ * or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX')
545
+ */
546
+ if ((! wcsstr ( name , L"msys-" ) && ! wcsstr ( name , L"cygwin-" )) ||
547
+ ! wcsstr ( name , L"-pty" ))
564
548
return ;
565
549
566
- /* set FDEV flag, reset FPIPE flag */
567
- _pioinfo (fd )-> osflags &= ~FPIPE ;
568
- _pioinfo (fd )-> osflags |= FDEV ;
550
+ fd_is_interactive [fd ] |= FD_MSYS ;
569
551
}
570
552
571
553
#endif
572
554
555
+ /*
556
+ * Wrapper for isatty(). Most calls in the main git code
557
+ * call isatty(1 or 2) to see if the instance is interactive
558
+ * and should: be colored, show progress, paginate output.
559
+ * We lie and give results for what the descriptor WAS at
560
+ * startup (and ignore any pipe redirection we internally
561
+ * do).
562
+ */
563
+ #undef isatty
564
+ int winansi_isatty (int fd )
565
+ {
566
+ if (fd >= 0 && fd <= 2 )
567
+ return fd_is_interactive [fd ] != 0 ;
568
+ return isatty (fd );
569
+ }
570
+
573
571
void winansi_init (void )
574
572
{
575
573
int con1 , con2 ;
@@ -578,6 +576,10 @@ void winansi_init(void)
578
576
/* check if either stdout or stderr is a console output screen buffer */
579
577
con1 = is_console (1 );
580
578
con2 = is_console (2 );
579
+
580
+ /* Also compute console bit for fd 0 even though we don't need the result here. */
581
+ is_console (0 );
582
+
581
583
if (!con1 && !con2 ) {
582
584
#ifdef DETECT_MSYS_TTY
583
585
/* check if stdin / stdout / stderr are MSYS2 pty pipes */
@@ -621,12 +623,10 @@ void winansi_init(void)
621
623
*/
622
624
HANDLE winansi_get_osfhandle (int fd )
623
625
{
624
- HANDLE hnd = (HANDLE ) _get_osfhandle (fd );
625
- if (isatty (fd ) && GetFileType (hnd ) == FILE_TYPE_PIPE ) {
626
- if (fd == 1 && hconsole1 )
627
- return hconsole1 ;
628
- else if (fd == 2 && hconsole2 )
629
- return hconsole2 ;
630
- }
631
- return hnd ;
626
+ if (fd == 1 && (fd_is_interactive [1 ] & FD_SWAPPED ))
627
+ return hconsole1 ;
628
+ if (fd == 2 && (fd_is_interactive [2 ] & FD_SWAPPED ))
629
+ return hconsole2 ;
630
+
631
+ return (HANDLE )_get_osfhandle (fd );
632
632
}
0 commit comments