diff options
Diffstat (limited to 'Source/kwsys/ConsoleBuf.hxx.in')
-rw-r--r-- | Source/kwsys/ConsoleBuf.hxx.in | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/Source/kwsys/ConsoleBuf.hxx.in b/Source/kwsys/ConsoleBuf.hxx.in new file mode 100644 index 0000000..73a1efb --- /dev/null +++ b/Source/kwsys/ConsoleBuf.hxx.in @@ -0,0 +1,396 @@ +/* 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, 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; + }; + } + + 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(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(), + &charsWritten, NULL) == 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, NULL) == 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, + NULL) == 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, NULL) == + 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(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(), + (int)wbuffer.size(), NULL, 0, NULL, NULL); + char* buf = new char[length]; + const bool success = + WideCharToMultiByte(m_activeOutputCodepage, 0, wbuffer.c_str(), + (int)wbuffer.size(), buf, length, NULL, NULL) > 0 + ? true + : false; + buffer = std::string(buf, length); + delete[] buf; + return success; + } + bool decodeInputBuffer(const std::string buffer, std::wstring& wbuffer) + { + size_t length = buffer.length(); + 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; + } + const size_t wlength = static_cast<size_t>(MultiByteToWideChar( + actualCodepage, 0, data, static_cast<int>(length), NULL, 0)); + wchar_t* wbuf = new wchar_t[wlength]; + const bool 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(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; + } + +}; // BasicConsoleBuf class + +typedef BasicConsoleBuf<char> ConsoleBuf; +typedef BasicConsoleBuf<wchar_t> WConsoleBuf; + +#endif +} // KWSYS_NAMESPACE + +#endif |