1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

Merge topic 'stdio-console'

ce6b0f9e10 ci: Drop unused build artifact
7cf81cdb04 Merge branch 'upstream-KWSys' into stdio-console
ad2d94cb9d KWSys 2025-05-14 (1d72955e)
40b0db5ffb cmConsoleBuf: Remove unused infrastructure
57b24e8fef StdIo: Init() automatically in Console()
3e88020aed StdIo: Replace uses of KWSys ConsoleBuf with StdIo::Console
f9f1f9a8cd StdIo: Add a Windows Console adaptor for cin, cout, and cerr

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !10769
This commit is contained in:
Brad King
2025-05-14 14:10:56 +00:00
committed by Kitware Robot
19 changed files with 492 additions and 1386 deletions

View File

@@ -39,7 +39,6 @@
- ${CMAKE_CI_BUILD_DIR}/Tests/CMake*Lib/*LibTests
- ${CMAKE_CI_BUILD_DIR}/Tests/CMake*Lib/*LibTests.exe
- ${CMAKE_CI_BUILD_DIR}/Source/kwsys/cmsysTest*
- ${CMAKE_CI_BUILD_DIR}/Source/kwsys/testConsoleBufChild.exe
- ${CMAKE_CI_BUILD_DIR}/Utilities/cmcurl/curltest
- ${CMAKE_CI_BUILD_DIR}/Utilities/cmcurl/curltest.exe
- ${CMAKE_CI_BUILD_DIR}/Utilities/KWIML/test/kwiml_test

View File

@@ -148,8 +148,6 @@ add_library(
cmComputeTargetDepends.cxx
cmConfigureLog.h
cmConfigureLog.cxx
cmConsoleBuf.h
cmConsoleBuf.cxx
cmConstStack.h
cmConstStack.tcc
cmCPackPropertiesGenerator.h
@@ -468,6 +466,8 @@ add_library(
cmStateSnapshot.cxx
cmStateSnapshot.h
cmStateTypes.h
cmStdIoConsole.h
cmStdIoConsole.cxx
cmStdIoInit.h
cmStdIoInit.cxx
cmStdIoStream.h

View File

@@ -24,7 +24,6 @@
#include "cmCPackGeneratorFactory.h"
#include "cmCPackLog.h"
#include "cmCommandLineArgument.h"
#include "cmConsoleBuf.h"
#include "cmDocumentation.h"
#include "cmDocumentationEntry.h"
#include "cmGlobalGenerator.h"
@@ -33,7 +32,7 @@
#include "cmMakefile.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
#include "cmStdIoInit.h"
#include "cmStdIoConsole.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
@@ -90,11 +89,7 @@ std::vector<cmDocumentationEntry> makeGeneratorDocs(
// this is CPack.
int main(int argc, char const* const* argv)
{
cm::StdIo::Init();
// Replace streambuf so we can output Unicode to console
cmConsoleBuf consoleBuf;
consoleBuf.SetUTF8Pipes();
cm::StdIo::Console console;
cmsys::Encoding::CommandLineArguments args =
cmsys::Encoding::CommandLineArguments::Main(argc, argv);

View File

@@ -21,7 +21,6 @@ set(KWSYS_USE_Base64 1)
set(KWSYS_USE_MD5 1)
set(KWSYS_USE_Process 1)
set(KWSYS_USE_CommandLineArguments 1)
set(KWSYS_USE_ConsoleBuf 1)
set(KWSYS_HEADER_ROOT ${CMake_BINARY_DIR}/Source)
set(KWSYS_INSTALL_DOC_DIR "${CMAKE_DOC_DIR}")
if(CMake_NO_CXX_STANDARD)

View File

@@ -1,21 +0,0 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmConsoleBuf.h"
#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
cmConsoleBuf::cmConsoleBuf()
: m_ConsoleOut(std::cout)
, m_ConsoleErr(std::cerr, true)
{
}
#else
cmConsoleBuf::cmConsoleBuf() = default;
#endif
void cmConsoleBuf::SetUTF8Pipes()
{
#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
m_ConsoleOut.SetUTF8Pipes();
m_ConsoleErr.SetUTF8Pipes();
#endif
}

View File

@@ -1,23 +0,0 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
# include "cmsys/ConsoleBuf.hxx"
#endif
class cmConsoleBuf
{
#if defined(_WIN32) && !defined(CMAKE_BOOTSTRAP)
cmsys::ConsoleBuf::Manager m_ConsoleOut;
cmsys::ConsoleBuf::Manager m_ConsoleErr;
#endif
public:
cmConsoleBuf();
~cmConsoleBuf() = default;
cmConsoleBuf(cmConsoleBuf const&) = delete;
cmConsoleBuf& operator=(cmConsoleBuf const&) = delete;
void SetUTF8Pipes();
};

361
Source/cmStdIoConsole.cxx Normal file
View File

@@ -0,0 +1,361 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmStdIoConsole.h"
#ifdef _WIN32
# include <cstddef>
# include <cstdlib>
# include <ios>
# include <streambuf>
# include <utility>
# include <vector>
# include <cm/memory>
# include <windows.h>
# include <fcntl.h> // for _O_BINARY
# include <io.h> // for _setmode
# include "cm_utf8.h"
# include "cmStdIoStream.h"
#endif
namespace cm {
namespace StdIo {
namespace {
#ifdef _WIN32
// Base class for a streambuf that reads or writes a Windows Console.
class ConsoleBuf : public std::streambuf
{
public:
ConsoleBuf(HANDLE console)
: console_(console)
{
}
~ConsoleBuf() throw() override {}
protected:
HANDLE console_ = nullptr;
};
// A streambuf that reads from a Windows Console using wide-character
// encoding to avoid conversion through the console output code page.
class ConsoleBufRead : public ConsoleBuf
{
public:
ConsoleBufRead(HANDLE console, DWORD consoleMode)
: ConsoleBuf(console)
, ConsoleMode_(consoleMode)
{
}
~ConsoleBufRead() throw() override {}
protected:
// Called to read an input character when the input buffer may be empty.
int_type underflow() override
{
// If the input buffer is not empty, return the next input character.
if (this->gptr() < this->egptr()) {
return traits_type::to_int_type(*this->gptr());
}
// The input buffer is empty. Read more input from the console.
static constexpr std::size_t kBufSize = 4096;
this->TmpW_.resize(kBufSize);
DWORD wlen = 0;
if (!ReadConsoleW(this->console_, this->TmpW_.data(),
DWORD(this->TmpW_.size()), &wlen, nullptr)) {
// Failure. Nothing was read.
return traits_type::eof();
}
// Emulate ReadFile behavior when the console is in "cooked mode".
// Treat a leading Ctrl+Z as EOF.
static constexpr char ctrl_z = 26; // Ctrl+Z is Ctrl + 26th letter.
if ((this->ConsoleMode_ & ENABLE_LINE_INPUT) &&
(wlen > 0 && this->TmpW_.front() == ctrl_z)) {
wlen = 0;
}
// Convert the wide-character encoding from the console to our
// internal UTF-8 narrow encoding.
if (int nlen =
WideCharToMultiByte(CP_UTF8, 0, this->TmpW_.data(), int(wlen),
nullptr, 0, nullptr, nullptr)) {
this->Buf_.resize(nlen);
if (WideCharToMultiByte(CP_UTF8, 0, this->TmpW_.data(), int(wlen),
this->Buf_.data(), int(nlen), nullptr,
nullptr)) {
// The converted content is now in the input buffer.
this->setg_();
// Success. Return the next input character.
return traits_type::to_int_type(*this->gptr());
}
}
// Failure. Nothing was read.
return traits_type::eof();
}
private:
DWORD ConsoleMode_ = 0;
std::vector<char> Buf_;
std::vector<wchar_t> TmpW_;
// Set input buffer pointers.
void setg_()
{
this->setg(this->Buf_.data(), this->Buf_.data(),
this->Buf_.data() + this->Buf_.size());
}
};
// A streambuf that writes to a Windows Console using wide-character
// encoding to avoid conversion through the console output code page.
class ConsoleBufWrite : public ConsoleBuf
{
public:
ConsoleBufWrite(HANDLE console)
: ConsoleBuf(console)
{
this->setp_();
}
~ConsoleBufWrite() throw() override { sync(); }
protected:
// Called to sync input and output buffers with the underlying device.
int sync() override
{
// Flush buffered output, if any.
if (this->pptr() != this->pbase()) {
// Use overflow() to flush the entire output buffer.
// It returns eof on failure.
if (traits_type::eq_int_type(this->overflow(), traits_type::eof())) {
return -1;
}
}
return 0;
}
// Called to flush at least some content from the output buffer.
int_type overflow(int_type ch = traits_type::eof()) override
{
std::size_t nlen; // Number of chars to emit.
std::size_t rlen = 0; // Number of chars to roll over.
if (traits_type::eq_int_type(ch, traits_type::eof())) {
// Our caller wants to flush the entire buffer. If there is a
// trailing partial codepoint, it's the caller's fault.
nlen = this->pptr() - this->pbase();
// If the buffer is empty, trivially succeed.
if (nlen == 0) {
return traits_type::not_eof(ch);
}
} else {
// Our caller had no room for this character in the buffer.
// However, setp_() reserved one byte for us to store it.
*this->pptr() = traits_type::to_char_type(ch);
this->pbump(1);
// Flush all complete codepoints, of which we expect at least one.
// If there is a trailing partial codepoint, roll over those chars.
char const* p = this->pptr_();
nlen = p - this->pbase();
rlen = this->pptr() - p;
}
// Fail unless we emit at least one (wide) character.
int_type result = traits_type::eof();
// Convert our internal UTF-8 narrow encoding to wide-character
// encoding to write to the console.
if (int wlen = MultiByteToWideChar(CP_UTF8, 0, this->pbase(), int(nlen),
nullptr, 0)) {
this->TmpW_.resize(wlen);
if (MultiByteToWideChar(CP_UTF8, 0, this->pbase(), int(nlen),
this->TmpW_.data(), int(wlen)) &&
WriteConsoleW(this->console_, this->TmpW_.data(), wlen, nullptr,
nullptr)) {
result = traits_type::not_eof(ch);
}
}
// Remove emitted contents from the buffer.
this->Buf_.erase(this->Buf_.begin(), this->Buf_.begin() + nlen);
// Re-initialize the output buffer.
this->setp_();
// Move the put-pointer past the rollover content.
this->pbump(rlen);
return result;
}
private:
std::vector<char> Buf_;
std::vector<wchar_t> TmpW_;
// Initialize the output buffer and set its put-pointer.
void setp_()
{
// Allocate the output buffer.
static constexpr std::size_t kBufSize = 4096;
this->Buf_.resize(kBufSize);
// Reserve one byte for the overflow() character.
this->setp(this->Buf_.data(), this->Buf_.data() + this->Buf_.size() - 1);
}
// Return pptr() adjusted backward past a partial codepoint.
char const* pptr_() const
{
char const* p = this->pptr();
while (p != this->pbase()) {
--p;
switch (cm_utf8_ones[static_cast<unsigned char>(*p)]) {
case 0: // 0xxx xxxx: starts codepoint of size 1
return p + 1;
case 1: // 10xx xxxx: continues a codepoint
continue;
case 2: // 110x xxxx: starts codepoint of size 2
return ((p + 2) <= this->pptr()) ? (p + 2) : p;
case 3: // 1110 xxxx: starts codepoint of size 3
return ((p + 3) <= this->pptr()) ? (p + 3) : p;
case 4: // 1111 0xxx: starts codepoint of size 4
return ((p + 4) <= this->pptr()) ? (p + 4) : p;
default: // invalid byte
// Roll over the invalid byte.
// The next overflow() will fail to convert it.
return p;
}
}
// No complete codepoint found. This overflow() will fail.
return p;
}
};
#endif
} // anonymous namespace
#ifdef _WIN32
class Console::Impl
{
protected:
class RAII
{
std::ios* IOS_ = nullptr;
int FD_ = -1;
std::unique_ptr<ConsoleBuf> ConsoleBuf_;
std::streambuf* OldStreamBuf_ = nullptr;
int OldMode_ = 0;
RAII(Stream& s);
void Init();
public:
RAII(IStream& is);
RAII(OStream& os);
~RAII();
};
RAII In_;
RAII Out_;
RAII Err_;
public:
Impl();
~Impl();
};
Console::Impl::RAII::RAII(Stream& s)
: IOS_(&s.IOS())
, FD_(s.FD())
{
}
Console::Impl::RAII::RAII(IStream& is)
: RAII(static_cast<Stream&>(is))
{
DWORD mode;
if (is.Console() && GetConsoleMode(is.Console(), &mode) &&
GetConsoleCP() != CP_UTF8) {
// The input stream reads from a console whose input code page is not
// UTF-8. Use a ConsoleBufRead to read wide-character encoding.
this->ConsoleBuf_ = cm::make_unique<ConsoleBufRead>(is.Console(), mode);
}
this->Init();
}
Console::Impl::RAII::RAII(OStream& os)
: RAII(static_cast<Stream&>(os))
{
DWORD mode;
if (os.Console() && GetConsoleMode(os.Console(), &mode) &&
GetConsoleOutputCP() != CP_UTF8) {
// The output stream writes to a console whose output code page is not
// UTF-8. Use a ConsoleBufWrite to write wide-character encoding.
this->ConsoleBuf_ = cm::make_unique<ConsoleBufWrite>(os.Console());
}
this->Init();
}
void Console::Impl::RAII::Init()
{
if (this->ConsoleBuf_) {
this->OldStreamBuf_ = this->IOS_->rdbuf(this->ConsoleBuf_.get());
} else if (this->FD_ >= 0) {
// The stream reads/writes a pipe, a file, or a console whose code
// page is UTF-8. Read/write UTF-8 using the default streambuf,
// but disable newline conversion to match ConsoleBuf behavior.
this->OldMode_ = _setmode(this->FD_, _O_BINARY);
}
}
Console::Impl::RAII::~RAII()
{
if (this->ConsoleBuf_) {
this->IOS_->rdbuf(this->OldStreamBuf_);
this->OldStreamBuf_ = nullptr;
this->ConsoleBuf_.reset();
} else if (this->FD_ >= 0) {
this->IOS_->rdbuf()->pubsync();
_setmode(this->FD_, this->OldMode_);
this->OldMode_ = 0;
}
this->FD_ = -1;
this->IOS_ = nullptr;
}
Console::Impl::Impl()
: In_(In())
, Out_(Out())
, Err_(Err())
{
}
Console::Impl::~Impl() = default;
Console::Console()
: Impl_(cm::make_unique<Impl>())
{
}
#else
Console::Console() = default;
#endif
Console::~Console() = default;
Console::Console(Console&&) noexcept = default;
Console& Console::operator=(Console&&) noexcept = default;
}
}

49
Source/cmStdIoConsole.h Normal file
View File

@@ -0,0 +1,49 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmStdIoInit.h"
#ifdef _WIN32
# include <memory>
#endif
namespace cm {
namespace StdIo {
/**
* On Windows, enables I/O with `cin`, `cout`, and `cerr` in UTF-8 encoding.
* On non-Windows platforms, does nothing.
*
* Construct an instance of this at the beginning of `main`:
*
* * If `cin`, `cout`, or `cerr` is attached to a Windows Console whose
* input/output code page is not UTF-8, this replaces its `streambuf`
* with one that reads/writes from/to the console using wide-character
* Windows APIs to avoid limitations of the code page's narrow encoding.
*
* * If `cin`, `cout`, or `cerr` is not attached to a Windows Console,
* this sets its stream to binary mode for consistency with the case
* that it's attached to a console.
*
* Destroy the instance of this to restore the original `streambuf`s.
*/
class Console : private Init
{
#ifdef _WIN32
class Impl;
std::unique_ptr<Impl> Impl_;
#endif
public:
Console();
~Console(); // NOLINT(performance-trivially-destructible)
Console(Console&&) noexcept;
Console(Console const&) = delete;
Console& operator=(Console&&) noexcept;
Console& operator=(Console const&) = delete;
};
}
}

