]> git.ipfire.org Git - thirdparty/git.git/blame - compat/winansi.c
The seventh batch
[thirdparty/git.git] / compat / winansi.c
CommitLineData
c09df8a7
PH
1/*
2 * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
3 */
4
1edeb9ab 5#undef NOGDI
05cd988d 6
c09df8a7 7#include "../git-compat-util.h"
1edeb9ab
KB
8#include <wingdi.h>
9#include <winreg.h>
a9b8a09c 10#include "win32.h"
4fa42df9 11#include "win32/lazyload.h"
c09df8a7 12
a9b8a09c
JH
13static int fd_is_interactive[3] = { 0, 0, 0 };
14#define FD_CONSOLE 0x1
15#define FD_SWAPPED 0x2
16#define FD_MSYS 0x4
cbb3f3c9 17
c09df8a7
PH
18/*
19 ANSI codes used by git: m, K
20
21 This file is git-specific. Therefore, this file does not attempt
22 to implement any codes that are not used by git.
c09df8a7
PH
23*/
24
25static HANDLE console;
26static WORD plain_attr;
27static WORD attr;
28static int negative;
eac14f89
KB
29static int non_ascii_used = 0;
30static HANDLE hthread, hread, hwrite;
eac14f89 31static HANDLE hconsole1, hconsole2;
c09df8a7 32
1edeb9ab 33#ifdef __MINGW32__
466931d9 34#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 5
1edeb9ab
KB
35typedef struct _CONSOLE_FONT_INFOEX {
36 ULONG cbSize;
37 DWORD nFont;
38 COORD dwFontSize;
39 UINT FontFamily;
40 UINT FontWeight;
41 WCHAR FaceName[LF_FACESIZE];
42} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX;
43#endif
466931d9 44#endif
1edeb9ab 45
eac14f89 46static void warn_if_raster_font(void)
1edeb9ab 47{
1edeb9ab 48 DWORD fontFamily = 0;
4a9b2049
MA
49 DECLARE_PROC_ADDR(kernel32.dll, BOOL, WINAPI,
50 GetCurrentConsoleFontEx, HANDLE, BOOL,
51 PCONSOLE_FONT_INFOEX);
1edeb9ab 52
eac14f89
KB
53 /* don't bother if output was ascii only */
54 if (!non_ascii_used)
1edeb9ab 55 return;
1edeb9ab
KB
56
57 /* GetCurrentConsoleFontEx is available since Vista */
4fa42df9 58 if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
1edeb9ab
KB
59 CONSOLE_FONT_INFOEX cfi;
60 cfi.cbSize = sizeof(cfi);
4fa42df9 61 if (GetCurrentConsoleFontEx(console, 0, &cfi))
1edeb9ab
KB
62 fontFamily = cfi.FontFamily;
63 } else {
64 /* pre-Vista: check default console font in registry */
65 HKEY hkey;
eac14f89
KB
66 if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, "Console",
67 0, KEY_READ, &hkey)) {
1edeb9ab
KB
68 DWORD size = sizeof(fontFamily);
69 RegQueryValueExA(hkey, "FontFamily", NULL, NULL,
70 (LPVOID) &fontFamily, &size);
71 RegCloseKey(hkey);
72 }
73 }
74
eac14f89
KB
75 if (!(fontFamily & TMPF_TRUETYPE)) {
76 const wchar_t *msg = L"\nWarning: Your console font probably "
77 L"doesn\'t support Unicode. If you experience strange "
78 L"characters in the output, consider switching to a "
79 L"TrueType font such as Consolas!\n";
80 DWORD dummy;
81 WriteConsoleW(console, msg, wcslen(msg), &dummy, NULL);
82 }
1edeb9ab
KB
83}
84
eac14f89 85static int is_console(int fd)
c09df8a7
PH
86{
87 CONSOLE_SCREEN_BUFFER_INFO sbi;
fee807c5 88 DWORD mode;
143e6152 89 HANDLE hcon;
c09df8a7
PH
90
91 static int initialized = 0;
c09df8a7 92
eac14f89
KB
93 /* get OS handle of the file descriptor */
94 hcon = (HANDLE) _get_osfhandle(fd);
143e6152
KB
95 if (hcon == INVALID_HANDLE_VALUE)
96 return 0;
97
eac14f89
KB
98 /* check if its a device (i.e. console, printer, serial port) */
99 if (GetFileType(hcon) != FILE_TYPE_CHAR)
100 return 0;
101
143e6152 102 /* check if its a handle to a console output screen buffer */
fee807c5
JS
103 if (!fd) {
104 if (!GetConsoleMode(hcon, &mode))
105 return 0;
3057da5a
JS
106 /*
107 * This code path is only reached if there is no console
108 * attached to stdout/stderr, i.e. we will not need to output
109 * any text to any console, therefore we might just as well
110 * use black as foreground color.
111 */
112 sbi.wAttributes = 0;
fee807c5 113 } else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
143e6152
KB
114 return 0;
115
a9b8a09c
JH
116 if (fd >= 0 && fd <= 2)
117 fd_is_interactive[fd] |= FD_CONSOLE;
118
eac14f89 119 /* initialize attributes */
143e6152 120 if (!initialized) {
fcd428f4 121 console = hcon;
143e6152
KB
122 attr = plain_attr = sbi.wAttributes;
123 negative = 0;
124 initialized = 1;
125 }
c09df8a7 126
143e6152 127 return 1;
c09df8a7
PH
128}
129
eac14f89
KB
130#define BUFFER_SIZE 4096
131#define MAX_PARAMS 16
132
133static void write_console(unsigned char *str, size_t len)
617ce965 134{
eac14f89
KB
135 /* only called from console_thread, so a static buffer will do */
136 static wchar_t wbuf[2 * BUFFER_SIZE + 1];
137 DWORD dummy;
617ce965 138
eac14f89
KB
139 /* convert utf-8 to utf-16 */
140 int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
b6b066ad
JS
141 if (wlen < 0) {
142 wchar_t *err = L"[invalid]";
143 WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
144 return;
145 }
617ce965 146
eac14f89
KB
147 /* write directly to console */
148 WriteConsoleW(console, wbuf, wlen, &dummy, NULL);
1edeb9ab 149
eac14f89
KB
150 /* remember if non-ascii characters are printed */
151 if (wlen != len)
152 non_ascii_used = 1;
617ce965 153}
c09df8a7
PH
154
155#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
156#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
157
158static void set_console_attr(void)
159{
160 WORD attributes = attr;
161 if (negative) {
162 attributes &= ~FOREGROUND_ALL;
163 attributes &= ~BACKGROUND_ALL;
164
165 /* This could probably use a bitmask
166 instead of a series of ifs */
167 if (attr & FOREGROUND_RED)
168 attributes |= BACKGROUND_RED;
169 if (attr & FOREGROUND_GREEN)
170 attributes |= BACKGROUND_GREEN;
171 if (attr & FOREGROUND_BLUE)
172 attributes |= BACKGROUND_BLUE;
173
174 if (attr & BACKGROUND_RED)
175 attributes |= FOREGROUND_RED;
176 if (attr & BACKGROUND_GREEN)
177 attributes |= FOREGROUND_GREEN;
178 if (attr & BACKGROUND_BLUE)
179 attributes |= FOREGROUND_BLUE;
180 }
181 SetConsoleTextAttribute(console, attributes);
182}
183
1897713f
JS
184static void erase_in_line(void)
185{
186 CONSOLE_SCREEN_BUFFER_INFO sbi;
492f7091 187 DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
1897713f
JS
188
189 if (!console)
190 return;
191
192 GetConsoleScreenBufferInfo(console, &sbi);
193 FillConsoleOutputCharacterA(console, ' ',
194 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
492f7091 195 &dummy);
1897713f
JS
196}
197
eac14f89 198static void set_attr(char func, const int *params, int paramlen)
c09df8a7 199{
eac14f89
KB
200 int i;
201 switch (func) {
c09df8a7 202 case 'm':
eac14f89
KB
203 for (i = 0; i < paramlen; i++) {
204 switch (params[i]) {
c09df8a7
PH
205 case 0: /* reset */
206 attr = plain_attr;
207 negative = 0;
208 break;
209 case 1: /* bold */
210 attr |= FOREGROUND_INTENSITY;
211 break;
212 case 2: /* faint */
213 case 22: /* normal */
214 attr &= ~FOREGROUND_INTENSITY;
215 break;
216 case 3: /* italic */
217 /* Unsupported */
218 break;
219 case 4: /* underline */
220 case 21: /* double underline */
221 /* Wikipedia says this flag does nothing */
222 /* Furthermore, mingw doesn't define this flag
223 attr |= COMMON_LVB_UNDERSCORE; */
224 break;
225 case 24: /* no underline */
226 /* attr &= ~COMMON_LVB_UNDERSCORE; */
227 break;
228 case 5: /* slow blink */
229 case 6: /* fast blink */
230 /* We don't have blink, but we do have
231 background intensity */
232 attr |= BACKGROUND_INTENSITY;
233 break;
234 case 25: /* no blink */
235 attr &= ~BACKGROUND_INTENSITY;
236 break;
237 case 7: /* negative */
238 negative = 1;
239 break;
240 case 27: /* positive */
241 negative = 0;
242 break;
243 case 8: /* conceal */
244 case 28: /* reveal */
245 /* Unsupported */
246 break;
247 case 30: /* Black */
248 attr &= ~FOREGROUND_ALL;
249 break;
250 case 31: /* Red */
251 attr &= ~FOREGROUND_ALL;
252 attr |= FOREGROUND_RED;
253 break;
254 case 32: /* Green */
255 attr &= ~FOREGROUND_ALL;
256 attr |= FOREGROUND_GREEN;
257 break;
258 case 33: /* Yellow */
259 attr &= ~FOREGROUND_ALL;
260 attr |= FOREGROUND_RED | FOREGROUND_GREEN;
261 break;
262 case 34: /* Blue */
263 attr &= ~FOREGROUND_ALL;
264 attr |= FOREGROUND_BLUE;
265 break;
266 case 35: /* Magenta */
267 attr &= ~FOREGROUND_ALL;
268 attr |= FOREGROUND_RED | FOREGROUND_BLUE;
269 break;
270 case 36: /* Cyan */
271 attr &= ~FOREGROUND_ALL;
272 attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
273 break;
274 case 37: /* White */
275 attr |= FOREGROUND_RED |
276 FOREGROUND_GREEN |
277 FOREGROUND_BLUE;
278 break;
279 case 38: /* Unknown */
280 break;
281 case 39: /* reset */
282 attr &= ~FOREGROUND_ALL;
283 attr |= (plain_attr & FOREGROUND_ALL);
284 break;
285 case 40: /* Black */
286 attr &= ~BACKGROUND_ALL;
287 break;
288 case 41: /* Red */
289 attr &= ~BACKGROUND_ALL;
290 attr |= BACKGROUND_RED;
291 break;
292 case 42: /* Green */
293 attr &= ~BACKGROUND_ALL;
294 attr |= BACKGROUND_GREEN;
295 break;
296 case 43: /* Yellow */
297 attr &= ~BACKGROUND_ALL;
298 attr |= BACKGROUND_RED | BACKGROUND_GREEN;
299 break;
300 case 44: /* Blue */
301 attr &= ~BACKGROUND_ALL;
302 attr |= BACKGROUND_BLUE;
303 break;
304 case 45: /* Magenta */
305 attr &= ~BACKGROUND_ALL;
306 attr |= BACKGROUND_RED | BACKGROUND_BLUE;
307 break;
308 case 46: /* Cyan */
309 attr &= ~BACKGROUND_ALL;
310 attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
311 break;
312 case 47: /* White */
313 attr |= BACKGROUND_RED |
314 BACKGROUND_GREEN |
315 BACKGROUND_BLUE;
316 break;
317 case 48: /* Unknown */
318 break;
319 case 49: /* reset */
320 attr &= ~BACKGROUND_ALL;
321 attr |= (plain_attr & BACKGROUND_ALL);
322 break;
323 default:
324 /* Unsupported code */
325 break;
326 }
eac14f89 327 }
c09df8a7
PH
328 set_console_attr();
329 break;
330 case 'K':
1897713f 331 erase_in_line();
c09df8a7
PH
332 break;
333 default:
334 /* Unsupported code */
335 break;
336 }
c09df8a7
PH
337}
338
eac14f89
KB
339enum {
340 TEXT = 0, ESCAPE = 033, BRACKET = '['
341};
c09df8a7 342
eac14f89
KB
343static DWORD WINAPI console_thread(LPVOID unused)
344{
345 unsigned char buffer[BUFFER_SIZE];
346 DWORD bytes;
347 int start, end = 0, c, parampos = 0, state = TEXT;
348 int params[MAX_PARAMS];
349
350 while (1) {
351 /* read next chunk of bytes from the pipe */
352 if (!ReadFile(hread, buffer + end, BUFFER_SIZE - end, &bytes,
353 NULL)) {
354 /* exit if pipe has been closed or disconnected */
355 if (GetLastError() == ERROR_PIPE_NOT_CONNECTED ||
356 GetLastError() == ERROR_BROKEN_PIPE)
357 break;
358 /* ignore other errors */
359 continue;
360 }
617ce965 361
eac14f89
KB
362 /* scan the bytes and handle ANSI control codes */
363 bytes += end;
364 start = end = 0;
365 while (end < bytes) {
366 c = buffer[end++];
367 switch (state) {
368 case TEXT:
369 if (c == ESCAPE) {
370 /* print text seen so far */
371 if (end - 1 > start)
372 write_console(buffer + start,
373 end - 1 - start);
374
375 /* then start parsing escape sequence */
376 start = end - 1;
377 memset(params, 0, sizeof(params));
378 parampos = 0;
379 state = ESCAPE;
380 }
381 break;
382
383 case ESCAPE:
384 /* continue if "\033[", otherwise bail out */
385 state = (c == BRACKET) ? BRACKET : TEXT;
386 break;
387
388 case BRACKET:
389 /* parse [0-9;]* into array of parameters */
390 if (c >= '0' && c <= '9') {
391 params[parampos] *= 10;
392 params[parampos] += c - '0';
393 } else if (c == ';') {
394 /*
395 * next parameter, bail out if out of
396 * bounds
397 */
398 parampos++;
399 if (parampos >= MAX_PARAMS)
400 state = TEXT;
401 } else {
402 /*
403 * end of escape sequence, change
404 * console attributes
405 */
406 set_attr(c, params, parampos + 1);
407 start = end;
408 state = TEXT;
409 }
410 break;
411 }
412 }
c09df8a7 413
eac14f89
KB
414 /* print remaining text unless parsing an escape sequence */
415 if (state == TEXT && end > start) {
416 /* check for incomplete UTF-8 sequences and fix end */
417 if (buffer[end - 1] >= 0x80) {
418 if (buffer[end -1] >= 0xc0)
419 end--;
420 else if (end - 1 > start &&
421 buffer[end - 2] >= 0xe0)
422 end -= 2;
423 else if (end - 2 > start &&
424 buffer[end - 3] >= 0xf0)
425 end -= 3;
c09df8a7
PH
426 }
427
eac14f89
KB
428 /* print remaining complete UTF-8 sequences */
429 if (end > start)
430 write_console(buffer + start, end - start);
c09df8a7 431
eac14f89
KB
432 /* move remaining bytes to the front */
433 if (end < bytes)
434 memmove(buffer, buffer + end, bytes - end);
435 end = bytes - end;
c09df8a7 436 } else {
eac14f89
KB
437 /* all data has been consumed, mark buffer empty */
438 end = 0;
c09df8a7
PH
439 }
440 }
c09df8a7 441
eac14f89
KB
442 /* check if the console font supports unicode */
443 warn_if_raster_font();
c09df8a7 444
eac14f89
KB
445 CloseHandle(hread);
446 return 0;
c09df8a7
PH
447}
448
eac14f89 449static void winansi_exit(void)
c09df8a7 450{
eac14f89
KB
451 /* flush all streams */
452 _flushall();
453
454 /* signal console thread to exit */
455 FlushFileBuffers(hwrite);
456 DisconnectNamedPipe(hwrite);
457
458 /* wait for console thread to copy remaining data */
459 WaitForSingleObject(hthread, INFINITE);
460
461 /* cleanup handles... */
eac14f89
KB
462 CloseHandle(hwrite);
463 CloseHandle(hthread);
464}
c09df8a7 465
eac14f89
KB
466static void die_lasterr(const char *fmt, ...)
467{
468 va_list params;
469 va_start(params, fmt);
470 errno = err_win_to_posix(GetLastError());
471 die_errno(fmt, params);
472 va_end(params);
473}
c09df8a7 474
ff8978d5
JS
475#undef dup2
476int winansi_dup2(int oldfd, int newfd)
477{
478 int ret = dup2(oldfd, newfd);
479
480 if (!ret && newfd >= 0 && newfd <= 2)
481 fd_is_interactive[newfd] = oldfd < 0 || oldfd > 2 ?
482 0 : fd_is_interactive[oldfd];
483
484 return ret;
485}
486
eac14f89
KB
487static HANDLE duplicate_handle(HANDLE hnd)
488{
489 HANDLE hresult, hproc = GetCurrentProcess();
490 if (!DuplicateHandle(hproc, hnd, hproc, &hresult, 0, TRUE,
491 DUPLICATE_SAME_ACCESS))
7c00bc39
JS
492 die_lasterr("DuplicateHandle(%li) failed",
493 (long) (intptr_t) hnd);
eac14f89
KB
494 return hresult;
495}
c09df8a7 496
fcd428f4
KB
497static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
498{
a9b8a09c
JH
499 /*
500 * Create a copy of the original handle associated with fd
501 * because the original will get closed when we dup2().
502 */
503 HANDLE handle = (HANDLE)_get_osfhandle(fd);
504 HANDLE duplicate = duplicate_handle(handle);
c09df8a7 505
a9b8a09c
JH
506 /* Create a temp fd associated with the already open "new_handle". */
507 int new_fd = _open_osfhandle((intptr_t)new_handle, O_BINARY);
c09df8a7 508
a9b8a09c 509 assert((fd == 1) || (fd == 2));
fcd428f4 510
a9b8a09c
JH
511 /*
512 * Use stock dup2() to re-bind fd to the new handle. Note that
513 * this will implicitly close(1) and close both fd=1 and the
514 * originally associated handle. It will open a new fd=1 and
515 * call DuplicateHandle() on the handle associated with new_fd.
516 * It is because of this implicit close() that we created the
517 * copy of the original.
518 *
1d3f065e
JS
519 * Note that we need to update the cached console handle to the
520 * duplicated one because the dup2() call will implicitly close
521 * the original one.
a9b8a09c
JH
522 *
523 * Note that dup2() when given target := {0,1,2} will also
524 * call SetStdHandle(), so we don't need to worry about that.
525 */
a9b8a09c
JH
526 if (console == handle)
527 console = duplicate;
1d3f065e 528 dup2(new_fd, fd);
fcd428f4 529
a9b8a09c
JH
530 /* Close the temp fd. This explicitly closes "new_handle"
531 * (because it has been associated with it).
532 */
533 close(new_fd);
fcd428f4 534
a4d92d57
JS
535 if (fd == 2)
536 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
a9b8a09c 537 fd_is_interactive[fd] |= FD_SWAPPED;
fcd428f4 538
a9b8a09c 539 return duplicate;
c09df8a7
PH
540}
541
f7f90e0f
KB
542#ifdef DETECT_MSYS_TTY
543
544#include <winternl.h>
5f3ff780
JH
545
546#if defined(_MSC_VER)
547
548typedef struct _OBJECT_NAME_INFORMATION
549{
550 UNICODE_STRING Name;
41616ef6 551 WCHAR NameBuffer[FLEX_ARRAY];
5f3ff780
JH
552} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
553
554#define ObjectNameInformation 1
555
556#else
f7f90e0f 557#include <ntstatus.h>
5f3ff780 558#endif
f7f90e0f
KB
559
560static void detect_msys_tty(int fd)
561{
562 ULONG result;
563 BYTE buffer[1024];
564 POBJECT_NAME_INFORMATION nameinfo = (POBJECT_NAME_INFORMATION) buffer;
565 PWSTR name;
566
567 /* check if fd is a pipe */
568 HANDLE h = (HANDLE) _get_osfhandle(fd);
569 if (GetFileType(h) != FILE_TYPE_PIPE)
570 return;
571
572 /* get pipe name */
573 if (!NT_SUCCESS(NtQueryObject(h, ObjectNameInformation,
574 buffer, sizeof(buffer) - 2, &result)))
575 return;
576 name = nameinfo->Name.Buffer;
c46458e8 577 name[nameinfo->Name.Length / sizeof(*name)] = 0;
f7f90e0f 578
86924838
AD
579 /*
580 * Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX')
581 * or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX')
582 */
583 if ((!wcsstr(name, L"msys-") && !wcsstr(name, L"cygwin-")) ||
584 !wcsstr(name, L"-pty"))
f7f90e0f
KB
585 return;
586
a4d92d57
JS
587 if (fd == 2)
588 setvbuf(stderr, NULL, _IONBF, BUFSIZ);
a9b8a09c 589 fd_is_interactive[fd] |= FD_MSYS;
f7f90e0f
KB
590}
591
592#endif
593
a9b8a09c
JH
594/*
595 * Wrapper for isatty(). Most calls in the main git code
596 * call isatty(1 or 2) to see if the instance is interactive
597 * and should: be colored, show progress, paginate output.
598 * We lie and give results for what the descriptor WAS at
599 * startup (and ignore any pipe redirection we internally
600 * do).
601 */
602#undef isatty
cbb3f3c9
JS
603int winansi_isatty(int fd)
604{
a9b8a09c
JH
605 if (fd >= 0 && fd <= 2)
606 return fd_is_interactive[fd] != 0;
607 return isatty(fd);
cbb3f3c9
JS
608}
609
eac14f89 610void winansi_init(void)
c09df8a7 611{
fcd428f4 612 int con1, con2;
94238859 613 wchar_t name[32];
c09df8a7 614
eac14f89
KB
615 /* check if either stdout or stderr is a console output screen buffer */
616 con1 = is_console(1);
617 con2 = is_console(2);
a9b8a09c
JH
618
619 /* Also compute console bit for fd 0 even though we don't need the result here. */
620 is_console(0);
621
f7f90e0f
KB
622 if (!con1 && !con2) {
623#ifdef DETECT_MSYS_TTY
624 /* check if stdin / stdout / stderr are MSYS2 pty pipes */
625 detect_msys_tty(0);
626 detect_msys_tty(1);
627 detect_msys_tty(2);
628#endif
eac14f89 629 return;
f7f90e0f 630 }
c09df8a7 631
eac14f89 632 /* create a named pipe to communicate with the console thread */
94238859
JS
633 if (swprintf(name, ARRAY_SIZE(name) - 1, L"\\\\.\\pipe\\winansi%lu",
634 GetCurrentProcessId()) < 0)
635 die("Could not initialize winansi pipe name");
636 hwrite = CreateNamedPipeW(name, PIPE_ACCESS_OUTBOUND,
eac14f89
KB
637 PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL);
638 if (hwrite == INVALID_HANDLE_VALUE)
639 die_lasterr("CreateNamedPipe failed");
640
94238859 641 hread = CreateFileW(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
eac14f89
KB
642 if (hread == INVALID_HANDLE_VALUE)
643 die_lasterr("CreateFile for named pipe failed");
644
645 /* start console spool thread on the pipe's read end */
646 hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL);
592bcab6 647 if (!hthread)
eac14f89
KB
648 die_lasterr("CreateThread(console_thread) failed");
649
650 /* schedule cleanup routine */
651 if (atexit(winansi_exit))
652 die_errno("atexit(winansi_exit) failed");
653
eac14f89
KB
654 /* redirect stdout / stderr to the pipe */
655 if (con1)
51822653 656 hconsole1 = swap_osfhnd(1, duplicate_handle(hwrite));
eac14f89 657 if (con2)
51822653 658 hconsole2 = swap_osfhnd(2, duplicate_handle(hwrite));
eac14f89 659}
c09df8a7 660
eac14f89
KB
661/*
662 * Returns the real console handle if stdout / stderr is a pipe redirecting
663 * to the console. Allows spawn / exec to pass the console to the next process.
664 */
665HANDLE winansi_get_osfhandle(int fd)
666{
c5a03b1e
JS
667 HANDLE ret;
668
a9b8a09c
JH
669 if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
670 return hconsole1;
671 if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
672 return hconsole2;
673
c5a03b1e
JS
674 ret = (HANDLE)_get_osfhandle(fd);
675
676 /*
677 * There are obviously circumstances under which _get_osfhandle()
678 * returns (HANDLE)-2. This is not documented anywhere, but that is so
679 * clearly an invalid handle value that we can just work around this
680 * and return the correct value for invalid handles.
681 */
682 return ret == (HANDLE)-2 ? INVALID_HANDLE_VALUE : ret;
c09df8a7 683}