summaryrefslogtreecommitdiffstats
path: root/Source/kwsys/ConsoleBuf.hxx.in
diff options
context:
space:
mode:
Diffstat (limited to 'Source/kwsys/ConsoleBuf.hxx.in')
-rw-r--r--Source/kwsys/ConsoleBuf.hxx.in396
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