123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing#kwsys for details. */
- #ifndef cmsys_ConsoleBuf_hxx
- #define cmsys_ConsoleBuf_hxx
- #include <cmsys/Configure.hxx>
- #include <cmsys/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 cmsys {
- #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;
- }
- cmsys_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;
- }
- cmsys_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
|