1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing#kwsys for details.  */
3 #include "kwsysPrivate.h"
4 
5 // Ignore Windows version levels defined by command-line flags.  This
6 // source needs access to all APIs available on the host in order for
7 // the test to run properly.  The test binary is not installed anyway.
8 #undef _WIN32_WINNT
9 #undef NTDDI_VERSION
10 
11 #include KWSYS_HEADER(Encoding.hxx)
12 
13 // Work-around CMake dependency scanning limitation.  This must
14 // duplicate the above list of headers.
15 #if 0
16 #  include "Encoding.hxx.in"
17 #endif
18 
19 #if defined(_WIN32)
20 
21 #  include <algorithm>
22 #  include <iomanip>
23 #  include <iostream>
24 #  include <stdexcept>
25 #  include <string.h>
26 #  include <wchar.h>
27 #  include <windows.h>
28 
29 #  include "testConsoleBuf.hxx"
30 
31 #  if defined(_MSC_VER) && _MSC_VER >= 1800
32 #    define KWSYS_WINDOWS_DEPRECATED_GetVersion
33 #  endif
34 // يونيكود
35 static const WCHAR UnicodeInputTestString[] =
36   L"\u064A\u0648\u0646\u064A\u0643\u0648\u062F!";
37 static UINT TestCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE;
38 
39 static const DWORD waitTimeout = 10 * 1000;
40 static STARTUPINFO startupInfo;
41 static PROCESS_INFORMATION processInfo;
42 static HANDLE beforeInputEvent;
43 static HANDLE afterOutputEvent;
44 static std::string encodedInputTestString;
45 static std::string encodedTestString;
46 
displayError(DWORD errorCode)47 static void displayError(DWORD errorCode)
48 {
49   std::cerr.setf(std::ios::hex, std::ios::basefield);
50   std::cerr << "Failed with error: 0x" << errorCode << "!" << std::endl;
51   LPWSTR message;
52   if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
53                        FORMAT_MESSAGE_FROM_SYSTEM,
54                      nullptr, errorCode, 0, (LPWSTR)&message, 0, nullptr)) {
55     std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message)
56               << std::endl;
57     HeapFree(GetProcessHeap(), 0, message);
58   } else {
59     std::cerr << "FormatMessage() failed with error: 0x" << GetLastError()
60               << "!" << std::endl;
61   }
62   std::cerr.unsetf(std::ios::hex);
63 }
64 
errstream(const char * unused)65 std::basic_streambuf<char>* errstream(const char* unused)
66 {
67   static_cast<void>(unused);
68   return std::cerr.rdbuf();
69 }
70 
errstream(const wchar_t * unused)71 std::basic_streambuf<wchar_t>* errstream(const wchar_t* unused)
72 {
73   static_cast<void>(unused);
74   return std::wcerr.rdbuf();
75 }
76 
77 template <typename T>
dumpBuffers(const T * expected,const T * received,size_t size)78 static void dumpBuffers(const T* expected, const T* received, size_t size)
79 {
80   std::basic_ostream<T> err(errstream(expected));
81   err << "Expected output: '" << std::basic_string<T>(expected, size) << "'"
82       << std::endl;
83   if (err.fail()) {
84     err.clear();
85     err << "--- Error while outputting ---" << std::endl;
86   }
87   err << "Received output: '" << std::basic_string<T>(received, size) << "'"
88       << std::endl;
89   if (err.fail()) {
90     err.clear();
91     err << "--- Error while outputting ---" << std::endl;
92   }
93   std::cerr << "Expected output | Received output" << std::endl;
94   for (size_t i = 0; i < size; i++) {
95     std::cerr << std::setbase(16) << std::setfill('0') << "     "
96               << "0x" << std::setw(8) << static_cast<unsigned int>(expected[i])
97               << " | "
98               << "0x" << std::setw(8)
99               << static_cast<unsigned int>(received[i]);
100     if (static_cast<unsigned int>(expected[i]) !=
101         static_cast<unsigned int>(received[i])) {
102       std::cerr << "   MISMATCH!";
103     }
104     std::cerr << std::endl;
105   }
106   std::cerr << std::endl;
107 }
108 
createProcess(HANDLE hIn,HANDLE hOut,HANDLE hErr)109 static bool createProcess(HANDLE hIn, HANDLE hOut, HANDLE hErr)
110 {
111   BOOL bInheritHandles = FALSE;
112   DWORD dwCreationFlags = 0;
113   memset(&processInfo, 0, sizeof(processInfo));
114   memset(&startupInfo, 0, sizeof(startupInfo));
115   startupInfo.cb = sizeof(startupInfo);
116   startupInfo.dwFlags = STARTF_USESHOWWINDOW;
117   startupInfo.wShowWindow = SW_HIDE;
118   if (hIn || hOut || hErr) {
119     startupInfo.dwFlags |= STARTF_USESTDHANDLES;
120     startupInfo.hStdInput = hIn;
121     startupInfo.hStdOutput = hOut;
122     startupInfo.hStdError = hErr;
123     bInheritHandles = TRUE;
124   }
125 
126   WCHAR cmd[MAX_PATH];
127   if (GetModuleFileNameW(nullptr, cmd, MAX_PATH) == 0) {
128     std::cerr << "GetModuleFileName failed!" << std::endl;
129     return false;
130   }
131   WCHAR* p = cmd + wcslen(cmd);
132   while (p > cmd && *p != L'\\')
133     p--;
134   *(p + 1) = 0;
135   wcscat(cmd, cmdConsoleBufChild);
136   wcscat(cmd, L".exe");
137 
138   bool success =
139     CreateProcessW(nullptr,         // No module name (use command line)
140                    cmd,             // Command line
141                    nullptr,         // Process handle not inheritable
142                    nullptr,         // Thread handle not inheritable
143                    bInheritHandles, // Set handle inheritance
144                    dwCreationFlags,
145                    nullptr,      // Use parent's environment block
146                    nullptr,      // Use parent's starting directory
147                    &startupInfo, // Pointer to STARTUPINFO structure
148                    &processInfo) !=
149     0; // Pointer to PROCESS_INFORMATION structure
150   if (!success) {
151     DWORD lastError = GetLastError();
152     std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd) << ")"
153               << std::endl;
154     displayError(lastError);
155   }
156   return success;
157 }
158 
finishProcess(bool success)159 static void finishProcess(bool success)
160 {
161   if (success) {
162     success =
163       WaitForSingleObject(processInfo.hProcess, waitTimeout) == WAIT_OBJECT_0;
164   };
165   if (!success) {
166     TerminateProcess(processInfo.hProcess, 1);
167   }
168   CloseHandle(processInfo.hProcess);
169   CloseHandle(processInfo.hThread);
170 }
171 
createPipe(PHANDLE readPipe,PHANDLE writePipe)172 static bool createPipe(PHANDLE readPipe, PHANDLE writePipe)
173 {
174   SECURITY_ATTRIBUTES securityAttributes;
175   securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
176   securityAttributes.bInheritHandle = TRUE;
177   securityAttributes.lpSecurityDescriptor = nullptr;
178   return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false
179                                                                       : true;
180 }
181 
finishPipe(HANDLE readPipe,HANDLE writePipe)182 static void finishPipe(HANDLE readPipe, HANDLE writePipe)
183 {
184   if (readPipe != INVALID_HANDLE_VALUE) {
185     CloseHandle(readPipe);
186   }
187   if (writePipe != INVALID_HANDLE_VALUE) {
188     CloseHandle(writePipe);
189   }
190 }
191 
createFile(LPCWSTR fileName)192 static HANDLE createFile(LPCWSTR fileName)
193 {
194   SECURITY_ATTRIBUTES securityAttributes;
195   securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
196   securityAttributes.bInheritHandle = TRUE;
197   securityAttributes.lpSecurityDescriptor = nullptr;
198 
199   HANDLE file =
200     CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE,
201                 0, // do not share
202                 &securityAttributes,
203                 CREATE_ALWAYS, // overwrite existing
204                 FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
205                 nullptr); // no template
206   if (file == INVALID_HANDLE_VALUE) {
207     DWORD lastError = GetLastError();
208     std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")"
209               << std::endl;
210     displayError(lastError);
211   }
212   return file;
213 }
214 
finishFile(HANDLE file)215 static void finishFile(HANDLE file)
216 {
217   if (file != INVALID_HANDLE_VALUE) {
218     CloseHandle(file);
219   }
220 }
221 
222 #  ifndef MAPVK_VK_TO_VSC
223 #    define MAPVK_VK_TO_VSC (0)
224 #  endif
225 
writeInputKeyEvent(INPUT_RECORD inputBuffer[],WCHAR chr)226 static void writeInputKeyEvent(INPUT_RECORD inputBuffer[], WCHAR chr)
227 {
228   inputBuffer[0].EventType = KEY_EVENT;
229   inputBuffer[0].Event.KeyEvent.bKeyDown = TRUE;
230   inputBuffer[0].Event.KeyEvent.wRepeatCount = 1;
231   SHORT keyCode = VkKeyScanW(chr);
232   if (keyCode == -1) {
233     // Character can't be entered with current keyboard layout
234     // Just set any, it doesn't really matter
235     keyCode = 'K';
236   }
237   inputBuffer[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(keyCode);
238   inputBuffer[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(
239     inputBuffer[0].Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC);
240   inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar = chr;
241   inputBuffer[0].Event.KeyEvent.dwControlKeyState = 0;
242   if ((HIBYTE(keyCode) & 1) == 1) {
243     inputBuffer[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
244   }
245   if ((HIBYTE(keyCode) & 2) == 2) {
246     inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED;
247   }
248   if ((HIBYTE(keyCode) & 4) == 4) {
249     inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED;
250   }
251   inputBuffer[1].EventType = inputBuffer[0].EventType;
252   inputBuffer[1].Event.KeyEvent.bKeyDown = FALSE;
253   inputBuffer[1].Event.KeyEvent.wRepeatCount = 1;
254   inputBuffer[1].Event.KeyEvent.wVirtualKeyCode =
255     inputBuffer[0].Event.KeyEvent.wVirtualKeyCode;
256   inputBuffer[1].Event.KeyEvent.wVirtualScanCode =
257     inputBuffer[0].Event.KeyEvent.wVirtualScanCode;
258   inputBuffer[1].Event.KeyEvent.uChar.UnicodeChar =
259     inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar;
260   inputBuffer[1].Event.KeyEvent.dwControlKeyState = 0;
261 }
262 
testPipe()263 static int testPipe()
264 {
265   int didFail = 1;
266   HANDLE inPipeRead = INVALID_HANDLE_VALUE;
267   HANDLE inPipeWrite = INVALID_HANDLE_VALUE;
268   HANDLE outPipeRead = INVALID_HANDLE_VALUE;
269   HANDLE outPipeWrite = INVALID_HANDLE_VALUE;
270   HANDLE errPipeRead = INVALID_HANDLE_VALUE;
271   HANDLE errPipeWrite = INVALID_HANDLE_VALUE;
272   UINT currentCodepage = GetConsoleCP();
273   char buffer[200];
274   char buffer2[200];
275   try {
276     if (!createPipe(&inPipeRead, &inPipeWrite) ||
277         !createPipe(&outPipeRead, &outPipeWrite) ||
278         !createPipe(&errPipeRead, &errPipeWrite)) {
279       throw std::runtime_error("createFile failed!");
280     }
281     if (TestCodepage == CP_ACP) {
282       TestCodepage = GetACP();
283     }
284     if (!SetConsoleCP(TestCodepage)) {
285       throw std::runtime_error("SetConsoleCP failed!");
286     }
287 
288     DWORD bytesWritten = 0;
289     if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(),
290                    (DWORD)encodedInputTestString.size(), &bytesWritten,
291                    nullptr) ||
292         bytesWritten == 0) {
293       throw std::runtime_error("WriteFile failed!");
294     }
295 
296     if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) {
297       try {
298         DWORD status;
299         if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
300             WAIT_OBJECT_0) {
301           std::cerr.setf(std::ios::hex, std::ios::basefield);
302           std::cerr << "WaitForSingleObject returned unexpected status 0x"
303                     << status << std::endl;
304           std::cerr.unsetf(std::ios::hex);
305           throw std::runtime_error("WaitForSingleObject failed!");
306         }
307         DWORD bytesRead = 0;
308         if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead,
309                       nullptr) ||
310             bytesRead == 0) {
311           throw std::runtime_error("ReadFile#1 failed!");
312         }
313         buffer[bytesRead] = 0;
314         if ((bytesRead <
315                encodedTestString.size() + 1 + encodedInputTestString.size() &&
316              !ReadFile(outPipeRead, buffer + bytesRead,
317                        sizeof(buffer) - bytesRead, &bytesRead, nullptr)) ||
318             bytesRead == 0) {
319           throw std::runtime_error("ReadFile#2 failed!");
320         }
321         if (memcmp(buffer, encodedTestString.c_str(),
322                    encodedTestString.size()) == 0 &&
323             memcmp(buffer + encodedTestString.size() + 1,
324                    encodedInputTestString.c_str(),
325                    encodedInputTestString.size()) == 0) {
326           bytesRead = 0;
327           if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead,
328                         nullptr) ||
329               bytesRead == 0) {
330             throw std::runtime_error("ReadFile#3 failed!");
331           }
332           buffer2[bytesRead] = 0;
333           didFail = encodedTestString.compare(0, std::string::npos, buffer2,
334                                               encodedTestString.size()) == 0
335             ? 0
336             : 1;
337         }
338         if (didFail != 0) {
339           std::cerr << "Pipe's output didn't match expected output!"
340                     << std::endl;
341           dumpBuffers<char>(encodedTestString.c_str(), buffer,
342                             encodedTestString.size());
343           dumpBuffers<char>(encodedInputTestString.c_str(),
344                             buffer + encodedTestString.size() + 1,
345                             encodedInputTestString.size());
346           dumpBuffers<char>(encodedTestString.c_str(), buffer2,
347                             encodedTestString.size());
348         }
349       } catch (const std::runtime_error& ex) {
350         DWORD lastError = GetLastError();
351         std::cerr << "In function testPipe, line " << __LINE__ << ": "
352                   << ex.what() << std::endl;
353         displayError(lastError);
354       }
355       finishProcess(didFail == 0);
356     }
357   } catch (const std::runtime_error& ex) {
358     DWORD lastError = GetLastError();
359     std::cerr << "In function testPipe, line " << __LINE__ << ": " << ex.what()
360               << std::endl;
361     displayError(lastError);
362   }
363   finishPipe(inPipeRead, inPipeWrite);
364   finishPipe(outPipeRead, outPipeWrite);
365   finishPipe(errPipeRead, errPipeWrite);
366   SetConsoleCP(currentCodepage);
367   return didFail;
368 }
369 
testFile()370 static int testFile()
371 {
372   int didFail = 1;
373   HANDLE inFile = INVALID_HANDLE_VALUE;
374   HANDLE outFile = INVALID_HANDLE_VALUE;
375   HANDLE errFile = INVALID_HANDLE_VALUE;
376   try {
377     if ((inFile = createFile(L"stdinFile.txt")) == INVALID_HANDLE_VALUE ||
378         (outFile = createFile(L"stdoutFile.txt")) == INVALID_HANDLE_VALUE ||
379         (errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) {
380       throw std::runtime_error("createFile failed!");
381     }
382     DWORD bytesWritten = 0;
383     char buffer[200];
384     char buffer2[200];
385 
386     int length;
387     if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString,
388                                       -1, buffer, sizeof(buffer), nullptr,
389                                       nullptr)) == 0) {
390       throw std::runtime_error("WideCharToMultiByte failed!");
391     }
392     buffer[length - 1] = '\n';
393     if (!WriteFile(inFile, buffer, length, &bytesWritten, nullptr) ||
394         bytesWritten == 0) {
395       throw std::runtime_error("WriteFile failed!");
396     }
397     if (SetFilePointer(inFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
398       throw std::runtime_error("SetFilePointer failed!");
399     }
400 
401     if (createProcess(inFile, outFile, errFile)) {
402       DWORD bytesRead = 0;
403       try {
404         DWORD status;
405         if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
406             WAIT_OBJECT_0) {
407           std::cerr.setf(std::ios::hex, std::ios::basefield);
408           std::cerr << "WaitForSingleObject returned unexpected status 0x"
409                     << status << std::endl;
410           std::cerr.unsetf(std::ios::hex);
411           throw std::runtime_error("WaitForSingleObject failed!");
412         }
413         if (SetFilePointer(outFile, 0, 0, FILE_BEGIN) ==
414             INVALID_SET_FILE_POINTER) {
415           throw std::runtime_error("SetFilePointer#1 failed!");
416         }
417         if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, nullptr) ||
418             bytesRead == 0) {
419           throw std::runtime_error("ReadFile#1 failed!");
420         }
421         buffer[bytesRead] = 0;
422         if (memcmp(buffer, encodedTestString.c_str(),
423                    encodedTestString.size()) == 0 &&
424             memcmp(buffer + encodedTestString.size() + 1,
425                    encodedInputTestString.c_str(),
426                    encodedInputTestString.size()) == 0) {
427           bytesRead = 0;
428           if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) ==
429               INVALID_SET_FILE_POINTER) {
430             throw std::runtime_error("SetFilePointer#2 failed!");
431           }
432 
433           if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead,
434                         nullptr) ||
435               bytesRead == 0) {
436             throw std::runtime_error("ReadFile#2 failed!");
437           }
438           buffer2[bytesRead] = 0;
439           didFail = encodedTestString.compare(0, std::string::npos, buffer2,
440                                               encodedTestString.size()) == 0
441             ? 0
442             : 1;
443         }
444         if (didFail != 0) {
445           std::cerr << "File's output didn't match expected output!"
446                     << std::endl;
447           dumpBuffers<char>(encodedTestString.c_str(), buffer,
448                             encodedTestString.size());
449           dumpBuffers<char>(encodedInputTestString.c_str(),
450                             buffer + encodedTestString.size() + 1,
451                             encodedInputTestString.size());
452           dumpBuffers<char>(encodedTestString.c_str(), buffer2,
453                             encodedTestString.size());
454         }
455       } catch (const std::runtime_error& ex) {
456         DWORD lastError = GetLastError();
457         std::cerr << "In function testFile, line " << __LINE__ << ": "
458                   << ex.what() << std::endl;
459         displayError(lastError);
460       }
461       finishProcess(didFail == 0);
462     }
463   } catch (const std::runtime_error& ex) {
464     DWORD lastError = GetLastError();
465     std::cerr << "In function testFile, line " << __LINE__ << ": " << ex.what()
466               << std::endl;
467     displayError(lastError);
468   }
469   finishFile(inFile);
470   finishFile(outFile);
471   finishFile(errFile);
472   return didFail;
473 }
474 
475 #  ifndef _WIN32_WINNT_VISTA
476 #    define _WIN32_WINNT_VISTA 0x0600
477 #  endif
478 
testConsole()479 static int testConsole()
480 {
481   int didFail = 1;
482   HANDLE parentIn = GetStdHandle(STD_INPUT_HANDLE);
483   HANDLE parentOut = GetStdHandle(STD_OUTPUT_HANDLE);
484   HANDLE parentErr = GetStdHandle(STD_ERROR_HANDLE);
485   HANDLE hIn = parentIn;
486   HANDLE hOut = parentOut;
487   DWORD consoleMode;
488   bool newConsole = false;
489   bool forceNewConsole = false;
490   bool restoreConsole = false;
491   LPCWSTR TestFaceName = L"Lucida Console";
492   const DWORD TestFontFamily = 0x00000036;
493   const DWORD TestFontSize = 0x000c0000;
494   HKEY hConsoleKey;
495   WCHAR FaceName[200];
496   FaceName[0] = 0;
497   DWORD FaceNameSize = sizeof(FaceName);
498   DWORD FontFamily = TestFontFamily;
499   DWORD FontSize = TestFontSize;
500 #  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
501 #    pragma warning(push)
502 #    ifdef __INTEL_COMPILER
503 #      pragma warning(disable : 1478)
504 #    elif defined __clang__
505 #      pragma clang diagnostic push
506 #      pragma clang diagnostic ignored "-Wdeprecated-declarations"
507 #    else
508 #      pragma warning(disable : 4996)
509 #    endif
510 #  endif
511   const bool isVistaOrGreater =
512     LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA);
513 #  ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
514 #    ifdef __clang__
515 #      pragma clang diagnostic pop
516 #    else
517 #      pragma warning(pop)
518 #    endif
519 #  endif
520   if (!isVistaOrGreater) {
521     if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE,
522                       &hConsoleKey) == ERROR_SUCCESS) {
523       DWORD dwordSize = sizeof(DWORD);
524       if (RegQueryValueExW(hConsoleKey, L"FontFamily", nullptr, nullptr,
525                            (LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) {
526         if (FontFamily != TestFontFamily) {
527           RegQueryValueExW(hConsoleKey, L"FaceName", nullptr, nullptr,
528                            (LPBYTE)FaceName, &FaceNameSize);
529           RegQueryValueExW(hConsoleKey, L"FontSize", nullptr, nullptr,
530                            (LPBYTE)&FontSize, &dwordSize);
531 
532           RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
533                          (BYTE*)&TestFontFamily, sizeof(TestFontFamily));
534           RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ,
535                          (BYTE*)TestFaceName,
536                          (DWORD)((wcslen(TestFaceName) + 1) * sizeof(WCHAR)));
537           RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD,
538                          (BYTE*)&TestFontSize, sizeof(TestFontSize));
539 
540           restoreConsole = true;
541           forceNewConsole = true;
542         }
543       } else {
544         std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl;
545       }
546       RegCloseKey(hConsoleKey);
547     } else {
548       std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!"
549                 << std::endl;
550     }
551   }
552   if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) {
553     // Not a real console, let's create new one.
554     FreeConsole();
555     if (!AllocConsole()) {
556       std::cerr << "AllocConsole failed!" << std::endl;
557       return didFail;
558     }
559     SECURITY_ATTRIBUTES securityAttributes;
560     securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
561     securityAttributes.bInheritHandle = TRUE;
562     securityAttributes.lpSecurityDescriptor = nullptr;
563     hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE,
564                       FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
565                       OPEN_EXISTING, 0, nullptr);
566     if (hIn == INVALID_HANDLE_VALUE) {
567       DWORD lastError = GetLastError();
568       std::cerr << "CreateFile(CONIN$)" << std::endl;
569       displayError(lastError);
570     }
571     hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
572                        FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes,
573                        OPEN_EXISTING, 0, nullptr);
574     if (hOut == INVALID_HANDLE_VALUE) {
575       DWORD lastError = GetLastError();
576       std::cerr << "CreateFile(CONOUT$)" << std::endl;
577       displayError(lastError);
578     }
579     SetStdHandle(STD_INPUT_HANDLE, hIn);
580     SetStdHandle(STD_OUTPUT_HANDLE, hOut);
581     SetStdHandle(STD_ERROR_HANDLE, hOut);
582     newConsole = true;
583   }
584 
585 #  if _WIN32_WINNT >= _WIN32_WINNT_VISTA
586   if (isVistaOrGreater) {
587     CONSOLE_FONT_INFOEX consoleFont;
588     memset(&consoleFont, 0, sizeof(consoleFont));
589     consoleFont.cbSize = sizeof(consoleFont);
590     HMODULE kernel32 = LoadLibraryW(L"kernel32.dll");
591     typedef BOOL(WINAPI * GetCurrentConsoleFontExFunc)(
592       HANDLE hConsoleOutput, BOOL bMaximumWindow,
593       PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
594     typedef BOOL(WINAPI * SetCurrentConsoleFontExFunc)(
595       HANDLE hConsoleOutput, BOOL bMaximumWindow,
596       PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
597     GetCurrentConsoleFontExFunc getConsoleFont =
598       (GetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
599                                                   "GetCurrentConsoleFontEx");
600     SetCurrentConsoleFontExFunc setConsoleFont =
601       (SetCurrentConsoleFontExFunc)GetProcAddress(kernel32,
602                                                   "SetCurrentConsoleFontEx");
603     if (getConsoleFont(hOut, FALSE, &consoleFont)) {
604       if (consoleFont.FontFamily != TestFontFamily) {
605         consoleFont.FontFamily = TestFontFamily;
606         wcscpy(consoleFont.FaceName, TestFaceName);
607         if (!setConsoleFont(hOut, FALSE, &consoleFont)) {
608           std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl;
609         }
610       }
611     } else {
612       std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl;
613     }
614   } else {
615 #  endif
616     if (restoreConsole &&
617         RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE,
618                       &hConsoleKey) == ERROR_SUCCESS) {
619       RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD,
620                      (BYTE*)&FontFamily, sizeof(FontFamily));
621       if (FaceName[0] != 0) {
622         RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, (BYTE*)FaceName,
623                        FaceNameSize);
624       } else {
625         RegDeleteValueW(hConsoleKey, L"FaceName");
626       }
627       RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, (BYTE*)&FontSize,
628                      sizeof(FontSize));
629       RegCloseKey(hConsoleKey);
630     }
631 #  if _WIN32_WINNT >= _WIN32_WINNT_VISTA
632   }
633 #  endif
634 
635   if (createProcess(nullptr, nullptr, nullptr)) {
636     try {
637       DWORD status;
638       if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) !=
639           WAIT_OBJECT_0) {
640         std::cerr.setf(std::ios::hex, std::ios::basefield);
641         std::cerr << "WaitForSingleObject returned unexpected status 0x"
642                   << status << std::endl;
643         std::cerr.unsetf(std::ios::hex);
644         throw std::runtime_error("WaitForSingleObject#1 failed!");
645       }
646       INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) /
647                                 sizeof(UnicodeInputTestString[0])) *
648                                2];
649       memset(&inputBuffer, 0, sizeof(inputBuffer));
650       unsigned int i;
651       for (i = 0; i < (sizeof(UnicodeInputTestString) /
652                          sizeof(UnicodeInputTestString[0]) -
653                        1);
654            i++) {
655         writeInputKeyEvent(&inputBuffer[i * 2], UnicodeInputTestString[i]);
656       }
657       writeInputKeyEvent(&inputBuffer[i * 2], VK_RETURN);
658       DWORD eventsWritten = 0;
659       // We need to wait a bit before writing to console so child process have
660       // started waiting for input on stdin.
661       Sleep(300);
662       if (!WriteConsoleInputW(hIn, inputBuffer,
663                               sizeof(inputBuffer) / sizeof(inputBuffer[0]),
664                               &eventsWritten) ||
665           eventsWritten == 0) {
666         throw std::runtime_error("WriteConsoleInput failed!");
667       }
668       if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) !=
669           WAIT_OBJECT_0) {
670         std::cerr.setf(std::ios::hex, std::ios::basefield);
671         std::cerr << "WaitForSingleObject returned unexpected status 0x"
672                   << status << std::endl;
673         std::cerr.unsetf(std::ios::hex);
674         throw std::runtime_error("WaitForSingleObject#2 failed!");
675       }
676       CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo;
677       if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) {
678         throw std::runtime_error("GetConsoleScreenBufferInfo failed!");
679       }
680 
681       COORD coord;
682       DWORD charsRead = 0;
683       coord.X = 0;
684       coord.Y = screenBufferInfo.dwCursorPosition.Y - 4;
685       WCHAR* outputBuffer = new WCHAR[screenBufferInfo.dwSize.X * 4];
686       if (!ReadConsoleOutputCharacterW(hOut, outputBuffer,
687                                        screenBufferInfo.dwSize.X * 4, coord,
688                                        &charsRead) ||
689           charsRead == 0) {
690         delete[] outputBuffer;
691         throw std::runtime_error("ReadConsoleOutputCharacter failed!");
692       }
693       std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString);
694       std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' ');
695       std::wstring wideInputTestString =
696         kwsys::Encoding::ToWide(encodedInputTestString);
697       if (memcmp(outputBuffer, wideTestString.c_str(),
698                  wideTestString.size() * sizeof(wchar_t)) == 0 &&
699           memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1,
700                  wideTestString.c_str(),
701                  wideTestString.size() * sizeof(wchar_t)) == 0 &&
702           memcmp(outputBuffer + screenBufferInfo.dwSize.X * 2,
703                  UnicodeInputTestString,
704                  sizeof(UnicodeInputTestString) - sizeof(WCHAR)) == 0 &&
705           memcmp(outputBuffer + screenBufferInfo.dwSize.X * 3,
706                  wideInputTestString.c_str(),
707                  (wideInputTestString.size() - 1) * sizeof(wchar_t)) == 0) {
708         didFail = 0;
709       } else {
710         std::cerr << "Console's output didn't match expected output!"
711                   << std::endl;
712         dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer,
713                              wideTestString.size());
714         dumpBuffers<wchar_t>(wideTestString.c_str(),
715                              outputBuffer + screenBufferInfo.dwSize.X * 1,
716                              wideTestString.size());
717         dumpBuffers<wchar_t>(
718           UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2,
719           (sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR));
720         dumpBuffers<wchar_t>(wideInputTestString.c_str(),
721                              outputBuffer + screenBufferInfo.dwSize.X * 3,
722                              wideInputTestString.size() - 1);
723       }
724       delete[] outputBuffer;
725     } catch (const std::runtime_error& ex) {
726       DWORD lastError = GetLastError();
727       std::cerr << "In function testConsole, line " << __LINE__ << ": "
728                 << ex.what() << std::endl;
729       displayError(lastError);
730     }
731     finishProcess(didFail == 0);
732   }
733   if (newConsole) {
734     SetStdHandle(STD_INPUT_HANDLE, parentIn);
735     SetStdHandle(STD_OUTPUT_HANDLE, parentOut);
736     SetStdHandle(STD_ERROR_HANDLE, parentErr);
737     CloseHandle(hIn);
738     CloseHandle(hOut);
739     FreeConsole();
740   }
741   return didFail;
742 }
743 
744 #endif
745 
testConsoleBuf(int,char * [])746 int testConsoleBuf(int, char* [])
747 {
748   int ret = 0;
749 
750 #if defined(_WIN32)
751   beforeInputEvent = CreateEventW(nullptr,
752                                   FALSE, // auto-reset event
753                                   FALSE, // initial state is nonsignaled
754                                   BeforeInputEventName); // object name
755   if (!beforeInputEvent) {
756     std::cerr << "CreateEvent#1 failed " << GetLastError() << std::endl;
757     return 1;
758   }
759 
760   afterOutputEvent = CreateEventW(nullptr, FALSE, FALSE, AfterOutputEventName);
761   if (!afterOutputEvent) {
762     std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl;
763     return 1;
764   }
765 
766   encodedTestString = kwsys::Encoding::ToNarrow(std::wstring(
767     UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1));
768   encodedInputTestString = kwsys::Encoding::ToNarrow(
769     std::wstring(UnicodeInputTestString,
770                  sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1));
771   encodedInputTestString += "\n";
772 
773   ret |= testPipe();
774   ret |= testFile();
775   ret |= testConsole();
776 
777   CloseHandle(beforeInputEvent);
778   CloseHandle(afterOutputEvent);
779 #endif
780 
781   return ret;
782 }
783