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