diff options
author | Brad King <brad.king@kitware.com> | 2016-09-14 13:41:55 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2016-09-14 13:41:55 (GMT) |
commit | e4fc770fa39f8433147cae4849923286e5b3511a (patch) | |
tree | a46db42095c88d36520f49f9f76a289bb304892a /Source/kwsys/ConsoleBuf.hxx.in | |
parent | a7305ef00a70518f7abb66211ef09b0ca6d4af71 (diff) | |
parent | b80d6136321fb6c2be019dec4af4b1e486389e2c (diff) | |
download | CMake-e4fc770fa39f8433147cae4849923286e5b3511a.zip CMake-e4fc770fa39f8433147cae4849923286e5b3511a.tar.gz CMake-e4fc770fa39f8433147cae4849923286e5b3511a.tar.bz2 |
Merge branch 'upstream-KWSys' into update-kwsys
* upstream-KWSys:
KWSys 2016-09-14 (e736efa1)
Diffstat (limited to 'Source/kwsys/ConsoleBuf.hxx.in')
-rw-r--r-- | Source/kwsys/ConsoleBuf.hxx.in | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/Source/kwsys/ConsoleBuf.hxx.in b/Source/kwsys/ConsoleBuf.hxx.in new file mode 100644 index 0000000..8aeeda1 --- /dev/null +++ b/Source/kwsys/ConsoleBuf.hxx.in @@ -0,0 +1,348 @@ +/*============================================================================ + KWSys - Kitware System Library + Copyright 2000-2016 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#ifndef @KWSYS_NAMESPACE@_ConsoleBuf_hxx +#define @KWSYS_NAMESPACE@_ConsoleBuf_hxx + +#include <@KWSYS_NAMESPACE@/Configure.hxx> +#include <@KWSYS_NAMESPACE@/Encoding.hxx> +#include <string> +#include <cstring> +#include <sstream> +#include <streambuf> +#include <iostream> +#include <stdexcept> + +#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 @KWSYS_NAMESPACE@_EXPORT 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; + }; + } + + ~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) { + wchar_t wbuffer[128]; + DWORD charsRead; + if (::ReadConsoleW(m_hInput, wbuffer, (sizeof(wbuffer) / sizeof(wbuffer[0])) - 1, + &charsRead, NULL) == 0 || charsRead == 0) { + _setg(true); + return Traits::eof(); + } + wbuffer[charsRead] = L'\0'; + setBuffer(wbuffer, m_ibuffer); + } else { + std::wstring totalBuffer; + std::wstring wbuffer; + char buffer[128]; + DWORD bytesRead; + while (::ReadFile(m_hInput, buffer, (sizeof(buffer) / sizeof(buffer[0])) - 1, + &bytesRead, NULL) == 0) { + if (::GetLastError() == ERROR_MORE_DATA) { + buffer[bytesRead] = '\0'; + if (decodeInputBuffer(buffer, wbuffer)) { + totalBuffer += wbuffer; + continue; + } + } + _setg(true); + return Traits::eof(); + } + buffer[bytesRead] = '\0'; + if (!decodeInputBuffer(buffer, wbuffer)) { + _setg(true); + return Traits::eof(); + } + totalBuffer += wbuffer; + setBuffer(totalBuffer, 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: + m_isConsoleInput = true; + break; + 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: + m_isConsoleOutput = true; + break; + 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) { + const int length = WideCharToMultiByte(m_activeOutputCodepage, 0, + wbuffer.c_str(), + (int)wbuffer.size(), NULL, 0, + NULL, NULL); + char *buf = new char[length + 1]; + const bool success = WideCharToMultiByte(m_activeOutputCodepage, 0, + wbuffer.c_str(), + (int)wbuffer.size(), buf, + length, NULL, NULL) > 0 + ? true : false; + buf[length] = '\0'; + buffer = buf; + delete[] buf; + return success; + } + bool decodeInputBuffer(const char *buffer, std::wstring &wbuffer) { + int actualCodepage = m_activeInputCodepage; + const char BOM_UTF8[] = { char(0xEF), char(0xBB), char(0xBF) }; + if (std::memcmp(buffer, BOM_UTF8, sizeof(BOM_UTF8)) == 0) { + // PowerShell uses UTF-8 with BOM for pipes + actualCodepage = CP_UTF8; + buffer += sizeof(BOM_UTF8); + } + const int wlength = MultiByteToWideChar(actualCodepage, 0, buffer, + -1, NULL, 0); + wchar_t *wbuf = new wchar_t[wlength]; + const bool success = MultiByteToWideChar(actualCodepage, 0, buffer, + -1, wbuf, wlength) > 0 + ? true : false; + wbuffer = wbuf; + 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 |