xref: /freebsd/contrib/llvm-project/libcxx/src/std_stream.h (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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(__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(__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, __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(__c, __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(&__c, sizeof(__c), 1, __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(__c, __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