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