]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/nowide/src/iostream.cpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / libs / nowide / src / iostream.cpp
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
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:
84 using decoder = utf::utf_traits<char>;
85 using encoder = utf::utf_traits<wchar_t>;
86
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:
121 console_input_buffer(HANDLE h) : handle_(h), wsize_(0), was_newline_(true)
122 {}
123
124 protected:
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 }
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
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 using decoder = utf::utf_traits<wchar_t>;
184 using encoder = utf::utf_traits<char>;
185
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
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