1 | // -*- C++ -*- |
2 | //===----------------------------------------------------------------------===// |
3 | // |
4 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
5 | // See https://llvm.org/LICENSE.txt for license information. |
6 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
7 | // |
8 | //===----------------------------------------------------------------------===// |
9 | |
10 | #ifndef _LIBCPP_STD_STREAM_H |
11 | #define _LIBCPP_STD_STREAM_H |
12 | |
13 | #include <__config> |
14 | #include <__locale> |
15 | #include <cstdio> |
16 | #include <istream> |
17 | #include <ostream> |
18 | |
19 | #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) |
20 | # pragma GCC system_header |
21 | #endif |
22 | |
23 | _LIBCPP_PUSH_MACROS |
24 | #include <__undef_macros> |
25 | |
26 | _LIBCPP_BEGIN_NAMESPACE_STD |
27 | |
28 | static const int __limit = 8; |
29 | |
30 | // __stdinbuf |
31 | |
32 | template <class _CharT> |
33 | class _LIBCPP_HIDDEN __stdinbuf : public basic_streambuf<_CharT, char_traits<_CharT> > { |
34 | public: |
35 | typedef _CharT char_type; |
36 | typedef char_traits<char_type> traits_type; |
37 | typedef typename traits_type::int_type int_type; |
38 | typedef typename traits_type::pos_type pos_type; |
39 | typedef typename traits_type::off_type off_type; |
40 | typedef typename traits_type::state_type state_type; |
41 | |
42 | __stdinbuf(FILE* __fp, state_type* __st); |
43 | |
44 | protected: |
45 | virtual int_type underflow(); |
46 | virtual int_type uflow(); |
47 | virtual int_type pbackfail(int_type __c = traits_type::eof()); |
48 | virtual void imbue(const locale& __loc); |
49 | |
50 | private: |
51 | FILE* __file_; |
52 | const codecvt<char_type, char, state_type>* __cv_; |
53 | state_type* __st_; |
54 | int __encoding_; |
55 | int_type __last_consumed_; |
56 | bool __last_consumed_is_next_; |
57 | bool __always_noconv_; |
58 | |
59 | #if defined(_LIBCPP_WIN32API) |
60 | static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>; |
61 | #else |
62 | static constexpr bool __is_win32api_wide_char = false; |
63 | #endif |
64 | |
65 | __stdinbuf(const __stdinbuf&); |
66 | __stdinbuf& operator=(const __stdinbuf&); |
67 | |
68 | int_type __getchar(bool __consume); |
69 | }; |
70 | |
71 | template <class _CharT> |
72 | __stdinbuf<_CharT>::__stdinbuf(FILE* __fp, state_type* __st) |
73 | : __file_(__fp), __st_(__st), __last_consumed_(traits_type::eof()), __last_consumed_is_next_(false) { |
74 | imbue(this->getloc()); |
75 | // On Windows, in wchar_t mode, ignore the codecvt from the locale by |
76 | // default and assume noconv; this passes wchar_t through unmodified from |
77 | // getwc. If the user sets a custom locale with imbue(), that gets honored, |
78 | // the IO is done with getc() and converted with the provided codecvt. |
79 | if constexpr (__is_win32api_wide_char) |
80 | __always_noconv_ = true; |
81 | } |
82 | |
83 | template <class _CharT> |
84 | void __stdinbuf<_CharT>::imbue(const locale& __loc) { |
85 | __cv_ = &use_facet<codecvt<char_type, char, state_type> >(__loc); |
86 | __encoding_ = __cv_->encoding(); |
87 | __always_noconv_ = __cv_->always_noconv(); |
88 | if (__encoding_ > __limit) |
89 | __throw_runtime_error("unsupported locale for standard input" ); |
90 | } |
91 | |
92 | template <class _CharT> |
93 | typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::underflow() { |
94 | return __getchar(false); |
95 | } |
96 | |
97 | template <class _CharT> |
98 | typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::uflow() { |
99 | return __getchar(true); |
100 | } |
101 | |
102 | inline bool __do_getc(FILE* __fp, char* __pbuf) { |
103 | int __c = getc(stream: __fp); |
104 | if (__c == EOF) |
105 | return false; |
106 | *__pbuf = static_cast<char>(__c); |
107 | return true; |
108 | } |
109 | #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS |
110 | inline bool __do_getc(FILE* __fp, wchar_t* __pbuf) { |
111 | wint_t __c = getwc(stream: __fp); |
112 | if (__c == WEOF) |
113 | return false; |
114 | *__pbuf = static_cast<wchar_t>(__c); |
115 | return true; |
116 | } |
117 | #endif |
118 | |
119 | inline bool __do_ungetc(int __c, FILE* __fp, char __dummy) { |
120 | if (ungetc(__c, stream: __fp) == EOF) |
121 | return false; |
122 | return true; |
123 | } |
124 | #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS |
125 | inline bool __do_ungetc(std::wint_t __c, FILE* __fp, wchar_t __dummy) { |
126 | if (ungetwc(wc: __c, stream: __fp) == WEOF) |
127 | return false; |
128 | return true; |
129 | } |
130 | #endif |
131 | |
132 | template <class _CharT> |
133 | typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::__getchar(bool __consume) { |
134 | if (__last_consumed_is_next_) { |
135 | int_type __result = __last_consumed_; |
136 | if (__consume) { |
137 | __last_consumed_ = traits_type::eof(); |
138 | __last_consumed_is_next_ = false; |
139 | } |
140 | return __result; |
141 | } |
142 | if (__always_noconv_) { |
143 | char_type __1buf; |
144 | if (!__do_getc(__file_, &__1buf)) |
145 | return traits_type::eof(); |
146 | if (!__consume) { |
147 | if (!__do_ungetc(traits_type::to_int_type(__1buf), __file_, __1buf)) |
148 | return traits_type::eof(); |
149 | } else |
150 | __last_consumed_ = traits_type::to_int_type(__1buf); |
151 | return traits_type::to_int_type(__1buf); |
152 | } |
153 | |
154 | char __extbuf[__limit]; |
155 | int __nread = std::max(1, __encoding_); |
156 | for (int __i = 0; __i < __nread; ++__i) { |
157 | int __c = getc(__file_); |
158 | if (__c == EOF) |
159 | return traits_type::eof(); |
160 | __extbuf[__i] = static_cast<char>(__c); |
161 | } |
162 | char_type __1buf; |
163 | const char* __enxt; |
164 | char_type* __inxt; |
165 | codecvt_base::result __r; |
166 | do { |
167 | state_type __sv_st = *__st_; |
168 | __r = __cv_->in(*__st_, __extbuf, __extbuf + __nread, __enxt, &__1buf, &__1buf + 1, __inxt); |
169 | switch (__r) { |
170 | case std::codecvt_base::ok: |
171 | break; |
172 | case codecvt_base::partial: |
173 | *__st_ = __sv_st; |
174 | if (__nread == sizeof(__extbuf)) |
175 | return traits_type::eof(); |
176 | { |
177 | int __c = getc(__file_); |
178 | if (__c == EOF) |
179 | return traits_type::eof(); |
180 | __extbuf[__nread] = static_cast<char>(__c); |
181 | } |
182 | ++__nread; |
183 | break; |
184 | case codecvt_base::error: |
185 | return traits_type::eof(); |
186 | case std::codecvt_base::noconv: |
187 | __1buf = static_cast<char_type>(__extbuf[0]); |
188 | break; |
189 | } |
190 | } while (__r == std::codecvt_base::partial); |
191 | if (!__consume) { |
192 | for (int __i = __nread; __i > 0;) { |
193 | if (ungetc(traits_type::to_int_type(__extbuf[--__i]), __file_) == EOF) |
194 | return traits_type::eof(); |
195 | } |
196 | } else |
197 | __last_consumed_ = traits_type::to_int_type(__1buf); |
198 | return traits_type::to_int_type(__1buf); |
199 | } |
200 | |
201 | template <class _CharT> |
202 | typename __stdinbuf<_CharT>::int_type __stdinbuf<_CharT>::pbackfail(int_type __c) { |
203 | if (traits_type::eq_int_type(__c, traits_type::eof())) { |
204 | if (!__last_consumed_is_next_) { |
205 | __c = __last_consumed_; |
206 | __last_consumed_is_next_ = !traits_type::eq_int_type(__last_consumed_, traits_type::eof()); |
207 | } |
208 | return __c; |
209 | } |
210 | if (__always_noconv_ && __last_consumed_is_next_) { |
211 | if (!__do_ungetc(__last_consumed_, __file_, traits_type::to_char_type(__last_consumed_))) |
212 | return traits_type::eof(); |
213 | } else if (__last_consumed_is_next_) { |
214 | char __extbuf[__limit]; |
215 | char* __enxt; |
216 | const char_type __ci = traits_type::to_char_type(__last_consumed_); |
217 | const char_type* __inxt; |
218 | switch (__cv_->out(*__st_, &__ci, &__ci + 1, __inxt, __extbuf, __extbuf + sizeof(__extbuf), __enxt)) { |
219 | case std::codecvt_base::ok: |
220 | break; |
221 | case std::codecvt_base::noconv: |
222 | __extbuf[0] = static_cast<char>(__last_consumed_); |
223 | __enxt = __extbuf + 1; |
224 | break; |
225 | case codecvt_base::partial: |
226 | case codecvt_base::error: |
227 | return traits_type::eof(); |
228 | } |
229 | while (__enxt > __extbuf) |
230 | if (ungetc(*--__enxt, __file_) == EOF) |
231 | return traits_type::eof(); |
232 | } |
233 | __last_consumed_ = __c; |
234 | __last_consumed_is_next_ = true; |
235 | return __c; |
236 | } |
237 | |
238 | // __stdoutbuf |
239 | |
240 | template <class _CharT> |
241 | class _LIBCPP_HIDDEN __stdoutbuf : public basic_streambuf<_CharT, char_traits<_CharT> > { |
242 | public: |
243 | typedef _CharT char_type; |
244 | typedef char_traits<char_type> traits_type; |
245 | typedef typename traits_type::int_type int_type; |
246 | typedef typename traits_type::pos_type pos_type; |
247 | typedef typename traits_type::off_type off_type; |
248 | typedef typename traits_type::state_type state_type; |
249 | |
250 | __stdoutbuf(FILE* __fp, state_type* __st); |
251 | |
252 | protected: |
253 | virtual int_type overflow(int_type __c = traits_type::eof()); |
254 | virtual streamsize xsputn(const char_type* __s, streamsize __n); |
255 | virtual int sync(); |
256 | virtual void imbue(const locale& __loc); |
257 | |
258 | private: |
259 | FILE* __file_; |
260 | const codecvt<char_type, char, state_type>* __cv_; |
261 | state_type* __st_; |
262 | bool __always_noconv_; |
263 | |
264 | #if defined(_LIBCPP_WIN32API) |
265 | static constexpr bool __is_win32api_wide_char = !is_same_v<_CharT, char>; |
266 | #else |
267 | static constexpr bool __is_win32api_wide_char = false; |
268 | #endif |
269 | |
270 | __stdoutbuf(const __stdoutbuf&); |
271 | __stdoutbuf& operator=(const __stdoutbuf&); |
272 | |
273 | _LIBCPP_EXPORTED_FROM_ABI friend FILE* __get_ostream_file(ostream&); |
274 | }; |
275 | |
276 | template <class _CharT> |
277 | __stdoutbuf<_CharT>::__stdoutbuf(FILE* __fp, state_type* __st) |
278 | : __file_(__fp), |
279 | __cv_(&use_facet<codecvt<char_type, char, state_type> >(this->getloc())), |
280 | __st_(__st), |
281 | __always_noconv_(__cv_->always_noconv()) { |
282 | // On Windows, in wchar_t mode, ignore the codecvt from the locale by |
283 | // default and assume noconv; this passes wchar_t through unmodified to |
284 | // fputwc, which handles it correctly depending on the actual mode of the |
285 | // output stream. If the user sets a custom locale with imbue(), that |
286 | // gets honored. |
287 | if constexpr (__is_win32api_wide_char) |
288 | __always_noconv_ = true; |
289 | } |
290 | |
291 | inline bool __do_fputc(char __c, FILE* __fp) { |
292 | if (fwrite(ptr: &__c, size: sizeof(__c), n: 1, s: __fp) != 1) |
293 | return false; |
294 | return true; |
295 | } |
296 | #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS |
297 | inline bool __do_fputc(wchar_t __c, FILE* __fp) { |
298 | // fputwc works regardless of wide/narrow mode of stdout, while |
299 | // fwrite of wchar_t only works if the stream actually has been set |
300 | // into wide mode. |
301 | if (fputwc(wc: __c, stream: __fp) == WEOF) |
302 | return false; |
303 | return true; |
304 | } |
305 | #endif |
306 | |
307 | template <class _CharT> |
308 | typename __stdoutbuf<_CharT>::int_type __stdoutbuf<_CharT>::overflow(int_type __c) { |
309 | char __extbuf[__limit]; |
310 | char_type __1buf; |
311 | if (!traits_type::eq_int_type(__c, traits_type::eof())) { |
312 | __1buf = traits_type::to_char_type(__c); |
313 | if (__always_noconv_) { |
314 | if (!__do_fputc(__1buf, __file_)) |
315 | return traits_type::eof(); |
316 | } else { |
317 | char* __extbe = __extbuf; |
318 | codecvt_base::result __r; |
319 | char_type* pbase = &__1buf; |
320 | char_type* pptr = pbase + 1; |
321 | do { |
322 | const char_type* __e; |
323 | __r = __cv_->out(*__st_, pbase, pptr, __e, __extbuf, __extbuf + sizeof(__extbuf), __extbe); |
324 | if (__e == pbase) |
325 | return traits_type::eof(); |
326 | if (__r == codecvt_base::noconv) { |
327 | if (fwrite(pbase, 1, 1, __file_) != 1) |
328 | return traits_type::eof(); |
329 | } else if (__r == codecvt_base::ok || __r == codecvt_base::partial) { |
330 | size_t __nmemb = static_cast<size_t>(__extbe - __extbuf); |
331 | if (fwrite(__extbuf, 1, __nmemb, __file_) != __nmemb) |
332 | return traits_type::eof(); |
333 | if (__r == codecvt_base::partial) { |
334 | pbase = const_cast<char_type*>(__e); |
335 | } |
336 | } else |
337 | return traits_type::eof(); |
338 | } while (__r == codecvt_base::partial); |
339 | } |
340 | } |
341 | return traits_type::not_eof(__c); |
342 | } |
343 | |
344 | template <class _CharT> |
345 | streamsize __stdoutbuf<_CharT>::xsputn(const char_type* __s, streamsize __n) { |
346 | // For wchar_t on Windows, don't call fwrite(), but write characters one |
347 | // at a time with fputwc(); that works both when stdout is in the default |
348 | // mode and when it is set to Unicode mode. |
349 | if (__always_noconv_ && !__is_win32api_wide_char) |
350 | return fwrite(__s, sizeof(char_type), __n, __file_); |
351 | streamsize __i = 0; |
352 | for (; __i < __n; ++__i, ++__s) |
353 | if (overflow(traits_type::to_int_type(*__s)) == traits_type::eof()) |
354 | break; |
355 | return __i; |
356 | } |
357 | |
358 | template <class _CharT> |
359 | int __stdoutbuf<_CharT>::sync() { |
360 | char __extbuf[__limit]; |
361 | codecvt_base::result __r; |
362 | do { |
363 | char* __extbe; |
364 | __r = __cv_->unshift(*__st_, __extbuf, __extbuf + sizeof(__extbuf), __extbe); |
365 | size_t __nmemb = static_cast<size_t>(__extbe - __extbuf); |
366 | if (fwrite(__extbuf, 1, __nmemb, __file_) != __nmemb) |
367 | return -1; |
368 | } while (__r == codecvt_base::partial); |
369 | if (__r == codecvt_base::error) |
370 | return -1; |
371 | if (fflush(__file_)) |
372 | return -1; |
373 | return 0; |
374 | } |
375 | |
376 | template <class _CharT> |
377 | void __stdoutbuf<_CharT>::imbue(const locale& __loc) { |
378 | sync(); |
379 | __cv_ = &use_facet<codecvt<char_type, char, state_type> >(__loc); |
380 | __always_noconv_ = __cv_->always_noconv(); |
381 | } |
382 | |
383 | _LIBCPP_END_NAMESPACE_STD |
384 | |
385 | _LIBCPP_POP_MACROS |
386 | |
387 | #endif // _LIBCPP_STD_STREAM_H |
388 | |