mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08:00
KWSys 2025-05-14 (1d72955e)
Code extracted from: https://gitlab.kitware.com/utils/kwsys.git at commit 1d72955e085992935140f6504990f3edf3894f9e (master). Upstream Shortlog ----------------- Brad King (1): 713d53a4 ConsoleBuf: Remove this component of KWSys
This commit is contained in:

committed by
Brad King

parent
9df0cea115
commit
ad2d94cb9d
@@ -152,7 +152,6 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
|
||||
set(KWSYS_USE_FStream 1)
|
||||
set(KWSYS_USE_String 1)
|
||||
set(KWSYS_USE_SystemInformation 1)
|
||||
set(KWSYS_USE_ConsoleBuf 1)
|
||||
endif()
|
||||
|
||||
# Enforce component dependencies.
|
||||
@@ -193,9 +192,6 @@ endif()
|
||||
if(KWSYS_USE_FStream)
|
||||
set(KWSYS_USE_Encoding 1)
|
||||
endif()
|
||||
if(KWSYS_USE_ConsoleBuf)
|
||||
set(KWSYS_USE_Encoding 1)
|
||||
endif()
|
||||
|
||||
# Specify default 8 bit encoding for Windows
|
||||
if(NOT KWSYS_ENCODING_DEFAULT_CODEPAGE)
|
||||
@@ -619,7 +615,7 @@ set(KWSYS_HXX_FILES Configure)
|
||||
# Add selected C++ classes.
|
||||
set(cppclasses
|
||||
Directory DynamicLoader Encoding Glob RegularExpression SystemTools
|
||||
CommandLineArguments FStream SystemInformation ConsoleBuf Status
|
||||
CommandLineArguments FStream SystemInformation Status
|
||||
)
|
||||
foreach(cpp IN LISTS cppclasses)
|
||||
if(KWSYS_USE_${cpp})
|
||||
@@ -970,24 +966,6 @@ if(KWSYS_STANDALONE OR CMake_SOURCE_DIR)
|
||||
testFStream.cxx
|
||||
)
|
||||
endif()
|
||||
if(KWSYS_USE_ConsoleBuf)
|
||||
add_executable(testConsoleBufChild testConsoleBufChild.cxx)
|
||||
set_property(TARGET testConsoleBufChild PROPERTY C_CLANG_TIDY "")
|
||||
set_property(TARGET testConsoleBufChild PROPERTY CXX_CLANG_TIDY "")
|
||||
set_property(TARGET testConsoleBufChild PROPERTY C_INCLUDE_WHAT_YOU_USE "")
|
||||
set_property(TARGET testConsoleBufChild PROPERTY CXX_INCLUDE_WHAT_YOU_USE "")
|
||||
set_property(TARGET testConsoleBufChild PROPERTY LABELS ${KWSYS_LABELS_EXE})
|
||||
target_link_libraries(testConsoleBufChild ${KWSYS_TARGET_LINK})
|
||||
set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS}
|
||||
testConsoleBuf.cxx
|
||||
)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "19.0.23506")
|
||||
set_property(SOURCE testConsoleBuf.cxx testConsoleBufChild.cxx PROPERTY COMPILE_FLAGS /utf-8)
|
||||
endif()
|
||||
set_property(SOURCE testConsoleBuf.cxx APPEND PROPERTY COMPILE_DEFINITIONS
|
||||
KWSYS_ENCODING_DEFAULT_CODEPAGE=${KWSYS_ENCODING_DEFAULT_CODEPAGE})
|
||||
endif()
|
||||
if(KWSYS_USE_SystemInformation)
|
||||
set(KWSYS_CXX_TESTS ${KWSYS_CXX_TESTS} testSystemInformation.cxx)
|
||||
endif()
|
||||
|
@@ -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;
|
||||
}
|
Reference in New Issue
Block a user