|
7 | 7 | #include <wingdi.h>
|
8 | 8 | #include <winreg.h>
|
9 | 9 |
|
10 |
| -/* |
11 |
| - Functions to be wrapped: |
12 |
| -*/ |
13 |
| -#undef isatty |
14 |
| - |
15 | 10 | /*
|
16 | 11 | ANSI codes used by git: m, K
|
17 | 12 |
|
@@ -104,6 +99,7 @@ static int is_console(int fd)
|
104 | 99 |
|
105 | 100 | /* initialize attributes */
|
106 | 101 | if (!initialized) {
|
| 102 | + console = hcon; |
107 | 103 | attr = plain_attr = sbi.wAttributes;
|
108 | 104 | negative = 0;
|
109 | 105 | initialized = 1;
|
@@ -465,29 +461,80 @@ static HANDLE duplicate_handle(HANDLE hnd)
|
465 | 461 | return hresult;
|
466 | 462 | }
|
467 | 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); |
475 | 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[]; |
478 | 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; |
482 | 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; |
486 | 533 | }
|
487 | 534 |
|
488 | 535 | void winansi_init(void)
|
489 | 536 | {
|
490 |
| - int con1, con2, hwrite_fd; |
| 537 | + int con1, con2; |
491 | 538 | char name[32];
|
492 | 539 |
|
493 | 540 | /* check if either stdout or stderr is a console output screen buffer */
|
@@ -516,39 +563,18 @@ void winansi_init(void)
|
516 | 563 | if (atexit(winansi_exit))
|
517 | 564 | die_errno("atexit(winansi_exit) failed");
|
518 | 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 |
| - |
524 | 566 | /* redirect stdout / stderr to the pipe */
|
525 | 567 | if (con1)
|
526 |
| - hwrite1 = redirect_console(stdout, &hconsole1, hwrite_fd); |
| 568 | + hconsole1 = swap_osfhnd(1, hwrite1 = duplicate_handle(hwrite)); |
527 | 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)); |
532 | 571 | }
|
533 | 572 |
|
534 | 573 | static int is_same_handle(HANDLE hnd, int fd)
|
535 | 574 | {
|
536 | 575 | return hnd != INVALID_HANDLE_VALUE && hnd == (HANDLE) _get_osfhandle(fd);
|
537 | 576 | }
|
538 | 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 |
| - |
552 | 578 | /*
|
553 | 579 | * Returns the real console handle if stdout / stderr is a pipe redirecting
|
554 | 580 | * to the console. Allows spawn / exec to pass the console to the next process.
|
|
0 commit comments