cmake/Source/kwsys/ConsoleBuf.hxx.in

399 lines
11 KiB
C++
Raw Normal View History

2017-04-14 19:02:05 +02:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
2016-10-30 18:24:19 +01:00
#ifndef @KWSYS_NAMESPACE@_ConsoleBuf_hxx
#define @KWSYS_NAMESPACE@_ConsoleBuf_hxx
#include <@KWSYS_NAMESPACE@/Configure.hxx>
2017-04-14 19:02:05 +02:00
2016-10-30 18:24:19 +01:00
#include <@KWSYS_NAMESPACE@/Encoding.hxx>
2017-04-14 19:02:05 +02:00
2016-10-30 18:24:19 +01:00
#include <cstring>
#include <iostream>
2017-04-14 19:02:05 +02:00
#include <sstream>
2016-10-30 18:24:19 +01:00
#include <stdexcept>
2017-04-14 19:02:05 +02:00
#include <streambuf>
#include <string>
2016-10-30 18:24:19 +01:00
#if defined(_WIN32)
2018-08-09 18:06:22 +02:00
# include <windows.h>
# if __cplusplus >= 201103L
# include <system_error>
# endif
2016-10-30 18:24:19 +01:00
#endif
2017-04-14 19:02:05 +02:00
namespace @KWSYS_NAMESPACE@ {
2016-10-30 18:24:19 +01:00
#if defined(_WIN32)
2017-04-14 19:02:05 +02:00
template <class CharT, class Traits = std::char_traits<CharT> >
2017-07-20 19:35:53 +02:00
class BasicConsoleBuf : public std::basic_streambuf<CharT, Traits>
2017-04-14 19:02:05 +02:00
{
public:
typedef typename Traits::int_type int_type;
typedef typename Traits::char_type char_type;
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
class Manager
{
public:
Manager(std::basic_ios<CharT, Traits>& ios, const bool err = false)
: m_consolebuf(0)
{
m_ios = &ios;
try {
m_consolebuf = new BasicConsoleBuf<CharT, Traits>(err);
m_streambuf = m_ios->rdbuf(m_consolebuf);
} catch (const std::runtime_error& ex) {
std::cerr << "Failed to create ConsoleBuf!" << std::endl
<< ex.what() << std::endl;
2016-10-30 18:24:19 +01:00
};
2017-04-14 19:02:05 +02:00
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
BasicConsoleBuf<CharT, Traits>* GetConsoleBuf() { return m_consolebuf; }
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
void SetUTF8Pipes()
{
if (m_consolebuf) {
m_consolebuf->input_pipe_codepage = CP_UTF8;
m_consolebuf->output_pipe_codepage = CP_UTF8;
m_consolebuf->activateCodepageChange();
2016-10-30 18:24:19 +01:00
}
2017-04-14 19:02:05 +02:00
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
~Manager()
{
if (m_consolebuf) {
delete m_consolebuf;
m_ios->rdbuf(m_streambuf);
2016-10-30 18:24:19 +01:00
}
2017-04-14 19:02:05 +02:00
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
private:
std::basic_ios<CharT, Traits>* m_ios;
std::basic_streambuf<CharT, Traits>* m_streambuf;
BasicConsoleBuf<CharT, Traits>* m_consolebuf;
};
BasicConsoleBuf(const bool 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()) {
const std::wstring wbuffer = getBuffer(m_obuffer);
if (m_isConsoleOutput) {
DWORD charsWritten;
success =
::WriteConsoleW(m_hOutput, wbuffer.c_str(), (DWORD)wbuffer.size(),
2020-02-01 23:06:01 +01:00
&charsWritten, nullptr) == 0
2017-04-14 19:02:05 +02:00
? false
: true;
} else {
DWORD bytesWritten;
std::string buffer;
success = encodeOutputBuffer(wbuffer, buffer);
if (success) {
2020-02-01 23:06:01 +01:00
success =
::WriteFile(m_hOutput, buffer.c_str(), (DWORD)buffer.size(),
&bytesWritten, nullptr) == 0
2017-04-14 19:02:05 +02:00
? false
: true;
2016-10-30 18:24:19 +01:00
}
}
2017-04-14 19:02:05 +02:00
}
m_ibuffer.clear();
m_obuffer.clear();
_setg();
_setp();
return success ? 0 : -1;
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
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,
2020-02-01 23:06:01 +01:00
nullptr) == 0 ||
2017-04-14 19:02:05 +02:00
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];
2020-02-01 23:06:01 +01:00
while (ReadFile(m_hInput, buffer, size.LowPart, &bytesRead, nullptr) ==
2017-04-14 19:02:05 +02:00
0) {
if (GetLastError() == ERROR_MORE_DATA) {
strbuffer += std::string(buffer, bytesRead);
continue;
2016-10-30 18:24:19 +01:00
}
2017-04-14 19:02:05 +02:00
_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();
2016-10-30 18:24:19 +01:00
}
2017-04-14 19:02:05 +02:00
setBuffer(wbuffer, m_ibuffer);
2016-10-30 18:24:19 +01:00
}
2017-04-14 19:02:05 +02:00
_setg();
}
return Traits::to_int_type(*this->gptr());
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
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)) {
2016-10-30 18:24:19 +01:00
sync();
}
2017-04-14 19:02:05 +02:00
return ch;
}
sync();
return Traits::eof();
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
public:
bool flush_on_newline;
UINT input_pipe_codepage;
UINT output_pipe_codepage;
UINT input_file_codepage;
UINT output_file_codepage;
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
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";
2018-08-09 18:06:22 +02:00
# if __cplusplus >= 201103L
2017-04-14 19:02:05 +02:00
throw std::system_error(::GetLastError(), std::system_category(),
errmsg);
2018-08-09 18:06:22 +02:00
# else
2017-04-14 19:02:05 +02:00
throw std::runtime_error(errmsg);
2018-08-09 18:06:22 +02:00
# endif
2017-04-14 19:02:05 +02:00
}
}
UINT getConsolesCodepage()
{
if (!m_consolesCodepage) {
m_consolesCodepage = GetConsoleCP();
if (!m_consolesCodepage) {
m_consolesCodepage = GetACP();
2016-10-30 18:24:19 +01:00
}
2017-04-14 19:02:05 +02:00
}
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;
2016-10-30 18:24:19 +01:00
}
2018-04-23 21:13:27 +02:00
@KWSYS_NAMESPACE@_FALLTHROUGH;
2017-04-14 19:02:05 +02:00
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;
2016-10-30 18:24:19 +01:00
}
2018-04-23 21:13:27 +02:00
@KWSYS_NAMESPACE@_FALLTHROUGH;
2017-04-14 19:02:05 +02:00
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(const std::wstring wbuffer, std::string& buffer)
{
if (wbuffer.size() == 0) {
buffer = std::string();
return true;
}
const int length =
WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(),
2020-02-01 23:06:01 +01:00
(int)wbuffer.size(), nullptr, 0, nullptr, nullptr);
2017-04-14 19:02:05 +02:00
char* buf = new char[length];
const bool success =
WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(),
2020-02-01 23:06:01 +01:00
(int)wbuffer.size(), buf, length, nullptr,
nullptr) > 0
2017-04-14 19:02:05 +02:00
? true
: false;
buffer = std::string(buf, length);
delete[] buf;
return success;
}
bool decodeInputBuffer(const std::string buffer, std::wstring& wbuffer)
{
2017-07-20 19:35:53 +02:00
size_t length = buffer.length();
2017-04-14 19:02:05 +02:00
if (length == 0) {
wbuffer = std::wstring();
return true;
}
int actualCodepage = m_activeInputCodepage;
const char BOM_UTF8[] = { char(0xEF), char(0xBB), char(0xBF) };
const char* data = buffer.data();
const size_t 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;
}
2017-07-20 19:35:53 +02:00
const size_t wlength = static_cast<size_t>(MultiByteToWideChar(
2020-02-01 23:06:01 +01:00
actualCodepage, 0, data, static_cast<int>(length), nullptr, 0));
2017-04-14 19:02:05 +02:00
wchar_t* wbuf = new wchar_t[wlength];
const bool success =
2017-07-20 19:35:53 +02:00
MultiByteToWideChar(actualCodepage, 0, data, static_cast<int>(length),
wbuf, static_cast<int>(wlength)) > 0
2017-04-14 19:02:05 +02:00
? true
: false;
wbuffer = std::wstring(wbuf, wlength);
delete[] wbuf;
return success;
}
std::wstring getBuffer(const std::basic_string<char> buffer)
{
return Encoding::ToWide(buffer);
}
std::wstring getBuffer(const std::basic_string<wchar_t> buffer)
{
return buffer;
}
void setBuffer(const std::wstring wbuffer, std::basic_string<char>& target)
{
target = Encoding::ToNarrow(wbuffer);
}
void setBuffer(const std::wstring wbuffer,
std::basic_string<wchar_t>& target)
{
target = wbuffer;
}
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
}; // BasicConsoleBuf class
2016-10-30 18:24:19 +01:00
2017-04-14 19:02:05 +02:00
typedef BasicConsoleBuf<char> ConsoleBuf;
typedef BasicConsoleBuf<wchar_t> WConsoleBuf;
2016-10-30 18:24:19 +01:00
#endif
} // KWSYS_NAMESPACE
#endif