1 // 2 // Copyright (c) 2012 Artyom Beilis (Tonkikh) 3 // 4 // Distributed under the Boost Software License, Version 1.0. (See 5 // accompanying file LICENSE or copy at 6 // http://www.boost.org/LICENSE_1_0.txt) 7 // 8 #define BOOST_NOWIDE_SOURCE 9 #include <boost/nowide/iostream.hpp> 10 11 #ifndef BOOST_WINDOWS 12 13 namespace boost { 14 namespace nowide { 15 /// Avoid empty compilation unit warnings 16 /// \internal dummy_exported_function()17 BOOST_NOWIDE_DECL void dummy_exported_function() 18 {} 19 } // namespace nowide 20 } // namespace boost 21 22 #else 23 24 #include <boost/nowide/convert.hpp> 25 #include <cassert> 26 #include <cstring> 27 #include <iostream> 28 #include <vector> 29 30 #ifndef NOMINMAX 31 #define NOMINMAX 32 #endif 33 34 #include <windows.h> 35 36 namespace boost { 37 namespace nowide { 38 namespace detail { 39 40 namespace { is_atty_handle(HANDLE h)41 bool is_atty_handle(HANDLE h) 42 { 43 if(h) 44 { 45 DWORD dummy; 46 return GetConsoleMode(h, &dummy) != FALSE; 47 } 48 return false; 49 } 50 } // namespace 51 52 class console_output_buffer : public std::streambuf 53 { 54 public: console_output_buffer(HANDLE h)55 console_output_buffer(HANDLE h) : handle_(h) 56 {} 57 58 protected: sync()59 int sync() 60 { 61 return overflow(traits_type::eof()); 62 } overflow(int c)63 int overflow(int c) 64 { 65 if(!handle_) 66 return -1; 67 int n = static_cast<int>(pptr() - pbase()); 68 int r = 0; 69 70 if(n > 0 && (r = write(pbase(), n)) < 0) 71 return -1; 72 if(r < n) 73 { 74 std::memmove(pbase(), pbase() + r, n - r); 75 } 76 setp(buffer_, buffer_ + buffer_size); 77 pbump(n - r); 78 if(c != traits_type::eof()) 79 sputc(traits_type::to_char_type(c)); 80 return 0; 81 } 82 83 private: 84 typedef utf::utf_traits<char> decoder; 85 typedef utf::utf_traits<wchar_t> encoder; 86 write(const char * p,int n)87 int write(const char* p, int n) 88 { 89 namespace uf = utf; 90 const char* b = p; 91 const char* e = p + n; 92 DWORD size = 0; 93 if(n > buffer_size) 94 return -1; 95 wchar_t* out = wbuffer_; 96 uf::code_point c; 97 size_t decoded = 0; 98 while((c = decoder::decode(p, e)) != uf::incomplete) 99 { 100 if(c == uf::illegal) 101 c = BOOST_NOWIDE_REPLACEMENT_CHARACTER; 102 assert(out - wbuffer_ + encoder::width(c) <= static_cast<int>(wbuffer_size)); 103 out = encoder::encode(c, out); 104 decoded = p - b; 105 } 106 if(!WriteConsoleW(handle_, wbuffer_, static_cast<DWORD>(out - wbuffer_), &size, 0)) 107 return -1; 108 return static_cast<int>(decoded); 109 } 110 111 static const int buffer_size = 1024; 112 static const int wbuffer_size = buffer_size * encoder::max_width; 113 char buffer_[buffer_size]; 114 wchar_t wbuffer_[wbuffer_size]; 115 HANDLE handle_; 116 }; 117 118 class console_input_buffer : public std::streambuf 119 { 120 public: console_input_buffer(HANDLE h)121 console_input_buffer(HANDLE h) : handle_(h), wsize_(0), was_newline_(true) 122 {} 123 124 protected: sync()125 int sync() 126 { 127 if(FlushConsoleInputBuffer(handle_) == 0) 128 return -1; 129 wsize_ = 0; 130 was_newline_ = true; 131 pback_buffer_.clear(); 132 setg(0, 0, 0); 133 return 0; 134 } pbackfail(int c)135 int pbackfail(int c) 136 { 137 if(c == traits_type::eof()) 138 return traits_type::eof(); 139 140 if(gptr() != eback()) 141 { 142 gbump(-1); 143 *gptr() = traits_type::to_char_type(c); 144 return 0; 145 } 146 147 char* pnext; 148 if(pback_buffer_.empty()) 149 { 150 pback_buffer_.resize(4); 151 pnext = &pback_buffer_[0] + pback_buffer_.size() - 1u; 152 } else 153 { 154 size_t n = pback_buffer_.size(); 155 pback_buffer_.resize(n * 2); 156 std::memcpy(&pback_buffer_[n], &pback_buffer_[0], n); 157 pnext = &pback_buffer_[0] + n - 1; 158 } 159 160 char* pFirst = &pback_buffer_[0]; 161 char* pLast = pFirst + pback_buffer_.size(); 162 setg(pFirst, pnext, pLast); 163 *gptr() = traits_type::to_char_type(c); 164 165 return 0; 166 } 167 underflow()168 int underflow() 169 { 170 if(!handle_) 171 return -1; 172 if(!pback_buffer_.empty()) 173 pback_buffer_.clear(); 174 175 size_t n = read(); 176 setg(buffer_, buffer_, buffer_ + n); 177 if(n == 0) 178 return traits_type::eof(); 179 return std::char_traits<char>::to_int_type(*gptr()); 180 } 181 182 private: 183 typedef utf::utf_traits<wchar_t> decoder; 184 typedef utf::utf_traits<char> encoder; 185 read()186 size_t read() 187 { 188 DWORD read_wchars = 0; 189 const size_t n = wbuffer_size - wsize_; 190 if(!ReadConsoleW(handle_, wbuffer_ + wsize_, static_cast<DWORD>(n), &read_wchars, 0)) 191 return 0; 192 wsize_ += read_wchars; 193 char* out = buffer_; 194 const wchar_t* cur_input_ptr = wbuffer_; 195 const wchar_t* const end_input_ptr = wbuffer_ + wsize_; 196 while(cur_input_ptr != end_input_ptr) 197 { 198 const wchar_t* const prev_input_ptr = cur_input_ptr; 199 utf::code_point c = decoder::decode(cur_input_ptr, end_input_ptr); 200 // If incomplete restore to beginning of incomplete char to use on next buffer 201 if(c == utf::incomplete) 202 { 203 cur_input_ptr = prev_input_ptr; 204 break; 205 } 206 if(c == utf::illegal) 207 c = BOOST_NOWIDE_REPLACEMENT_CHARACTER; 208 assert(out - buffer_ + encoder::width(c) <= static_cast<int>(buffer_size)); 209 // Skip \r chars as std::cin does 210 if(c != '\r') 211 out = encoder::encode(c, out); 212 } 213 214 wsize_ = end_input_ptr - cur_input_ptr; 215 if(wsize_ > 0) 216 std::memmove(wbuffer_, end_input_ptr - wsize_, sizeof(wchar_t) * wsize_); 217 218 // A CTRL+Z at the start of the line should be treated as EOF 219 if(was_newline_ && out > buffer_ && buffer_[0] == '\x1a') 220 { 221 sync(); 222 return 0; 223 } 224 was_newline_ = out == buffer_ || out[-1] == '\n'; 225 226 return out - buffer_; 227 } 228 229 static const size_t wbuffer_size = 1024; 230 static const size_t buffer_size = wbuffer_size * encoder::max_width; 231 char buffer_[buffer_size]; 232 wchar_t wbuffer_[wbuffer_size]; 233 HANDLE handle_; 234 size_t wsize_; 235 std::vector<char> pback_buffer_; 236 bool was_newline_; 237 }; 238 winconsole_ostream(int fd,winconsole_ostream * tieStream)239 winconsole_ostream::winconsole_ostream(int fd, winconsole_ostream* tieStream) : std::ostream(0) 240 { 241 HANDLE h = 0; 242 switch(fd) 243 { 244 case 1: h = GetStdHandle(STD_OUTPUT_HANDLE); break; 245 case 2: h = GetStdHandle(STD_ERROR_HANDLE); break; 246 } 247 if(is_atty_handle(h)) 248 { 249 d.reset(new console_output_buffer(h)); 250 std::ostream::rdbuf(d.get()); 251 } else 252 { 253 std::ostream::rdbuf(fd == 1 ? std::cout.rdbuf() : std::cerr.rdbuf()); 254 } 255 if(tieStream) 256 tie(tieStream); 257 } ~winconsole_ostream()258 winconsole_ostream::~winconsole_ostream() 259 { 260 try 261 { 262 flush(); 263 } catch(...) 264 {} 265 } 266 winconsole_istream(winconsole_ostream * tieStream)267 winconsole_istream::winconsole_istream(winconsole_ostream* tieStream) : std::istream(0) 268 { 269 HANDLE h = GetStdHandle(STD_INPUT_HANDLE); 270 if(is_atty_handle(h)) 271 { 272 d.reset(new console_input_buffer(h)); 273 std::istream::rdbuf(d.get()); 274 } else 275 { 276 std::istream::rdbuf(std::cin.rdbuf()); 277 } 278 if(tieStream) 279 tie(tieStream); 280 } 281 ~winconsole_istream()282 winconsole_istream::~winconsole_istream() 283 {} 284 285 } // namespace detail 286 287 detail::winconsole_ostream cout(1, NULL); 288 detail::winconsole_istream cin(&cout); 289 detail::winconsole_ostream cerr(2, &cout); 290 detail::winconsole_ostream clog(2, &cout); 291 } // namespace nowide 292 } // namespace boost 293 294 #endif 295