• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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