|
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 |
|
@@ -103,6 +98,7 @@ static int is_console(int fd)
|
103 | 98 |
|
104 | 99 | /* initialize attributes */
|
105 | 100 | if (!initialized) {
|
| 101 | + console = hcon; |
106 | 102 | attr = plain_attr = sbi.wAttributes;
|
107 | 103 | negative = 0;
|
108 | 104 | initialized = 1;
|
@@ -463,29 +459,80 @@ static HANDLE duplicate_handle(HANDLE hnd)
|
463 | 459 | return hresult;
|
464 | 460 | }
|
465 | 461 |
|
466 |
| -static HANDLE redirect_console(FILE *stream, HANDLE *phcon, int new_fd) |
467 |
| -{ |
468 |
| - /* get original console handle */ |
469 |
| - int fd = _fileno(stream); |
470 |
| - HANDLE hcon = (HANDLE) _get_osfhandle(fd); |
471 |
| - if (hcon == INVALID_HANDLE_VALUE) |
472 |
| - die_errno("_get_osfhandle(%i) failed", fd); |
473 | 462 |
|
474 |
| - /* save a copy to phcon and console (used by the background thread) */ |
475 |
| - console = *phcon = duplicate_handle(hcon); |
| 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[]; |
476 | 480 |
|
477 |
| - /* duplicate new_fd over fd (closes fd and associated handle (hcon)) */ |
478 |
| - if (_dup2(new_fd, fd)) |
479 |
| - die_errno("_dup2(%i, %i) failed", new_fd, fd); |
| 481 | +static size_t sizeof_ioinfo = 0; |
480 | 482 |
|
481 |
| - /* no buffering, or stdout / stderr will be out of sync */ |
482 |
| - setbuf(stream, NULL); |
483 |
| - return (HANDLE) _get_osfhandle(fd); |
| 483 | +#define IOINFO_L2E 5 |
| 484 | +#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) |
| 485 | + |
| 486 | +#define FDEV 0x40 |
| 487 | + |
| 488 | +static inline ioinfo* _pioinfo(int fd) |
| 489 | +{ |
| 490 | + return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] + |
| 491 | + (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo); |
| 492 | +} |
| 493 | + |
| 494 | +static int init_sizeof_ioinfo() |
| 495 | +{ |
| 496 | + int istty, wastty; |
| 497 | + /* don't init twice */ |
| 498 | + if (sizeof_ioinfo) |
| 499 | + return sizeof_ioinfo >= 256; |
| 500 | + |
| 501 | + sizeof_ioinfo = sizeof(ioinfo); |
| 502 | + wastty = isatty(1); |
| 503 | + while (sizeof_ioinfo < 256) { |
| 504 | + /* toggle FDEV flag, check isatty, then toggle back */ |
| 505 | + _pioinfo(1)->osflags ^= FDEV; |
| 506 | + istty = isatty(1); |
| 507 | + _pioinfo(1)->osflags ^= FDEV; |
| 508 | + /* return if we found the correct size */ |
| 509 | + if (istty != wastty) |
| 510 | + return 0; |
| 511 | + sizeof_ioinfo += sizeof(void*); |
| 512 | + } |
| 513 | + error("Tweaking file descriptors doesn't work with this MSVCRT.dll"); |
| 514 | + return 1; |
| 515 | +} |
| 516 | + |
| 517 | +static HANDLE swap_osfhnd(int fd, HANDLE new_handle) |
| 518 | +{ |
| 519 | + ioinfo *pioinfo; |
| 520 | + HANDLE old_handle; |
| 521 | + |
| 522 | + /* init ioinfo size if we haven't done so */ |
| 523 | + if (init_sizeof_ioinfo()) |
| 524 | + return INVALID_HANDLE_VALUE; |
| 525 | + |
| 526 | + /* get ioinfo pointer and change the handles */ |
| 527 | + pioinfo = _pioinfo(fd); |
| 528 | + old_handle = pioinfo->osfhnd; |
| 529 | + pioinfo->osfhnd = new_handle; |
| 530 | + return old_handle; |
484 | 531 | }
|
485 | 532 |
|
486 | 533 | void winansi_init(void)
|
487 | 534 | {
|
488 |
| - int con1, con2, hwrite_fd; |
| 535 | + int con1, con2; |
489 | 536 | char name[32];
|
490 | 537 |
|
491 | 538 | /* check if either stdout or stderr is a console output screen buffer */
|
@@ -514,39 +561,18 @@ void winansi_init(void)
|
514 | 561 | if (atexit(winansi_exit))
|
515 | 562 | die_errno("atexit(winansi_exit) failed");
|
516 | 563 |
|
517 |
| - /* create a file descriptor for the write end of the pipe */ |
518 |
| - hwrite_fd = _open_osfhandle((long) duplicate_handle(hwrite), _O_BINARY); |
519 |
| - if (hwrite_fd == -1) |
520 |
| - die_errno("_open_osfhandle(%li) failed", (long) hwrite); |
521 |
| - |
522 | 564 | /* redirect stdout / stderr to the pipe */
|
523 | 565 | if (con1)
|
524 |
| - hwrite1 = redirect_console(stdout, &hconsole1, hwrite_fd); |
| 566 | + hconsole1 = swap_osfhnd(1, hwrite1 = duplicate_handle(hwrite)); |
525 | 567 | if (con2)
|
526 |
| - hwrite2 = redirect_console(stderr, &hconsole2, hwrite_fd); |
527 |
| - |
528 |
| - /* close pipe file descriptor (also closes the duped hwrite) */ |
529 |
| - close(hwrite_fd); |
| 568 | + hconsole2 = swap_osfhnd(2, hwrite2 = duplicate_handle(hwrite)); |
530 | 569 | }
|
531 | 570 |
|
532 | 571 | static int is_same_handle(HANDLE hnd, int fd)
|
533 | 572 | {
|
534 | 573 | return hnd != INVALID_HANDLE_VALUE && hnd == (HANDLE) _get_osfhandle(fd);
|
535 | 574 | }
|
536 | 575 |
|
537 |
| -/* |
538 |
| - * Return true if stdout / stderr is a pipe redirecting to the console. |
539 |
| - */ |
540 |
| -int winansi_isatty(int fd) |
541 |
| -{ |
542 |
| - if (fd == 1 && is_same_handle(hwrite1, 1)) |
543 |
| - return 1; |
544 |
| - else if (fd == 2 && is_same_handle(hwrite2, 2)) |
545 |
| - return 1; |
546 |
| - else |
547 |
| - return isatty(fd); |
548 |
| -} |
549 |
| - |
550 | 576 | /*
|
551 | 577 | * Returns the real console handle if stdout / stderr is a pipe redirecting
|
552 | 578 | * to the console. Allows spawn / exec to pass the console to the next process.
|
|
0 commit comments