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