View File

@@ -16,7 +16,6 @@
#include <utility>
#include <vector>
#include <cm/memory>
#include <cm/optional>
#include <cmext/algorithm>
@@ -24,7 +23,6 @@
#include "cmBuildOptions.h"
#include "cmCommandLineArgument.h"
#include "cmConsoleBuf.h"
#include "cmDocumentationEntry.h"
#include "cmGlobalGenerator.h"
#include "cmInstallScriptHandler.h"
@@ -35,7 +33,7 @@
#include "cmMessageMetadata.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStdIoInit.h"
#include "cmStdIoConsole.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
@@ -139,13 +137,13 @@ cmDocumentationEntry const cmDocumentationOptions[35] = {
#endif
int do_command(int ac, char const* const* av,
std::unique_ptr<cmConsoleBuf> consoleBuf)
cm::optional<cm::StdIo::Console> console)
{
std::vector<std::string> args;
args.reserve(ac - 1);
args.emplace_back(av[0]);
cm::append(args, av + 2, av + ac);
return cmcmd::ExecuteCMakeCommand(args, std::move(consoleBuf));
return cmcmd::ExecuteCMakeCommand(args, std::move(console));
}
cmMakefile* cmakemainGetMakefile(cmake* cm)
@@ -1143,11 +1141,7 @@ int do_open(int ac, char const* const* av)
int main(int ac, char const* const* av)
{
cm::StdIo::Init();
// Replace streambuf so we can output Unicode to console
auto consoleBuf = cm::make_unique<cmConsoleBuf>();
consoleBuf->SetUTF8Pipes();
cm::optional<cm::StdIo::Console> console = cm::StdIo::Console();
cmsys::Encoding::CommandLineArguments args =
cmsys::Encoding::CommandLineArguments::Main(ac, av);
@@ -1170,7 +1164,7 @@ int main(int ac, char const* const* av)
return do_workflow(ac, av);
}
if (strcmp(av[1], "-E") == 0) {
return do_command(ac, av, std::move(consoleBuf));
return do_command(ac, av, std::move(console));
}
if (strcmp(av[1], "--print-config-dir") == 0) {
std::cout << cmSystemTools::ConvertToOutputPath(

View File

@@ -12,7 +12,6 @@
#include <fcntl.h>
#include "cmCommandLineArgument.h"
#include "cmConsoleBuf.h"
#include "cmCryptoHash.h"
#include "cmDuration.h"
#include "cmGlobalGenerator.h"
@@ -26,6 +25,7 @@
#include "cmState.h"
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
#include "cmStdIoConsole.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTransformDepfile.h"
@@ -689,7 +689,7 @@ int cmcmd::HandleCoCompileCommands(std::vector<std::string> const& args)
}
int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
std::unique_ptr<cmConsoleBuf> consoleBuf)
cm::optional<cm::StdIo::Console> console)
{
// IF YOU ADD A NEW COMMAND, DOCUMENT IT ABOVE and in cmakemain.cxx
if (args.size() > 1) {
@@ -1191,7 +1191,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
if (arg == "-") {
doing_options = false;
// Destroy console buffers to drop cout/cerr encoding transform.
consoleBuf.reset();
console.reset();
cmCatFile(arg);
} else if (doing_options && cmHasLiteralPrefix(arg, "-")) {
if (arg == "--") {
@@ -1215,7 +1215,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
// Ignore empty files, this is not an error
} else {
// Destroy console buffers to drop cout/cerr encoding transform.
consoleBuf.reset();
console.reset();
cmCatFile(arg);
}
}
@@ -1464,11 +1464,11 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
}
if (args[1] == "vs_link_exe") {
return cmcmd::VisualStudioLink(args, 1, std::move(consoleBuf));
return cmcmd::VisualStudioLink(args, 1, std::move(console));
}
if (args[1] == "vs_link_dll") {
return cmcmd::VisualStudioLink(args, 2, std::move(consoleBuf));
return cmcmd::VisualStudioLink(args, 2, std::move(console));
}
if (args[1] == "cmake_llvm_rc") {
@@ -2221,7 +2221,7 @@ private:
// exe and dll's. This code does that in such a way that incremental linking
// still works.
int cmcmd::VisualStudioLink(std::vector<std::string> const& args, int type,
std::unique_ptr<cmConsoleBuf> consoleBuf)
cm::optional<cm::StdIo::Console> console)
{
// MSVC tools print output in the language specified by the VSLANG
// environment variable, and encoded in the console output code page.
@@ -2229,7 +2229,7 @@ int cmcmd::VisualStudioLink(std::vector<std::string> const& args, int type,
// RunCommand tells RunSingleCommand to *not* convert encoding, so
// we buffer the output in its original encoding instead of UTF-8.
// Drop our output encoding conversion so we print with original encoding.
consoleBuf.reset();
console.reset();
if (args.size() < 2) {
return -1;

View File

@@ -4,15 +4,20 @@
#include "cmConfigure.h" // IWYU pragma: keep
#include <memory>
#include <string>
#include <vector>
#include <cm/optional>
#include "cmsys/Status.hxx"
#include "cmCryptoHash.h"
class cmConsoleBuf;
namespace cm {
namespace StdIo {
class Console;
}
}
class cmcmd
{
@@ -22,7 +27,7 @@ public:
* as echo, remove file etc.
*/
static int ExecuteCMakeCommand(std::vector<std::string> const&,
std::unique_ptr<cmConsoleBuf> consoleBuf);
cm::optional<cm::StdIo::Console> console);
protected:
static int HandleCoCompileCommands(std::vector<std::string> const& args);
@@ -40,5 +45,5 @@ protected:
std::string const& intermediate_file);
static int RunLLVMRC(std::vector<std::string> const& args);
static int VisualStudioLink(std::vector<std::string> const& args, int type,
std::unique_ptr<cmConsoleBuf> consoleBuf);
cm::optional<cm::StdIo::Console> console);
};

View File

@@ -9,12 +9,11 @@
#include "cmsys/Encoding.hxx"
#include "cmCTest.h"
#include "cmConsoleBuf.h"
#include "cmDocumentation.h"
#include "cmDocumentationEntry.h"
#include "cmInstrumentation.h"
#include "cmInstrumentationQuery.h"
#include "cmStdIoInit.h"
#include "cmStdIoConsole.h"
#include "cmSystemTools.h"
#include "CTest/cmCTestLaunch.h"
@@ -167,11 +166,7 @@ cmDocumentationEntry const cmDocumentationOptions[] = {
// this is a test driver program for cmCTest.
int main(int argc, char const* const* argv)
{
cm::StdIo::Init();
// Replace streambuf so we can output Unicode to console
cmConsoleBuf consoleBuf;
consoleBuf.SetUTF8Pipes();
cm::StdIo::Console console;
cmsys::Encoding::CommandLineArguments encoding_args =
cmsys::Encoding::CommandLineArguments::Main(argc, argv);

View File

@@ -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()

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -1,9 +1,12 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include <string>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmStdIoConsole.h"
#include "cmStdIoInit.h"
#include "cmStdIoStream.h"
@@ -11,6 +14,18 @@
namespace {
#ifdef _WIN32
cm::string_view const kUTF8 =
" Chinese Hindi Greek English Russian\n "
"\xe6\xb3\xa8\xe6\x84\x8f " // Chinese
"\xe0\xa4\xaf\xe0\xa5\x82\xe0\xa4\xa8\xe0" // ...
"\xa4\xbf\xe0\xa4\x95\xe0\xa5\x8b\xe0\xa4\xa1 " // Hindi
"\xce\xb5\xce\xaf\xce\xbd\xce\xb1\xce\xb9 " // Greek
"very " // English
"\xd0\xb7\xd0\xb4\xd0\xbe\xd1\x80\xd0\xbe\xd0\xb2\xd0\xbe" // Russian
"!"_s;
#endif
void printTermKind(cm::string_view t, cm::StdIo::Stream& s)
{
switch (s.Kind()) {
@@ -37,12 +52,44 @@ bool testStream()
return true;
}
bool testConsoleStdIn = false;
bool testConsole()
{
std::cout << "testConsole()\n";
#ifdef _WIN32
std::cout << kUTF8 << '\n';
#endif
if (testConsoleStdIn) {
std::cout << " input: " << std::flush;
std::string line;
if (std::getline(std::cin, line)) {
std::cout << " output: " << line << '\n';
}
}
return true;
}
int testStdIo(int /*unused*/, char* /*unused*/[])
cm::string_view const kUsage = "usage: CMakeLibTests testStdIo [--stdin]"_s;
}
int testStdIo(int argc, char* argv[])
{
cm::StdIo::Init();
cm::StdIo::Console console;
for (int i = 1; i < argc; ++i) {
if (argv[i] == "--stdin"_s && !testConsoleStdIn) {
testConsoleStdIn = true;
} else {
std::cerr << kUsage << '\n';
return 1;
}
}
return runTests({
testStream,
testConsole,
});
}

View File

@@ -322,7 +322,6 @@ CMAKE_CXX_SOURCES="\
cmComputeLinkDepends \
cmComputeLinkInformation \
cmComputeTargetDepends \
cmConsoleBuf \
cmConditionEvaluator \
cmConfigureFileCommand \
cmContinueCommand \
@@ -489,6 +488,7 @@ CMAKE_CXX_SOURCES="\
cmState \
cmStateDirectory \
cmStateSnapshot \
cmStdIoConsole \
cmStdIoInit \
cmStdIoStream \
cmString \
@@ -544,6 +544,10 @@ if ${cmake_system_mingw}; then
"
fi
CMAKE_C_SOURCES="\
cm_utf8 \
"
CMAKE_STD_CXX_HEADERS="\
filesystem \
memory \