]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // |
2 | // Copyright (c) 2012 Artyom Beilis (Tonkikh) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See | |
20effc67 | 5 | // accompanying file LICENSE or copy at |
f67539c2 TL |
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 | |
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 { | |
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: | |
55 | console_output_buffer(HANDLE h) : handle_(h) | |
56 | {} | |
57 | ||
58 | protected: | |
59 | int sync() | |
60 | { | |
61 | return overflow(traits_type::eof()); | |
62 | } | |
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: | |
20effc67 TL |
84 | using decoder = utf::utf_traits<char>; |
85 | using encoder = utf::utf_traits<wchar_t>; | |
f67539c2 TL |
86 | |
87 | int write(const char* p, int n) | |
88 | { | |
20effc67 | 89 | namespace uf = utf; |
f67539c2 TL |
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: | |
20effc67 | 121 | console_input_buffer(HANDLE h) : handle_(h), wsize_(0), was_newline_(true) |
f67539c2 TL |
122 | {} |
123 | ||
124 | protected: | |
20effc67 TL |
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 | } | |
f67539c2 TL |
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 | ||
20effc67 | 147 | char* pnext; |
f67539c2 TL |
148 | if(pback_buffer_.empty()) |
149 | { | |
150 | pback_buffer_.resize(4); | |
20effc67 | 151 | pnext = &pback_buffer_[0] + pback_buffer_.size() - 1u; |
f67539c2 TL |
152 | } else |
153 | { | |
154 | size_t n = pback_buffer_.size(); | |
20effc67 TL |
155 | pback_buffer_.resize(n * 2); |
156 | std::memcpy(&pback_buffer_[n], &pback_buffer_[0], n); | |
157 | pnext = &pback_buffer_[0] + n - 1; | |
f67539c2 TL |
158 | } |
159 | ||
20effc67 TL |
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 | ||
f67539c2 TL |
165 | return 0; |
166 | } | |
167 | ||
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: | |
20effc67 TL |
183 | using decoder = utf::utf_traits<wchar_t>; |
184 | using encoder = utf::utf_traits<char>; | |
f67539c2 TL |
185 | |
186 | size_t read() | |
187 | { | |
f67539c2 | 188 | DWORD read_wchars = 0; |
20effc67 | 189 | const size_t n = wbuffer_size - wsize_; |
f67539c2 TL |
190 | if(!ReadConsoleW(handle_, wbuffer_ + wsize_, static_cast<DWORD>(n), &read_wchars, 0)) |
191 | return 0; | |
192 | wsize_ += read_wchars; | |
193 | char* out = buffer_; | |
20effc67 TL |
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) | |
f67539c2 | 197 | { |
20effc67 TL |
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) | |
f67539c2 TL |
207 | c = BOOST_NOWIDE_REPLACEMENT_CHARACTER; |
208 | assert(out - buffer_ + encoder::width(c) <= static_cast<int>(buffer_size)); | |
20effc67 TL |
209 | // Skip \r chars as std::cin does |
210 | if(c != '\r') | |
211 | out = encoder::encode(c, out); | |
f67539c2 TL |
212 | } |
213 | ||
20effc67 | 214 | wsize_ = end_input_ptr - cur_input_ptr; |
f67539c2 | 215 | if(wsize_ > 0) |
20effc67 TL |
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'; | |
f67539c2 TL |
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_; | |
20effc67 | 236 | bool was_newline_; |
f67539c2 TL |
237 | }; |
238 | ||
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 | } | |
258 | winconsole_ostream::~winconsole_ostream() | |
259 | { | |
260 | try | |
261 | { | |
262 | flush(); | |
263 | } catch(...) | |
264 | {} | |
265 | } | |
266 | ||
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 | ||
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 |