mirror of https://github.com/Kitware/CMake.git
Browse Source
Merge branch 'upstream-KWSys' into stdio-console
Merge branch 'upstream-KWSys' into stdio-console
# By KWSys Upstream * upstream-KWSys: KWSys 2025-05-14 (1d72955e)release

5 changed files with 1 additions and 1299 deletions
-
24Source/kwsys/CMakeLists.txt
-
398Source/kwsys/ConsoleBuf.hxx.in
-
806Source/kwsys/testConsoleBuf.cxx
-
17Source/kwsys/testConsoleBuf.hxx
-
55Source/kwsys/testConsoleBufChild.cxx
@ -1,398 +0,0 @@ |
|||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
|||
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */ |
|||
#ifndef @KWSYS_NAMESPACE@_ConsoleBuf_hxx |
|||
#define @KWSYS_NAMESPACE@_ConsoleBuf_hxx |
|||
|
|||
#include <@KWSYS_NAMESPACE@/Configure.hxx> |
|||
|
|||
#include <@KWSYS_NAMESPACE@/Encoding.hxx> |
|||
|
|||
#include <cstring> |
|||
#include <iostream> |
|||
#include <sstream> |
|||
#include <stdexcept> |
|||
#include <streambuf> |
|||
#include <string> |
|||
|
|||
#if defined(_WIN32) |
|||
# include <windows.h> |
|||
# if __cplusplus >= 201103L |
|||
# include <system_error> |
|||
# endif |
|||
#endif |
|||
|
|||
namespace @KWSYS_NAMESPACE@ { |
|||
#if defined(_WIN32) |
|||
|
|||
template <class CharT, class Traits = std::char_traits<CharT> > |
|||
class BasicConsoleBuf : public std::basic_streambuf<CharT, Traits> |
|||
{ |
|||
public: |
|||
typedef typename Traits::int_type int_type; |
|||
typedef typename Traits::char_type char_type; |
|||
|
|||
class Manager |
|||
{ |
|||
public: |
|||
Manager(std::basic_ios<CharT, Traits>& ios, bool const err = false) |
|||
: m_consolebuf(0) |
|||
{ |
|||
m_ios = &ios; |
|||
try { |
|||
m_consolebuf = new BasicConsoleBuf<CharT, Traits>(err); |
|||
m_streambuf = m_ios->rdbuf(m_consolebuf); |
|||
} catch (std::runtime_error const& ex) { |
|||
std::cerr << "Failed to create ConsoleBuf!" << std::endl |
|||
<< ex.what() << std::endl; |
|||
}; |
|||
} |
|||
|
|||
BasicConsoleBuf<CharT, Traits>* GetConsoleBuf() { return m_consolebuf; } |
|||
|
|||
void SetUTF8Pipes() |
|||
{ |
|||
if (m_consolebuf) { |
|||
m_consolebuf->input_pipe_codepage = CP_UTF8; |
|||
m_consolebuf->output_pipe_codepage = CP_UTF8; |
|||
m_consolebuf->activateCodepageChange(); |
|||
} |
|||
} |
|||
|
|||
~Manager() |
|||
{ |
|||
if (m_consolebuf) { |
|||
delete m_consolebuf; |
|||
m_ios->rdbuf(m_streambuf); |
|||
} |
|||
} |
|||
|
|||
private: |
|||
std::basic_ios<CharT, Traits>* m_ios; |
|||
std::basic_streambuf<CharT, Traits>* m_streambuf; |
|||
BasicConsoleBuf<CharT, Traits>* m_consolebuf; |
|||
}; |
|||
|
|||
BasicConsoleBuf(bool const err = false) |
|||
: flush_on_newline(true) |
|||
, input_pipe_codepage(0) |
|||
, output_pipe_codepage(0) |
|||
, input_file_codepage(CP_UTF8) |
|||
, output_file_codepage(CP_UTF8) |
|||
, m_consolesCodepage(0) |
|||
{ |
|||
m_hInput = ::GetStdHandle(STD_INPUT_HANDLE); |
|||
checkHandle(true, "STD_INPUT_HANDLE"); |
|||
if (!setActiveInputCodepage()) { |
|||
throw std::runtime_error("setActiveInputCodepage failed!"); |
|||
} |
|||
m_hOutput = err ? ::GetStdHandle(STD_ERROR_HANDLE) |
|||
: ::GetStdHandle(STD_OUTPUT_HANDLE); |
|||
checkHandle(false, err ? "STD_ERROR_HANDLE" : "STD_OUTPUT_HANDLE"); |
|||
if (!setActiveOutputCodepage()) { |
|||
throw std::runtime_error("setActiveOutputCodepage failed!"); |
|||
} |
|||
_setg(); |
|||
_setp(); |
|||
} |
|||
|
|||
~BasicConsoleBuf() throw() { sync(); } |
|||
|
|||
bool activateCodepageChange() |
|||
{ |
|||
return setActiveInputCodepage() && setActiveOutputCodepage(); |
|||
} |
|||
|
|||
protected: |
|||
virtual int sync() |
|||
{ |
|||
bool success = true; |
|||
if (m_hInput && m_isConsoleInput && |
|||
::FlushConsoleInputBuffer(m_hInput) == 0) { |
|||
success = false; |
|||
} |
|||
if (m_hOutput && !m_obuffer.empty()) { |
|||
std::wstring const wbuffer = getBuffer(m_obuffer); |
|||
if (m_isConsoleOutput) { |
|||
DWORD charsWritten; |
|||
success = |
|||
::WriteConsoleW(m_hOutput, wbuffer.c_str(), (DWORD)wbuffer.size(), |
|||
&charsWritten, nullptr) == 0 |
|||
? false |
|||
: true; |
|||
} else { |
|||
DWORD bytesWritten; |
|||
std::string buffer; |
|||
success = encodeOutputBuffer(wbuffer, buffer); |
|||
if (success) { |
|||
success = |
|||
::WriteFile(m_hOutput, buffer.c_str(), (DWORD)buffer.size(), |
|||
&bytesWritten, nullptr) == 0 |
|||
? false |
|||
: true; |
|||
} |
|||
} |
|||
} |
|||
m_ibuffer.clear(); |
|||
m_obuffer.clear(); |
|||
_setg(); |
|||
_setp(); |
|||
return success ? 0 : -1; |
|||
} |
|||
|
|||
virtual int_type underflow() |
|||
{ |
|||
if (this->gptr() >= this->egptr()) { |
|||
if (!m_hInput) { |
|||
_setg(true); |
|||
return Traits::eof(); |
|||
} |
|||
if (m_isConsoleInput) { |
|||
// ReadConsole doesn't tell if there's more input available |
|||
// don't support reading more characters than this |
|||
wchar_t wbuffer[8192]; |
|||
DWORD charsRead; |
|||
if (ReadConsoleW(m_hInput, wbuffer, |
|||
(sizeof(wbuffer) / sizeof(wbuffer[0])), &charsRead, |
|||
nullptr) == 0 || |
|||
charsRead == 0) { |
|||
_setg(true); |
|||
return Traits::eof(); |
|||
} |
|||
setBuffer(std::wstring(wbuffer, charsRead), m_ibuffer); |
|||
} else { |
|||
std::wstring wbuffer; |
|||
std::string strbuffer; |
|||
DWORD bytesRead; |
|||
LARGE_INTEGER size; |
|||
if (GetFileSizeEx(m_hInput, &size) == 0) { |
|||
_setg(true); |
|||
return Traits::eof(); |
|||
} |
|||
char* buffer = new char[size.LowPart]; |
|||
while (ReadFile(m_hInput, buffer, size.LowPart, &bytesRead, nullptr) == |
|||
0) { |
|||
if (GetLastError() == ERROR_MORE_DATA) { |
|||
strbuffer += std::string(buffer, bytesRead); |
|||
continue; |
|||
} |
|||
_setg(true); |
|||
delete[] buffer; |
|||
return Traits::eof(); |
|||
} |
|||
if (bytesRead > 0) { |
|||
strbuffer += std::string(buffer, bytesRead); |
|||
} |
|||
delete[] buffer; |
|||
if (!decodeInputBuffer(strbuffer, wbuffer)) { |
|||
_setg(true); |
|||
return Traits::eof(); |
|||
} |
|||
setBuffer(wbuffer, m_ibuffer); |
|||
} |
|||
_setg(); |
|||
} |
|||
return Traits::to_int_type(*this->gptr()); |
|||
} |
|||
|
|||
virtual int_type overflow(int_type ch = Traits::eof()) |
|||
{ |
|||
if (!Traits::eq_int_type(ch, Traits::eof())) { |
|||
char_type chr = Traits::to_char_type(ch); |
|||
m_obuffer += chr; |
|||
if ((flush_on_newline && Traits::eq(chr, '\n')) || |
|||
Traits::eq_int_type(ch, 0x00)) { |
|||
sync(); |
|||
} |
|||
return ch; |
|||
} |
|||
sync(); |
|||
return Traits::eof(); |
|||
} |
|||
|
|||
public: |
|||
bool flush_on_newline; |
|||
UINT input_pipe_codepage; |
|||
UINT output_pipe_codepage; |
|||
UINT input_file_codepage; |
|||
UINT output_file_codepage; |
|||
|
|||
private: |
|||
HANDLE m_hInput; |
|||
HANDLE m_hOutput; |
|||
std::basic_string<char_type> m_ibuffer; |
|||
std::basic_string<char_type> m_obuffer; |
|||
bool m_isConsoleInput; |
|||
bool m_isConsoleOutput; |
|||
UINT m_activeInputCodepage; |
|||
UINT m_activeOutputCodepage; |
|||
UINT m_consolesCodepage; |
|||
void checkHandle(bool input, std::string handleName) |
|||
{ |
|||
if ((input && m_hInput == INVALID_HANDLE_VALUE) || |
|||
(!input && m_hOutput == INVALID_HANDLE_VALUE)) { |
|||
std::string errmsg = |
|||
"GetStdHandle(" + handleName + ") returned INVALID_HANDLE_VALUE"; |
|||
# if __cplusplus >= 201103L |
|||
throw std::system_error(::GetLastError(), std::system_category(), |
|||
errmsg); |
|||
# else |
|||
throw std::runtime_error(errmsg); |
|||
# endif |
|||
} |
|||
} |
|||
UINT getConsolesCodepage() |
|||
{ |
|||
if (!m_consolesCodepage) { |
|||
m_consolesCodepage = GetConsoleCP(); |
|||
if (!m_consolesCodepage) { |
|||
m_consolesCodepage = GetACP(); |
|||
} |
|||
} |
|||
return m_consolesCodepage; |
|||
} |
|||
bool setActiveInputCodepage() |
|||
{ |
|||
m_isConsoleInput = false; |
|||
switch (GetFileType(m_hInput)) { |
|||
case FILE_TYPE_DISK: |
|||
m_activeInputCodepage = input_file_codepage; |
|||
break; |
|||
case FILE_TYPE_CHAR: |
|||
// Check for actual console. |
|||
DWORD consoleMode; |
|||
m_isConsoleInput = |
|||
GetConsoleMode(m_hInput, &consoleMode) == 0 ? false : true; |
|||
if (m_isConsoleInput) { |
|||
break; |
|||
} |
|||
@KWSYS_NAMESPACE@_FALLTHROUGH; |
|||
case FILE_TYPE_PIPE: |
|||
m_activeInputCodepage = input_pipe_codepage; |
|||
break; |
|||
default: |
|||
return false; |
|||
} |
|||
if (!m_isConsoleInput && m_activeInputCodepage == 0) { |
|||
m_activeInputCodepage = getConsolesCodepage(); |
|||
} |
|||
return true; |
|||
} |
|||
bool setActiveOutputCodepage() |
|||
{ |
|||
m_isConsoleOutput = false; |
|||
switch (GetFileType(m_hOutput)) { |
|||
case FILE_TYPE_DISK: |
|||
m_activeOutputCodepage = output_file_codepage; |
|||
break; |
|||
case FILE_TYPE_CHAR: |
|||
// Check for actual console. |
|||
DWORD consoleMode; |
|||
m_isConsoleOutput = |
|||
GetConsoleMode(m_hOutput, &consoleMode) == 0 ? false : true; |
|||
if (m_isConsoleOutput) { |
|||
break; |
|||
} |
|||
@KWSYS_NAMESPACE@_FALLTHROUGH; |
|||
case FILE_TYPE_PIPE: |
|||
m_activeOutputCodepage = output_pipe_codepage; |
|||
break; |
|||
default: |
|||
return false; |
|||
} |
|||
if (!m_isConsoleOutput && m_activeOutputCodepage == 0) { |
|||
m_activeOutputCodepage = getConsolesCodepage(); |
|||
} |
|||
return true; |
|||
} |
|||
void _setg(bool empty = false) |
|||
{ |
|||
if (!empty) { |
|||
this->setg((char_type*)m_ibuffer.data(), (char_type*)m_ibuffer.data(), |
|||
(char_type*)m_ibuffer.data() + m_ibuffer.size()); |
|||
} else { |
|||
this->setg((char_type*)m_ibuffer.data(), |
|||
(char_type*)m_ibuffer.data() + m_ibuffer.size(), |
|||
(char_type*)m_ibuffer.data() + m_ibuffer.size()); |
|||
} |
|||
} |
|||
void _setp() |
|||
{ |
|||
this->setp((char_type*)m_obuffer.data(), |
|||
(char_type*)m_obuffer.data() + m_obuffer.size()); |
|||
} |
|||
bool encodeOutputBuffer(std::wstring const wbuffer, std::string& buffer) |
|||
{ |
|||
if (wbuffer.size() == 0) { |
|||
buffer = std::string(); |
|||
return true; |
|||
} |
|||
int const length = |
|||
WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), |
|||
(int)wbuffer.size(), nullptr, 0, nullptr, nullptr); |
|||
char* buf = new char[length]; |
|||
bool const success = |
|||
WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), |
|||
(int)wbuffer.size(), buf, length, nullptr, |
|||
nullptr) > 0 |
|||
? true |
|||
: false; |
|||
buffer = std::string(buf, length); |
|||
delete[] buf; |
|||
return success; |
|||
} |
|||
bool decodeInputBuffer(std::string const buffer, std::wstring& wbuffer) |
|||
{ |
|||
size_t length = buffer.length(); |
|||
if (length == 0) { |
|||
wbuffer = std::wstring(); |
|||
return true; |
|||
} |
|||
int actualCodepage = m_activeInputCodepage; |
|||
char const BOM_UTF8[] = { char(0xEF), char(0xBB), char(0xBF) }; |
|||
char const* data = buffer.data(); |
|||
size_t const BOMsize = sizeof(BOM_UTF8); |
|||
if (length >= BOMsize && std::memcmp(data, BOM_UTF8, BOMsize) == 0) { |
|||
// PowerShell uses UTF-8 with BOM for pipes |
|||
actualCodepage = CP_UTF8; |
|||
data += BOMsize; |
|||
length -= BOMsize; |
|||
} |
|||
size_t const wlength = static_cast<size_t>(MultiByteToWideChar( |
|||
actualCodepage, 0, data, static_cast<int>(length), nullptr, 0)); |
|||
wchar_t* wbuf = new wchar_t[wlength]; |
|||
bool const success = |
|||
MultiByteToWideChar(actualCodepage, 0, data, static_cast<int>(length), |
|||
wbuf, static_cast<int>(wlength)) > 0 |
|||
? true |
|||
: false; |
|||
wbuffer = std::wstring(wbuf, wlength); |
|||
delete[] wbuf; |
|||
return success; |
|||
} |
|||
std::wstring getBuffer(std::basic_string<char> const buffer) |
|||
{ |
|||
return Encoding::ToWide(buffer); |
|||
} |
|||
std::wstring getBuffer(std::basic_string<wchar_t> const buffer) |
|||
{ |
|||
return buffer; |
|||
} |
|||
void setBuffer(std::wstring const wbuffer, std::basic_string<char>& target) |
|||
{ |
|||
target = Encoding::ToNarrow(wbuffer); |
|||
} |
|||
void setBuffer(std::wstring const wbuffer, |
|||
std::basic_string<wchar_t>& target) |
|||
{ |
|||
target = wbuffer; |
|||
} |
|||
|
|||
}; // BasicConsoleBuf class |
|||
|
|||
typedef BasicConsoleBuf<char> ConsoleBuf; |
|||
typedef BasicConsoleBuf<wchar_t> WConsoleBuf; |
|||
|
|||
#endif |
|||
} // KWSYS_NAMESPACE |
|||
|
|||
#endif |
@ -1,806 +0,0 @@ |
|||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|||
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
|
|||
#include "kwsysPrivate.h"
|
|||
|
|||
// Ignore Windows version levels defined by command-line flags. This
|
|||
// source needs access to all APIs available on the host in order for
|
|||
// the test to run properly. The test binary is not installed anyway.
|
|||
#undef _WIN32_WINNT
|
|||
#undef NTDDI_VERSION
|
|||
|
|||
#include KWSYS_HEADER(Encoding.hxx)
|
|||
|
|||
// Work-around CMake dependency scanning limitation. This must
|
|||
// duplicate the above list of headers.
|
|||
#if 0
|
|||
# include "Encoding.hxx.in"
|
|||
#endif
|
|||
|
|||
#if defined(_WIN32)
|
|||
|
|||
# include <algorithm>
|
|||
# include <iomanip>
|
|||
# include <iostream>
|
|||
# include <stdexcept>
|
|||
# include <string.h>
|
|||
# include <wchar.h>
|
|||
# include <windows.h>
|
|||
|
|||
# include "testConsoleBuf.hxx"
|
|||
|
|||
# if defined(_MSC_VER) && _MSC_VER >= 1800
|
|||
# define KWSYS_WINDOWS_DEPRECATED_GetVersion
|
|||
# endif
|
|||
// يونيكود
|
|||
static const WCHAR UnicodeInputTestString[] = |
|||
L"\u064A\u0648\u0646\u064A\u0643\u0648\u062F!"; |
|||
static UINT TestCodepage = KWSYS_ENCODING_DEFAULT_CODEPAGE; |
|||
|
|||
static const DWORD waitTimeout = 10 * 1000; |
|||
static STARTUPINFO startupInfo; |
|||
static PROCESS_INFORMATION processInfo; |
|||
static HANDLE beforeInputEvent; |
|||
static HANDLE afterOutputEvent; |
|||
static std::string encodedInputTestString; |
|||
static std::string encodedTestString; |
|||
|
|||
static void displayError(DWORD errorCode) |
|||
{ |
|||
std::cerr.setf(std::ios::hex, std::ios::basefield); |
|||
std::cerr << "Failed with error: 0x" << errorCode << "!" << std::endl; |
|||
LPWSTR message; |
|||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
|||
FORMAT_MESSAGE_FROM_SYSTEM, |
|||
nullptr, errorCode, 0, (LPWSTR)&message, 0, nullptr)) { |
|||
std::cerr << "Error message: " << kwsys::Encoding::ToNarrow(message) |
|||
<< std::endl; |
|||
HeapFree(GetProcessHeap(), 0, message); |
|||
} else { |
|||
std::cerr << "FormatMessage() failed with error: 0x" << GetLastError() |
|||
<< "!" << std::endl; |
|||
} |
|||
std::cerr.unsetf(std::ios::hex); |
|||
} |
|||
|
|||
std::basic_streambuf<char>* errstream(char const* unused) |
|||
{ |
|||
static_cast<void>(unused); |
|||
return std::cerr.rdbuf(); |
|||
} |
|||
|
|||
std::basic_streambuf<wchar_t>* errstream(wchar_t const* unused) |
|||
{ |
|||
static_cast<void>(unused); |
|||
return std::wcerr.rdbuf(); |
|||
} |
|||
|
|||
template <typename T> |
|||
static void dumpBuffers(T const* expected, T const* received, size_t size) |
|||
{ |
|||
std::basic_ostream<T> err(errstream(expected)); |
|||
err << "Expected output: '" << std::basic_string<T>(expected, size) << "'" |
|||
<< std::endl; |
|||
if (err.fail()) { |
|||
err.clear(); |
|||
err << "--- Error while outputting ---" << std::endl; |
|||
} |
|||
err << "Received output: '" << std::basic_string<T>(received, size) << "'" |
|||
<< std::endl; |
|||
if (err.fail()) { |
|||
err.clear(); |
|||
err << "--- Error while outputting ---" << std::endl; |
|||
} |
|||
std::cerr << "Expected output | Received output" << std::endl; |
|||
for (size_t i = 0; i < size; i++) { |
|||
std::cerr << std::setbase(16) << std::setfill('0') << " " |
|||
<< "0x" << std::setw(8) << static_cast<unsigned int>(expected[i]) |
|||
<< " | " |
|||
<< "0x" << std::setw(8) |
|||
<< static_cast<unsigned int>(received[i]); |
|||
if (static_cast<unsigned int>(expected[i]) != |
|||
static_cast<unsigned int>(received[i])) { |
|||
std::cerr << " MISMATCH!"; |
|||
} |
|||
std::cerr << std::endl; |
|||
} |
|||
std::cerr << std::endl; |
|||
} |
|||
|
|||
static bool createProcess(HANDLE hIn, HANDLE hOut, HANDLE hErr) |
|||
{ |
|||
BOOL bInheritHandles = FALSE; |
|||
DWORD dwCreationFlags = 0; |
|||
memset(&processInfo, 0, sizeof(processInfo)); |
|||
memset(&startupInfo, 0, sizeof(startupInfo)); |
|||
startupInfo.cb = sizeof(startupInfo); |
|||
startupInfo.dwFlags = STARTF_USESHOWWINDOW; |
|||
startupInfo.wShowWindow = SW_HIDE; |
|||
if (hIn || hOut || hErr) { |
|||
startupInfo.dwFlags |= STARTF_USESTDHANDLES; |
|||
startupInfo.hStdInput = hIn; |
|||
startupInfo.hStdOutput = hOut; |
|||
startupInfo.hStdError = hErr; |
|||
bInheritHandles = TRUE; |
|||
} |
|||
|
|||
WCHAR cmd[MAX_PATH]; |
|||
if (GetModuleFileNameW(nullptr, cmd, MAX_PATH) == 0) { |
|||
std::cerr << "GetModuleFileName failed!" << std::endl; |
|||
return false; |
|||
} |
|||
WCHAR* p = cmd + wcslen(cmd); |
|||
while (p > cmd && *p != L'\\') |
|||
p--; |
|||
*(p + 1) = 0; |
|||
wcscat(cmd, cmdConsoleBufChild); |
|||
wcscat(cmd, L".exe"); |
|||
|
|||
bool success = |
|||
CreateProcessW(nullptr, // No module name (use command line)
|
|||
cmd, // Command line
|
|||
nullptr, // Process handle not inheritable
|
|||
nullptr, // Thread handle not inheritable
|
|||
bInheritHandles, // Set handle inheritance
|
|||
dwCreationFlags, |
|||
nullptr, // Use parent's environment block
|
|||
nullptr, // Use parent's starting directory
|
|||
&startupInfo, // Pointer to STARTUPINFO structure
|
|||
&processInfo) != |
|||
0; // Pointer to PROCESS_INFORMATION structure
|
|||
if (!success) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "CreateProcess(" << kwsys::Encoding::ToNarrow(cmd) << ")" |
|||
<< std::endl; |
|||
displayError(lastError); |
|||
} |
|||
return success; |
|||
} |
|||
|
|||
static void finishProcess(bool success) |
|||
{ |
|||
if (success) { |
|||
success = |
|||
WaitForSingleObject(processInfo.hProcess, waitTimeout) == WAIT_OBJECT_0; |
|||
}; |
|||
if (!success) { |
|||
TerminateProcess(processInfo.hProcess, 1); |
|||
} |
|||
CloseHandle(processInfo.hProcess); |
|||
CloseHandle(processInfo.hThread); |
|||
} |
|||
|
|||
static bool createPipe(PHANDLE readPipe, PHANDLE writePipe) |
|||
{ |
|||
SECURITY_ATTRIBUTES securityAttributes; |
|||
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
|||
securityAttributes.bInheritHandle = TRUE; |
|||
securityAttributes.lpSecurityDescriptor = nullptr; |
|||
return CreatePipe(readPipe, writePipe, &securityAttributes, 0) == 0 ? false |
|||
: true; |
|||
} |
|||
|
|||
static void finishPipe(HANDLE readPipe, HANDLE writePipe) |
|||
{ |
|||
if (readPipe != INVALID_HANDLE_VALUE) { |
|||
CloseHandle(readPipe); |
|||
} |
|||
if (writePipe != INVALID_HANDLE_VALUE) { |
|||
CloseHandle(writePipe); |
|||
} |
|||
} |
|||
|
|||
static HANDLE createFile(LPCWSTR fileName) |
|||
{ |
|||
SECURITY_ATTRIBUTES securityAttributes; |
|||
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
|||
securityAttributes.bInheritHandle = TRUE; |
|||
securityAttributes.lpSecurityDescriptor = nullptr; |
|||
|
|||
HANDLE file = |
|||
CreateFileW(fileName, GENERIC_READ | GENERIC_WRITE, |
|||
0, // do not share
|
|||
&securityAttributes, |
|||
CREATE_ALWAYS, // overwrite existing
|
|||
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, |
|||
nullptr); // no template
|
|||
if (file == INVALID_HANDLE_VALUE) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "CreateFile(" << kwsys::Encoding::ToNarrow(fileName) << ")" |
|||
<< std::endl; |
|||
displayError(lastError); |
|||
} |
|||
return file; |
|||
} |
|||
|
|||
static void finishFile(HANDLE file) |
|||
{ |
|||
if (file != INVALID_HANDLE_VALUE) { |
|||
CloseHandle(file); |
|||
} |
|||
} |
|||
|
|||
# ifndef MAPVK_VK_TO_VSC
|
|||
# define MAPVK_VK_TO_VSC (0)
|
|||
# endif
|
|||
|
|||
static void writeInputKeyEvent(INPUT_RECORD inputBuffer[], WCHAR chr) |
|||
{ |
|||
inputBuffer[0].EventType = KEY_EVENT; |
|||
inputBuffer[0].Event.KeyEvent.bKeyDown = TRUE; |
|||
inputBuffer[0].Event.KeyEvent.wRepeatCount = 1; |
|||
SHORT keyCode = VkKeyScanW(chr); |
|||
if (keyCode == -1) { |
|||
// Character can't be entered with current keyboard layout
|
|||
// Just set any, it doesn't really matter
|
|||
keyCode = 'K'; |
|||
} |
|||
inputBuffer[0].Event.KeyEvent.wVirtualKeyCode = LOBYTE(keyCode); |
|||
inputBuffer[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey( |
|||
inputBuffer[0].Event.KeyEvent.wVirtualKeyCode, MAPVK_VK_TO_VSC); |
|||
inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar = chr; |
|||
inputBuffer[0].Event.KeyEvent.dwControlKeyState = 0; |
|||
if ((HIBYTE(keyCode) & 1) == 1) { |
|||
inputBuffer[0].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED; |
|||
} |
|||
if ((HIBYTE(keyCode) & 2) == 2) { |
|||
inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_CTRL_PRESSED; |
|||
} |
|||
if ((HIBYTE(keyCode) & 4) == 4) { |
|||
inputBuffer[0].Event.KeyEvent.dwControlKeyState |= RIGHT_ALT_PRESSED; |
|||
} |
|||
inputBuffer[1].EventType = inputBuffer[0].EventType; |
|||
inputBuffer[1].Event.KeyEvent.bKeyDown = FALSE; |
|||
inputBuffer[1].Event.KeyEvent.wRepeatCount = 1; |
|||
inputBuffer[1].Event.KeyEvent.wVirtualKeyCode = |
|||
inputBuffer[0].Event.KeyEvent.wVirtualKeyCode; |
|||
inputBuffer[1].Event.KeyEvent.wVirtualScanCode = |
|||
inputBuffer[0].Event.KeyEvent.wVirtualScanCode; |
|||
inputBuffer[1].Event.KeyEvent.uChar.UnicodeChar = |
|||
inputBuffer[0].Event.KeyEvent.uChar.UnicodeChar; |
|||
inputBuffer[1].Event.KeyEvent.dwControlKeyState = 0; |
|||
} |
|||
|
|||
static int testPipe() |
|||
{ |
|||
int didFail = 1; |
|||
HANDLE inPipeRead = INVALID_HANDLE_VALUE; |
|||
HANDLE inPipeWrite = INVALID_HANDLE_VALUE; |
|||
HANDLE outPipeRead = INVALID_HANDLE_VALUE; |
|||
HANDLE outPipeWrite = INVALID_HANDLE_VALUE; |
|||
HANDLE errPipeRead = INVALID_HANDLE_VALUE; |
|||
HANDLE errPipeWrite = INVALID_HANDLE_VALUE; |
|||
UINT currentCodepage = GetConsoleCP(); |
|||
char buffer[200]; |
|||
char buffer2[200]; |
|||
try { |
|||
if (!createPipe(&inPipeRead, &inPipeWrite) || |
|||
!createPipe(&outPipeRead, &outPipeWrite) || |
|||
!createPipe(&errPipeRead, &errPipeWrite)) { |
|||
throw std::runtime_error("createFile failed!"); |
|||
} |
|||
if (TestCodepage == CP_ACP) { |
|||
TestCodepage = GetACP(); |
|||
} |
|||
if (!SetConsoleCP(TestCodepage)) { |
|||
throw std::runtime_error("SetConsoleCP failed!"); |
|||
} |
|||
|
|||
DWORD bytesWritten = 0; |
|||
if (!WriteFile(inPipeWrite, encodedInputTestString.c_str(), |
|||
(DWORD)encodedInputTestString.size(), &bytesWritten, |
|||
nullptr) || |
|||
bytesWritten == 0) { |
|||
throw std::runtime_error("WriteFile failed!"); |
|||
} |
|||
|
|||
if (createProcess(inPipeRead, outPipeWrite, errPipeWrite)) { |
|||
try { |
|||
DWORD status; |
|||
if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) != |
|||
WAIT_OBJECT_0) { |
|||
std::cerr.setf(std::ios::hex, std::ios::basefield); |
|||
std::cerr << "WaitForSingleObject returned unexpected status 0x" |
|||
<< status << std::endl; |
|||
std::cerr.unsetf(std::ios::hex); |
|||
throw std::runtime_error("WaitForSingleObject failed!"); |
|||
} |
|||
DWORD bytesRead = 0; |
|||
if (!ReadFile(outPipeRead, buffer, sizeof(buffer), &bytesRead, |
|||
nullptr) || |
|||
bytesRead == 0) { |
|||
throw std::runtime_error("ReadFile#1 failed!"); |
|||
} |
|||
buffer[bytesRead] = 0; |
|||
if ((bytesRead < |
|||
encodedTestString.size() + 1 + encodedInputTestString.size() && |
|||
!ReadFile(outPipeRead, buffer + bytesRead, |
|||
sizeof(buffer) - bytesRead, &bytesRead, nullptr)) || |
|||
bytesRead == 0) { |
|||
throw std::runtime_error("ReadFile#2 failed!"); |
|||
} |
|||
if (memcmp(buffer, encodedTestString.c_str(), |
|||
encodedTestString.size()) == 0 && |
|||
memcmp(buffer + encodedTestString.size() + 1, |
|||
encodedInputTestString.c_str(), |
|||
encodedInputTestString.size()) == 0) { |
|||
bytesRead = 0; |
|||
if (!ReadFile(errPipeRead, buffer2, sizeof(buffer2), &bytesRead, |
|||
nullptr) || |
|||
bytesRead == 0) { |
|||
throw std::runtime_error("ReadFile#3 failed!"); |
|||
} |
|||
buffer2[bytesRead] = 0; |
|||
didFail = encodedTestString.compare(0, std::string::npos, buffer2, |
|||
encodedTestString.size()) == 0 |
|||
? 0 |
|||
: 1; |
|||
} |
|||
if (didFail != 0) { |
|||
std::cerr << "Pipe's output didn't match expected output!" |
|||
<< std::endl; |
|||
dumpBuffers<char>(encodedTestString.c_str(), buffer, |
|||
encodedTestString.size()); |
|||
dumpBuffers<char>(encodedInputTestString.c_str(), |
|||
buffer + encodedTestString.size() + 1, |
|||
encodedInputTestString.size()); |
|||
dumpBuffers<char>(encodedTestString.c_str(), buffer2, |
|||
encodedTestString.size()); |
|||
} |
|||
} catch (std::runtime_error const& ex) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "In function testPipe, line " << __LINE__ << ": " |
|||
<< ex.what() << std::endl; |
|||
displayError(lastError); |
|||
} |
|||
finishProcess(didFail == 0); |
|||
} |
|||
} catch (std::runtime_error const& ex) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "In function testPipe, line " << __LINE__ << ": " << ex.what() |
|||
<< std::endl; |
|||
displayError(lastError); |
|||
} |
|||
finishPipe(inPipeRead, inPipeWrite); |
|||
finishPipe(outPipeRead, outPipeWrite); |
|||
finishPipe(errPipeRead, errPipeWrite); |
|||
SetConsoleCP(currentCodepage); |
|||
return didFail; |
|||
} |
|||
|
|||
static int testFile() |
|||
{ |
|||
int didFail = 1; |
|||
HANDLE inFile = INVALID_HANDLE_VALUE; |
|||
HANDLE outFile = INVALID_HANDLE_VALUE; |
|||
HANDLE errFile = INVALID_HANDLE_VALUE; |
|||
try { |
|||
if ((inFile = createFile(L"stdinFile.txt")) == INVALID_HANDLE_VALUE || |
|||
(outFile = createFile(L"stdoutFile.txt")) == INVALID_HANDLE_VALUE || |
|||
(errFile = createFile(L"stderrFile.txt")) == INVALID_HANDLE_VALUE) { |
|||
throw std::runtime_error("createFile failed!"); |
|||
} |
|||
DWORD bytesWritten = 0; |
|||
char buffer[200]; |
|||
char buffer2[200]; |
|||
|
|||
int length; |
|||
if ((length = WideCharToMultiByte(TestCodepage, 0, UnicodeInputTestString, |
|||
-1, buffer, sizeof(buffer), nullptr, |
|||
nullptr)) == 0) { |
|||
throw std::runtime_error("WideCharToMultiByte failed!"); |
|||
} |
|||
buffer[length - 1] = '\n'; |
|||
if (!WriteFile(inFile, buffer, length, &bytesWritten, nullptr) || |
|||
bytesWritten == 0) { |
|||
throw std::runtime_error("WriteFile failed!"); |
|||
} |
|||
if (SetFilePointer(inFile, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { |
|||
throw std::runtime_error("SetFilePointer failed!"); |
|||
} |
|||
|
|||
if (createProcess(inFile, outFile, errFile)) { |
|||
DWORD bytesRead = 0; |
|||
try { |
|||
DWORD status; |
|||
if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) != |
|||
WAIT_OBJECT_0) { |
|||
std::cerr.setf(std::ios::hex, std::ios::basefield); |
|||
std::cerr << "WaitForSingleObject returned unexpected status 0x" |
|||
<< status << std::endl; |
|||
std::cerr.unsetf(std::ios::hex); |
|||
throw std::runtime_error("WaitForSingleObject failed!"); |
|||
} |
|||
if (SetFilePointer(outFile, 0, 0, FILE_BEGIN) == |
|||
INVALID_SET_FILE_POINTER) { |
|||
throw std::runtime_error("SetFilePointer#1 failed!"); |
|||
} |
|||
if (!ReadFile(outFile, buffer, sizeof(buffer), &bytesRead, nullptr) || |
|||
bytesRead == 0) { |
|||
throw std::runtime_error("ReadFile#1 failed!"); |
|||
} |
|||
buffer[bytesRead] = 0; |
|||
if (memcmp(buffer, encodedTestString.c_str(), |
|||
encodedTestString.size()) == 0 && |
|||
memcmp(buffer + encodedTestString.size() + 1, |
|||
encodedInputTestString.c_str(), |
|||
encodedInputTestString.size()) == 0) { |
|||
bytesRead = 0; |
|||
if (SetFilePointer(errFile, 0, 0, FILE_BEGIN) == |
|||
INVALID_SET_FILE_POINTER) { |
|||
throw std::runtime_error("SetFilePointer#2 failed!"); |
|||
} |
|||
|
|||
if (!ReadFile(errFile, buffer2, sizeof(buffer2), &bytesRead, |
|||
nullptr) || |
|||
bytesRead == 0) { |
|||
throw std::runtime_error("ReadFile#2 failed!"); |
|||
} |
|||
buffer2[bytesRead] = 0; |
|||
didFail = encodedTestString.compare(0, std::string::npos, buffer2, |
|||
encodedTestString.size()) == 0 |
|||
? 0 |
|||
: 1; |
|||
} |
|||
if (didFail != 0) { |
|||
std::cerr << "File's output didn't match expected output!" |
|||
<< std::endl; |
|||
dumpBuffers<char>(encodedTestString.c_str(), buffer, |
|||
encodedTestString.size()); |
|||
dumpBuffers<char>(encodedInputTestString.c_str(), |
|||
buffer + encodedTestString.size() + 1, |
|||
encodedInputTestString.size()); |
|||
dumpBuffers<char>(encodedTestString.c_str(), buffer2, |
|||
encodedTestString.size()); |
|||
} |
|||
} catch (std::runtime_error const& ex) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "In function testFile, line " << __LINE__ << ": " |
|||
<< ex.what() << std::endl; |
|||
displayError(lastError); |
|||
} |
|||
finishProcess(didFail == 0); |
|||
} |
|||
} catch (std::runtime_error const& ex) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "In function testFile, line " << __LINE__ << ": " << ex.what() |
|||
<< std::endl; |
|||
displayError(lastError); |
|||
} |
|||
finishFile(inFile); |
|||
finishFile(outFile); |
|||
finishFile(errFile); |
|||
return didFail; |
|||
} |
|||
|
|||
# ifndef _WIN32_WINNT_VISTA
|
|||
# define _WIN32_WINNT_VISTA 0x0600
|
|||
# endif
|
|||
|
|||
static bool consoleIsConhost() |
|||
{ |
|||
wchar_t consoleClassNameBuf[64]; |
|||
int const consoleClassNameLen = GetClassNameW( |
|||
GetConsoleWindow(), &consoleClassNameBuf[0], sizeof(consoleClassNameBuf)); |
|||
// Windows Console Host: ConsoleWindowClass
|
|||
// Windows Terminal / ConPTY: PseudoConsoleWindow (undocumented)
|
|||
return (consoleClassNameLen > 0 && |
|||
wcscmp(consoleClassNameBuf, L"ConsoleWindowClass") == 0); |
|||
} |
|||
|
|||
static bool charIsNUL(wchar_t c) |
|||
{ |
|||
return c == 0; |
|||
} |
|||
|
|||
static int testConsole() |
|||
{ |
|||
int didFail = 1; |
|||
HANDLE parentIn = GetStdHandle(STD_INPUT_HANDLE); |
|||
HANDLE parentOut = GetStdHandle(STD_OUTPUT_HANDLE); |
|||
HANDLE parentErr = GetStdHandle(STD_ERROR_HANDLE); |
|||
HANDLE hIn = parentIn; |
|||
HANDLE hOut = parentOut; |
|||
DWORD consoleMode; |
|||
bool newConsole = false; |
|||
bool forceNewConsole = false; |
|||
bool restoreConsole = false; |
|||
LPCWSTR TestFaceName = L"Lucida Console"; |
|||
const DWORD TestFontFamily = 0x00000036; |
|||
const DWORD TestFontSize = 0x000c0000; |
|||
HKEY hConsoleKey; |
|||
WCHAR FaceName[200]; |
|||
FaceName[0] = 0; |
|||
DWORD FaceNameSize = sizeof(FaceName); |
|||
DWORD FontFamily = TestFontFamily; |
|||
DWORD FontSize = TestFontSize; |
|||
# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
|
|||
# pragma warning(push)
|
|||
# ifdef __INTEL_COMPILER
|
|||
# pragma warning(disable : 1478)
|
|||
# elif defined __clang__
|
|||
# pragma clang diagnostic push
|
|||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|||
# else
|
|||
# pragma warning(disable : 4996)
|
|||
# endif
|
|||
# endif
|
|||
bool const isVistaOrGreater = |
|||
LOBYTE(LOWORD(GetVersion())) >= HIBYTE(_WIN32_WINNT_VISTA); |
|||
# ifdef KWSYS_WINDOWS_DEPRECATED_GetVersion
|
|||
# ifdef __clang__
|
|||
# pragma clang diagnostic pop
|
|||
# else
|
|||
# pragma warning(pop)
|
|||
# endif
|
|||
# endif
|
|||
if (!isVistaOrGreater) { |
|||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_READ | KEY_WRITE, |
|||
&hConsoleKey) == ERROR_SUCCESS) { |
|||
DWORD dwordSize = sizeof(DWORD); |
|||
if (RegQueryValueExW(hConsoleKey, L"FontFamily", nullptr, nullptr, |
|||
(LPBYTE)&FontFamily, &dwordSize) == ERROR_SUCCESS) { |
|||
if (FontFamily != TestFontFamily) { |
|||
RegQueryValueExW(hConsoleKey, L"FaceName", nullptr, nullptr, |
|||
(LPBYTE)FaceName, &FaceNameSize); |
|||
RegQueryValueExW(hConsoleKey, L"FontSize", nullptr, nullptr, |
|||
(LPBYTE)&FontSize, &dwordSize); |
|||
|
|||
RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD, |
|||
(BYTE*)&TestFontFamily, sizeof(TestFontFamily)); |
|||
RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, |
|||
(BYTE*)TestFaceName, |
|||
(DWORD)((wcslen(TestFaceName) + 1) * sizeof(WCHAR))); |
|||
RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, |
|||
(BYTE*)&TestFontSize, sizeof(TestFontSize)); |
|||
|
|||
restoreConsole = true; |
|||
forceNewConsole = true; |
|||
} |
|||
} else { |
|||
std::cerr << "RegGetValueW(FontFamily) failed!" << std::endl; |
|||
} |
|||
RegCloseKey(hConsoleKey); |
|||
} else { |
|||
std::cerr << "RegOpenKeyExW(HKEY_CURRENT_USER\\Console) failed!" |
|||
<< std::endl; |
|||
} |
|||
} |
|||
if (forceNewConsole || GetConsoleMode(parentOut, &consoleMode) == 0) { |
|||
// Not a real console, let's create new one.
|
|||
FreeConsole(); |
|||
if (!AllocConsole()) { |
|||
std::cerr << "AllocConsole failed!" << std::endl; |
|||
return didFail; |
|||
} |
|||
SECURITY_ATTRIBUTES securityAttributes; |
|||
securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); |
|||
securityAttributes.bInheritHandle = TRUE; |
|||
securityAttributes.lpSecurityDescriptor = nullptr; |
|||
hIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, |
|||
FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes, |
|||
OPEN_EXISTING, 0, nullptr); |
|||
if (hIn == INVALID_HANDLE_VALUE) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "CreateFile(CONIN$)" << std::endl; |
|||
displayError(lastError); |
|||
} |
|||
hOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, |
|||
FILE_SHARE_READ | FILE_SHARE_WRITE, &securityAttributes, |
|||
OPEN_EXISTING, 0, nullptr); |
|||
if (hOut == INVALID_HANDLE_VALUE) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "CreateFile(CONOUT$)" << std::endl; |
|||
displayError(lastError); |
|||
} |
|||
SetStdHandle(STD_INPUT_HANDLE, hIn); |
|||
SetStdHandle(STD_OUTPUT_HANDLE, hOut); |
|||
SetStdHandle(STD_ERROR_HANDLE, hOut); |
|||
newConsole = true; |
|||
} |
|||
|
|||
# if _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
|||
if (isVistaOrGreater) { |
|||
CONSOLE_FONT_INFOEX consoleFont; |
|||
memset(&consoleFont, 0, sizeof(consoleFont)); |
|||
consoleFont.cbSize = sizeof(consoleFont); |
|||
HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); |
|||
typedef BOOL(WINAPI * GetCurrentConsoleFontExFunc)( |
|||
HANDLE hConsoleOutput, BOOL bMaximumWindow, |
|||
PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx); |
|||
typedef BOOL(WINAPI * SetCurrentConsoleFontExFunc)( |
|||
HANDLE hConsoleOutput, BOOL bMaximumWindow, |
|||
PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx); |
|||
GetCurrentConsoleFontExFunc getConsoleFont = |
|||
(GetCurrentConsoleFontExFunc)GetProcAddress(kernel32, |
|||
"GetCurrentConsoleFontEx"); |
|||
SetCurrentConsoleFontExFunc setConsoleFont = |
|||
(SetCurrentConsoleFontExFunc)GetProcAddress(kernel32, |
|||
"SetCurrentConsoleFontEx"); |
|||
if (getConsoleFont(hOut, FALSE, &consoleFont)) { |
|||
if (consoleFont.FontFamily != TestFontFamily) { |
|||
consoleFont.FontFamily = TestFontFamily; |
|||
wcscpy(consoleFont.FaceName, TestFaceName); |
|||
if (!setConsoleFont(hOut, FALSE, &consoleFont)) { |
|||
std::cerr << "SetCurrentConsoleFontEx failed!" << std::endl; |
|||
} |
|||
} |
|||
} else { |
|||
std::cerr << "GetCurrentConsoleFontEx failed!" << std::endl; |
|||
} |
|||
} else { |
|||
# endif
|
|||
if (restoreConsole && |
|||
RegOpenKeyExW(HKEY_CURRENT_USER, L"Console", 0, KEY_WRITE, |
|||
&hConsoleKey) == ERROR_SUCCESS) { |
|||
RegSetValueExW(hConsoleKey, L"FontFamily", 0, REG_DWORD, |
|||
(BYTE*)&FontFamily, sizeof(FontFamily)); |
|||
if (FaceName[0] != 0) { |
|||
RegSetValueExW(hConsoleKey, L"FaceName", 0, REG_SZ, (BYTE*)FaceName, |
|||
FaceNameSize); |
|||
} else { |
|||
RegDeleteValueW(hConsoleKey, L"FaceName"); |
|||
} |
|||
RegSetValueExW(hConsoleKey, L"FontSize", 0, REG_DWORD, (BYTE*)&FontSize, |
|||
sizeof(FontSize)); |
|||
RegCloseKey(hConsoleKey); |
|||
} |
|||
# if _WIN32_WINNT >= _WIN32_WINNT_VISTA
|
|||
} |
|||
# endif
|
|||
|
|||
if (createProcess(nullptr, nullptr, nullptr)) { |
|||
try { |
|||
DWORD status; |
|||
if ((status = WaitForSingleObject(beforeInputEvent, waitTimeout)) != |
|||
WAIT_OBJECT_0) { |
|||
std::cerr.setf(std::ios::hex, std::ios::basefield); |
|||
std::cerr << "WaitForSingleObject returned unexpected status 0x" |
|||
<< status << std::endl; |
|||
std::cerr.unsetf(std::ios::hex); |
|||
throw std::runtime_error("WaitForSingleObject#1 failed!"); |
|||
} |
|||
INPUT_RECORD inputBuffer[(sizeof(UnicodeInputTestString) / |
|||
sizeof(UnicodeInputTestString[0])) * |
|||
2]; |
|||
memset(&inputBuffer, 0, sizeof(inputBuffer)); |
|||
unsigned int i; |
|||
for (i = 0; i < (sizeof(UnicodeInputTestString) / |
|||
sizeof(UnicodeInputTestString[0]) - |
|||
1); |
|||
i++) { |
|||
writeInputKeyEvent(&inputBuffer[i * 2], UnicodeInputTestString[i]); |
|||
} |
|||
writeInputKeyEvent(&inputBuffer[i * 2], VK_RETURN); |
|||
DWORD eventsWritten = 0; |
|||
// We need to wait a bit before writing to console so child process have
|
|||
// started waiting for input on stdin.
|
|||
Sleep(300); |
|||
if (!WriteConsoleInputW(hIn, inputBuffer, |
|||
sizeof(inputBuffer) / sizeof(inputBuffer[0]), |
|||
&eventsWritten) || |
|||
eventsWritten == 0) { |
|||
throw std::runtime_error("WriteConsoleInput failed!"); |
|||
} |
|||
if ((status = WaitForSingleObject(afterOutputEvent, waitTimeout)) != |
|||
WAIT_OBJECT_0) { |
|||
std::cerr.setf(std::ios::hex, std::ios::basefield); |
|||
std::cerr << "WaitForSingleObject returned unexpected status 0x" |
|||
<< status << std::endl; |
|||
std::cerr.unsetf(std::ios::hex); |
|||
throw std::runtime_error("WaitForSingleObject#2 failed!"); |
|||
} |
|||
CONSOLE_SCREEN_BUFFER_INFO screenBufferInfo; |
|||
if (!GetConsoleScreenBufferInfo(hOut, &screenBufferInfo)) { |
|||
throw std::runtime_error("GetConsoleScreenBufferInfo failed!"); |
|||
} |
|||
|
|||
COORD coord; |
|||
DWORD charsRead = 0; |
|||
coord.X = 0; |
|||
coord.Y = screenBufferInfo.dwCursorPosition.Y - 4; |
|||
WCHAR* outputBuffer = new WCHAR[screenBufferInfo.dwSize.X * 4]; |
|||
if (!ReadConsoleOutputCharacterW(hOut, outputBuffer, |
|||
screenBufferInfo.dwSize.X * 4, coord, |
|||
&charsRead) || |
|||
charsRead == 0) { |
|||
delete[] outputBuffer; |
|||
throw std::runtime_error("ReadConsoleOutputCharacter failed!"); |
|||
} |
|||
std::wstring wideTestString = kwsys::Encoding::ToWide(encodedTestString); |
|||
if (consoleIsConhost()) { |
|||
// Windows Console Host converts NUL bytes to spaces.
|
|||
std::replace(wideTestString.begin(), wideTestString.end(), '\0', ' '); |
|||
} else { |
|||
// Windows Terminal / ConPTY removes NUL bytes.
|
|||
wideTestString.erase(std::remove_if(wideTestString.begin(), |
|||
wideTestString.end(), charIsNUL), |
|||
wideTestString.end()); |
|||
} |
|||
std::wstring wideInputTestString = |
|||
kwsys::Encoding::ToWide(encodedInputTestString); |
|||
if (memcmp(outputBuffer, wideTestString.c_str(), |
|||
wideTestString.size() * sizeof(wchar_t)) == 0 && |
|||
memcmp(outputBuffer + screenBufferInfo.dwSize.X * 1, |
|||
wideTestString.c_str(), |
|||
wideTestString.size() * sizeof(wchar_t)) == 0 && |
|||
memcmp(outputBuffer + screenBufferInfo.dwSize.X * 2, |
|||
UnicodeInputTestString, |
|||
sizeof(UnicodeInputTestString) - sizeof(WCHAR)) == 0 && |
|||
memcmp(outputBuffer + screenBufferInfo.dwSize.X * 3, |
|||
wideInputTestString.c_str(), |
|||
(wideInputTestString.size() - 1) * sizeof(wchar_t)) == 0) { |
|||
didFail = 0; |
|||
} else { |
|||
std::cerr << "Console's output didn't match expected output!" |
|||
<< std::endl; |
|||
dumpBuffers<wchar_t>(wideTestString.c_str(), outputBuffer, |
|||
wideTestString.size()); |
|||
dumpBuffers<wchar_t>(wideTestString.c_str(), |
|||
outputBuffer + screenBufferInfo.dwSize.X * 1, |
|||
wideTestString.size()); |
|||
dumpBuffers<wchar_t>( |
|||
UnicodeInputTestString, outputBuffer + screenBufferInfo.dwSize.X * 2, |
|||
(sizeof(UnicodeInputTestString) - 1) / sizeof(WCHAR)); |
|||
dumpBuffers<wchar_t>(wideInputTestString.c_str(), |
|||
outputBuffer + screenBufferInfo.dwSize.X * 3, |
|||
wideInputTestString.size() - 1); |
|||
} |
|||
delete[] outputBuffer; |
|||
} catch (std::runtime_error const& ex) { |
|||
DWORD lastError = GetLastError(); |
|||
std::cerr << "In function testConsole, line " << __LINE__ << ": " |
|||
<< ex.what() << std::endl; |
|||
displayError(lastError); |
|||
} |
|||
finishProcess(didFail == 0); |
|||
} |
|||
if (newConsole) { |
|||
SetStdHandle(STD_INPUT_HANDLE, parentIn); |
|||
SetStdHandle(STD_OUTPUT_HANDLE, parentOut); |
|||
SetStdHandle(STD_ERROR_HANDLE, parentErr); |
|||
CloseHandle(hIn); |
|||
CloseHandle(hOut); |
|||
FreeConsole(); |
|||
} |
|||
return didFail; |
|||
} |
|||
|
|||
#endif
|
|||
|
|||
int testConsoleBuf(int, char*[]) |
|||
{ |
|||
int ret = 0; |
|||
|
|||
#if defined(_WIN32)
|
|||
beforeInputEvent = CreateEventW(nullptr, |
|||
FALSE, // auto-reset event
|
|||
FALSE, // initial state is nonsignaled
|
|||
BeforeInputEventName); // object name
|
|||
if (!beforeInputEvent) { |
|||
std::cerr << "CreateEvent#1 failed " << GetLastError() << std::endl; |
|||
return 1; |
|||
} |
|||
|
|||
afterOutputEvent = CreateEventW(nullptr, FALSE, FALSE, AfterOutputEventName); |
|||
if (!afterOutputEvent) { |
|||
std::cerr << "CreateEvent#2 failed " << GetLastError() << std::endl; |
|||
return 1; |
|||
} |
|||
|
|||
encodedTestString = kwsys::Encoding::ToNarrow(std::wstring( |
|||
UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1)); |
|||
encodedInputTestString = kwsys::Encoding::ToNarrow( |
|||
std::wstring(UnicodeInputTestString, |
|||
sizeof(UnicodeInputTestString) / sizeof(wchar_t) - 1)); |
|||
encodedInputTestString += "\n"; |
|||
|
|||
ret |= testPipe(); |
|||
ret |= testFile(); |
|||
ret |= testConsole(); |
|||
|
|||
CloseHandle(beforeInputEvent); |
|||
CloseHandle(afterOutputEvent); |
|||
#endif
|
|||
|
|||
return ret; |
|||
} |
@ -1,17 +0,0 @@ |
|||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|||
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
|
|||
#ifndef testConsoleBuf_hxx
|
|||
#define testConsoleBuf_hxx
|
|||
|
|||
static wchar_t const cmdConsoleBufChild[] = L"testConsoleBufChild"; |
|||
|
|||
static wchar_t const BeforeInputEventName[] = L"BeforeInputEvent"; |
|||
static wchar_t const AfterOutputEventName[] = L"AfterOutputEvent"; |
|||
|
|||
// यूनिकोड είναι здорово!
|
|||
static wchar_t const UnicodeTestString[] = |
|||
L"\u092F\u0942\u0928\u093F\u0915\u094B\u0921 " |
|||
L"\u03B5\u03AF\u03BD\0\u03B1\u03B9 " |
|||
L"\u0437\u0434\u043E\u0440\u043E\u0432\u043E!"; |
|||
|
|||
#endif
|
@ -1,55 +0,0 @@ |
|||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|||
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
|
|||
#include "kwsysPrivate.h"
|
|||
|
|||
#include KWSYS_HEADER(ConsoleBuf.hxx)
|
|||
#include KWSYS_HEADER(Encoding.hxx)
|
|||
|
|||
// Work-around CMake dependency scanning limitation. This must
|
|||
// duplicate the above list of headers.
|
|||
#if 0
|
|||
# include "ConsoleBuf.hxx.in"
|
|||
# include "Encoding.hxx.in"
|
|||
#endif
|
|||
|
|||
#include <iostream>
|
|||
|
|||
#include "testConsoleBuf.hxx"
|
|||
|
|||
int main(int argc, char const* argv[]) |
|||
{ |
|||
#if defined(_WIN32)
|
|||
kwsys::ConsoleBuf::Manager out(std::cout); |
|||
kwsys::ConsoleBuf::Manager err(std::cerr, true); |
|||
kwsys::ConsoleBuf::Manager in(std::cin); |
|||
|
|||
if (argc > 1) { |
|||
std::cout << argv[1] << std::endl; |
|||
std::cerr << argv[1] << std::endl; |
|||
} else { |
|||
std::string str = kwsys::Encoding::ToNarrow(std::wstring( |
|||
UnicodeTestString, sizeof(UnicodeTestString) / sizeof(wchar_t) - 1)); |
|||
std::cout << str << std::endl; |
|||
std::cerr << str << std::endl; |
|||
} |
|||
|
|||
std::string input; |
|||
HANDLE event = OpenEventW(EVENT_MODIFY_STATE, FALSE, BeforeInputEventName); |
|||
if (event) { |
|||
SetEvent(event); |
|||
CloseHandle(event); |
|||
} |
|||
|
|||
std::cin >> input; |
|||
std::cout << input << std::endl; |
|||
event = OpenEventW(EVENT_MODIFY_STATE, FALSE, AfterOutputEventName); |
|||
if (event) { |
|||
SetEvent(event); |
|||
CloseHandle(event); |
|||
} |
|||
#else
|
|||
static_cast<void>(argc); |
|||
static_cast<void>(argv); |
|||
#endif
|
|||
return 0; |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue