7
#include <wingdi.h>
8
#include <winreg.h>
9
10
-/*
11
- Functions to be wrapped:
12
-*/
13
-#undef isatty
14
-
10
/*
11
ANSI codes used by git: m, K
12
99
100
/* initialize attributes */
101
if (!initialized) {
102
+ console = hcon;
103
attr = plain_attr = sbi.wAttributes;
104
negative = 0;
105
initialized = 1;
461
return hresult;
462
}
463
468
-static HANDLE redirect_console(FILE *stream, HANDLE *phcon, int new_fd)
469
-{
470
- /* get original console handle */
471
- int fd = _fileno(stream);
472
- HANDLE hcon = (HANDLE) _get_osfhandle(fd);
473
- if (hcon == INVALID_HANDLE_VALUE)
474
- die_errno("_get_osfhandle(%i) failed", fd);
464
476
- /* save a copy to phcon and console (used by the background thread) */
477
- console = *phcon = duplicate_handle(hcon);
465
+/*
466
+ * Make MSVCRT's internal file descriptor control structure accessible
467
+ * so that we can tweak OS handles and flags directly (we need MSVCRT
468
+ * to treat our pipe handle as if it were a console).
469
+ *
470
+ * We assume that the ioinfo structure (exposed by MSVCRT.dll via
471
+ * __pioinfo) starts with the OS handle and the flags. The exact size
472
+ * varies between MSVCRT versions, so we try different sizes until
473
+ * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in
474
+ * isatty(1).
475
+ */
476
+typedef struct {
477
+ HANDLE osfhnd;
478
+ char osflags;
479
+} ioinfo;
480
+
481
+extern __declspec(dllimport) ioinfo *__pioinfo[];
482
479
- /* duplicate new_fd over fd (closes fd and associated handle (hcon)) */
480
- if (_dup2(new_fd, fd))
481
- die_errno("_dup2(%i, %i) failed", new_fd, fd);
483
+static size_t sizeof_ioinfo = 0;
484
483
- /* no buffering, or stdout / stderr will be out of sync */
484
- setbuf(stream, NULL);
485
- return (HANDLE) _get_osfhandle(fd);
485
+#define IOINFO_L2E 5
486
+#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E)
487
+
488
+#define FDEV 0x40
489
+
490
+static inline ioinfo* _pioinfo(int fd)
491
+{
492
+ return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] +
493
+ (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo);
494
+}
495
+
496
+static int init_sizeof_ioinfo()
497
+{
498
+ int istty, wastty;
499
+ /* don't init twice */
500
+ if (sizeof_ioinfo)
501
+ return sizeof_ioinfo >= 256;
502
+
503
+ sizeof_ioinfo = sizeof(ioinfo);
504
+ wastty = isatty(1);
505
+ while (sizeof_ioinfo < 256) {
506
+ /* toggle FDEV flag, check isatty, then toggle back */
507
+ _pioinfo(1)->osflags ^= FDEV;
508
+ istty = isatty(1);
509
+ _pioinfo(1)->osflags ^= FDEV;
510
+ /* return if we found the correct size */
511
+ if (istty != wastty)
512
+ return 0;
513
+ sizeof_ioinfo += sizeof(void*);
514
+ }
515
+ error("Tweaking file descriptors doesn't work with this MSVCRT.dll");
516
+ return 1;
517
+}
518
+
519
+static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
520
+{
521
+ ioinfo *pioinfo;
522
+ HANDLE old_handle;
523
+
524
+ /* init ioinfo size if we haven't done so */
525
+ if (init_sizeof_ioinfo())
526
+ return INVALID_HANDLE_VALUE;
527
+
528
+ /* get ioinfo pointer and change the handles */
529
+ pioinfo = _pioinfo(fd);
530
+ old_handle = pioinfo->osfhnd;
531
+ pioinfo->osfhnd = new_handle;
532
+ return old_handle;
533
}
534
535
void winansi_init(void)
536
{
490
- int con1, con2, hwrite_fd;
537
+ int con1, con2;
538
char name[32];
539
540
/* check if either stdout or stderr is a console output screen buffer */
563
if (atexit(winansi_exit))
564
die_errno("atexit(winansi_exit) failed");
565
519
- /* create a file descriptor for the write end of the pipe */
520
- hwrite_fd = _open_osfhandle((long) duplicate_handle(hwrite), _O_BINARY);
521
- if (hwrite_fd == -1)
522
- die_errno("_open_osfhandle(%li) failed", (long) hwrite);
523
-
566
/* redirect stdout / stderr to the pipe */
567
if (con1)
526
- hwrite1 = redirect_console(stdout, &hconsole1, hwrite_fd);
568
+ hconsole1 = swap_osfhnd(1, hwrite1 = duplicate_handle(hwrite));
569
if (con2)
528
- hwrite2 = redirect_console(stderr, &hconsole2, hwrite_fd);
529
-
530
- /* close pipe file descriptor (also closes the duped hwrite) */
531
- close(hwrite_fd);
570
+ hconsole2 = swap_osfhnd(2, hwrite2 = duplicate_handle(hwrite));
571
}
572
573
static int is_same_handle(HANDLE hnd, int fd)
575
return hnd != INVALID_HANDLE_VALUE && hnd == (HANDLE) _get_osfhandle(fd);
576
}
577
539
-/*
540
- * Return true if stdout / stderr is a pipe redirecting to the console.
541
- */
542
-int winansi_isatty(int fd)
543
-{
544
- if (fd == 1 && is_same_handle(hwrite1, 1))
545
- return 1;
546
- else if (fd == 2 && is_same_handle(hwrite2, 2))
547
- return 1;
548
- else
549
- return isatty(fd);
550
-}
551
-
578
/*
579
* Returns the real console handle if stdout / stderr is a pipe redirecting
580
* to the console. Allows spawn / exec to pass the console to the next process.