2 // Copyright (c) 2012 Artyom Beilis (Tonkikh)
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)
8 #define BOOST_NOWIDE_SOURCE
9 #include <boost/nowide/iostream.hpp>
15 /// Avoid empty compilation unit warnings
17 BOOST_NOWIDE_DECL
void dummy_exported_function()
24 #include <boost/nowide/convert.hpp>
41 bool is_atty_handle(HANDLE h
)
46 return GetConsoleMode(h
, &dummy
) != FALSE
;
52 class console_output_buffer
: public std::streambuf
55 console_output_buffer(HANDLE h
) : handle_(h
)
61 return overflow(traits_type::eof());
67 int n
= static_cast<int>(pptr() - pbase());
70 if(n
> 0 && (r
= write(pbase(), n
)) < 0)
74 std::memmove(pbase(), pbase() + r
, n
- r
);
76 setp(buffer_
, buffer_
+ buffer_size
);
78 if(c
!= traits_type::eof())
79 sputc(traits_type::to_char_type(c
));
84 using decoder
= utf::utf_traits
<char>;
85 using encoder
= utf::utf_traits
<wchar_t>;
87 int write(const char* p
, int n
)
91 const char* e
= p
+ n
;
95 wchar_t* out
= wbuffer_
;
98 while((c
= decoder::decode(p
, e
)) != uf::incomplete
)
101 c
= BOOST_NOWIDE_REPLACEMENT_CHARACTER
;
102 assert(out
- wbuffer_
+ encoder::width(c
) <= static_cast<int>(wbuffer_size
));
103 out
= encoder::encode(c
, out
);
106 if(!WriteConsoleW(handle_
, wbuffer_
, static_cast<DWORD
>(out
- wbuffer_
), &size
, 0))
108 return static_cast<int>(decoded
);
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
];
118 class console_input_buffer
: public std::streambuf
121 console_input_buffer(HANDLE h
) : handle_(h
), wsize_(0), was_newline_(true)
127 if(FlushConsoleInputBuffer(handle_
) == 0)
131 pback_buffer_
.clear();
137 if(c
== traits_type::eof())
138 return traits_type::eof();
140 if(gptr() != eback())
143 *gptr() = traits_type::to_char_type(c
);
148 if(pback_buffer_
.empty())
150 pback_buffer_
.resize(4);
151 pnext
= &pback_buffer_
[0] + pback_buffer_
.size() - 1u;
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;
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
);
172 if(!pback_buffer_
.empty())
173 pback_buffer_
.clear();
176 setg(buffer_
, buffer_
, buffer_
+ n
);
178 return traits_type::eof();
179 return std::char_traits
<char>::to_int_type(*gptr());
183 using decoder
= utf::utf_traits
<wchar_t>;
184 using encoder
= utf::utf_traits
<char>;
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))
192 wsize_
+= read_wchars
;
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
)
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
)
203 cur_input_ptr
= prev_input_ptr
;
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
211 out
= encoder::encode(c
, out
);
214 wsize_
= end_input_ptr
- cur_input_ptr
;
216 std::memmove(wbuffer_
, end_input_ptr
- wsize_
, sizeof(wchar_t) * wsize_
);
218 // A CTRL+Z at the start of the line should be treated as EOF
219 if(was_newline_
&& out
> buffer_
&& buffer_
[0] == '\x1a')
224 was_newline_
= out
== buffer_
|| out
[-1] == '\n';
226 return out
- buffer_
;
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
];
235 std::vector
<char> pback_buffer_
;
239 winconsole_ostream::winconsole_ostream(int fd
, winconsole_ostream
* tieStream
) : std::ostream(0)
244 case 1: h
= GetStdHandle(STD_OUTPUT_HANDLE
); break;
245 case 2: h
= GetStdHandle(STD_ERROR_HANDLE
); break;
247 if(is_atty_handle(h
))
249 d
.reset(new console_output_buffer(h
));
250 std::ostream::rdbuf(d
.get());
253 std::ostream::rdbuf(fd
== 1 ? std::cout
.rdbuf() : std::cerr
.rdbuf());
258 winconsole_ostream::~winconsole_ostream()
267 winconsole_istream::winconsole_istream(winconsole_ostream
* tieStream
) : std::istream(0)
269 HANDLE h
= GetStdHandle(STD_INPUT_HANDLE
);
270 if(is_atty_handle(h
))
272 d
.reset(new console_input_buffer(h
));
273 std::istream::rdbuf(d
.get());
276 std::istream::rdbuf(std::cin
.rdbuf());
282 winconsole_istream::~winconsole_istream()
285 } // namespace detail
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