Home | History | Annotate | Line # | Download | only in bits
      1 // File based streams -*- C++ -*-
      2 
      3 // Copyright (C) 1997-2024 Free Software Foundation, Inc.
      4 //
      5 // This file is part of the GNU ISO C++ Library.  This library is free
      6 // software; you can redistribute it and/or modify it under the
      7 // terms of the GNU General Public License as published by the
      8 // Free Software Foundation; either version 3, or (at your option)
      9 // any later version.
     10 
     11 // This library is distributed in the hope that it will be useful,
     12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 // GNU General Public License for more details.
     15 
     16 // Under Section 7 of GPL version 3, you are granted additional
     17 // permissions described in the GCC Runtime Library Exception, version
     18 // 3.1, as published by the Free Software Foundation.
     19 
     20 // You should have received a copy of the GNU General Public License and
     21 // a copy of the GCC Runtime Library Exception along with this program;
     22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23 // <http://www.gnu.org/licenses/>.
     24 
     25 /** @file bits/fstream.tcc
     26  *  This is an internal header file, included by other library headers.
     27  *  Do not attempt to use it directly. @headername{fstream}
     28  */
     29 
     30 //
     31 // ISO C++ 14882: 27.8  File-based streams
     32 //
     33 
     34 #ifndef _FSTREAM_TCC
     35 #define _FSTREAM_TCC 1
     36 
     37 #pragma GCC system_header
     38 
     39 #include <bits/cxxabi_forced.h>
     40 #include <bits/move.h>   // for swap
     41 #include <cerrno>
     42 
     43 namespace std _GLIBCXX_VISIBILITY(default)
     44 {
     45 _GLIBCXX_BEGIN_NAMESPACE_VERSION
     46 
     47   template<typename _CharT, typename _Traits>
     48     void
     49     basic_filebuf<_CharT, _Traits>::
     50     _M_allocate_internal_buffer()
     51     {
     52       // Allocate internal buffer only if one doesn't already exist
     53       // (either allocated or provided by the user via setbuf).
     54       if (!_M_buf_allocated && !_M_buf)
     55 	{
     56 	  _M_buf = new char_type[_M_buf_size];
     57 	  _M_buf_allocated = true;
     58 	}
     59     }
     60 
     61   template<typename _CharT, typename _Traits>
     62     void
     63     basic_filebuf<_CharT, _Traits>::
     64     _M_destroy_internal_buffer() throw()
     65     {
     66       if (_M_buf_allocated)
     67 	{
     68 	  delete [] _M_buf;
     69 	  _M_buf = 0;
     70 	  _M_buf_allocated = false;
     71 	}
     72       delete [] _M_ext_buf;
     73       _M_ext_buf = 0;
     74       _M_ext_buf_size = 0;
     75       _M_ext_next = 0;
     76       _M_ext_end = 0;
     77     }
     78 
     79   template<typename _CharT, typename _Traits>
     80     basic_filebuf<_CharT, _Traits>::
     81     basic_filebuf() : __streambuf_type(), _M_lock(), _M_file(&_M_lock),
     82     _M_mode(ios_base::openmode(0)), _M_state_beg(), _M_state_cur(),
     83     _M_state_last(), _M_buf(0), _M_buf_size(_GLIBCXX_BUFSIZ),
     84     _M_buf_allocated(false), _M_reading(false), _M_writing(false), _M_pback(), 
     85     _M_pback_cur_save(0), _M_pback_end_save(0), _M_pback_init(false),
     86     _M_codecvt(0), _M_ext_buf(0), _M_ext_buf_size(0), _M_ext_next(0),
     87     _M_ext_end(0)
     88     {
     89       _M_codecvt = std::__try_use_facet<__codecvt_type>(this->_M_buf_locale);
     90     }
     91 
     92 #if __cplusplus >= 201103L
     93   template<typename _CharT, typename _Traits>
     94     basic_filebuf<_CharT, _Traits>::
     95     basic_filebuf(basic_filebuf&& __rhs)
     96     : __streambuf_type(__rhs),
     97     _M_lock(), _M_file(std::move(__rhs._M_file), &_M_lock),
     98     _M_mode(std::__exchange(__rhs._M_mode, ios_base::openmode(0))),
     99     _M_state_beg(std::move(__rhs._M_state_beg)),
    100     _M_state_cur(std::move(__rhs._M_state_cur)),
    101     _M_state_last(std::move(__rhs._M_state_last)),
    102     _M_buf(std::__exchange(__rhs._M_buf, nullptr)),
    103     _M_buf_size(std::__exchange(__rhs._M_buf_size, 1)),
    104     _M_buf_allocated(std::__exchange(__rhs._M_buf_allocated, false)),
    105     _M_reading(std::__exchange(__rhs._M_reading, false)),
    106     _M_writing(std::__exchange(__rhs._M_writing, false)),
    107     _M_pback(__rhs._M_pback),
    108     _M_pback_cur_save(std::__exchange(__rhs._M_pback_cur_save, nullptr)),
    109     _M_pback_end_save(std::__exchange(__rhs._M_pback_end_save, nullptr)),
    110     _M_pback_init(std::__exchange(__rhs._M_pback_init, false)),
    111     _M_codecvt(__rhs._M_codecvt),
    112     _M_ext_buf(std::__exchange(__rhs._M_ext_buf, nullptr)),
    113     _M_ext_buf_size(std::__exchange(__rhs._M_ext_buf_size, 0)),
    114     _M_ext_next(std::__exchange(__rhs._M_ext_next, nullptr)),
    115     _M_ext_end(std::__exchange(__rhs._M_ext_end, nullptr))
    116     {
    117       __rhs._M_set_buffer(-1);
    118       __rhs._M_state_last = __rhs._M_state_cur = __rhs._M_state_beg;
    119     }
    120 
    121   template<typename _CharT, typename _Traits>
    122     basic_filebuf<_CharT, _Traits>&
    123     basic_filebuf<_CharT, _Traits>::
    124     operator=(basic_filebuf&& __rhs)
    125     {
    126       this->close();
    127       __streambuf_type::operator=(__rhs);
    128       _M_file.swap(__rhs._M_file);
    129       _M_mode = std::__exchange(__rhs._M_mode, ios_base::openmode(0));
    130       _M_state_beg = std::move(__rhs._M_state_beg);
    131       _M_state_cur = std::move(__rhs._M_state_cur);
    132       _M_state_last = std::move(__rhs._M_state_last);
    133       _M_buf = std::__exchange(__rhs._M_buf, nullptr);
    134       _M_buf_size = std::__exchange(__rhs._M_buf_size, 1);
    135       _M_buf_allocated = std::__exchange(__rhs._M_buf_allocated, false);
    136       _M_ext_buf = std::__exchange(__rhs._M_ext_buf, nullptr);
    137       _M_ext_buf_size = std::__exchange(__rhs._M_ext_buf_size, 0);
    138       _M_ext_next = std::__exchange(__rhs._M_ext_next, nullptr);
    139       _M_ext_end = std::__exchange(__rhs._M_ext_end, nullptr);
    140       _M_reading = std::__exchange(__rhs._M_reading, false);
    141       _M_writing = std::__exchange(__rhs._M_writing, false);
    142       _M_pback_cur_save = std::__exchange(__rhs._M_pback_cur_save, nullptr);
    143       _M_pback_end_save = std::__exchange(__rhs._M_pback_end_save, nullptr);
    144       _M_pback_init = std::__exchange(__rhs._M_pback_init, false);
    145       __rhs._M_set_buffer(-1);
    146       __rhs._M_state_last = __rhs._M_state_cur = __rhs._M_state_beg;
    147       return *this;
    148     }
    149 
    150   template<typename _CharT, typename _Traits>
    151     void
    152     basic_filebuf<_CharT, _Traits>::
    153     swap(basic_filebuf& __rhs)
    154     {
    155       __streambuf_type::swap(__rhs);
    156       _M_file.swap(__rhs._M_file);
    157       std::swap(_M_mode, __rhs._M_mode);
    158       std::swap(_M_state_beg, __rhs._M_state_beg);
    159       std::swap(_M_state_cur, __rhs._M_state_cur);
    160       std::swap(_M_state_last, __rhs._M_state_last);
    161       std::swap(_M_buf, __rhs._M_buf);
    162       std::swap(_M_buf_size, __rhs._M_buf_size);
    163       std::swap(_M_buf_allocated, __rhs._M_buf_allocated);
    164       std::swap(_M_ext_buf, __rhs._M_ext_buf);
    165       std::swap(_M_ext_buf_size, __rhs._M_ext_buf_size);
    166       std::swap(_M_ext_next, __rhs._M_ext_next);
    167       std::swap(_M_ext_end, __rhs._M_ext_end);
    168       std::swap(_M_reading, __rhs._M_reading);
    169       std::swap(_M_writing, __rhs._M_writing);
    170       std::swap(_M_pback_cur_save, __rhs._M_pback_cur_save);
    171       std::swap(_M_pback_end_save, __rhs._M_pback_end_save);
    172       std::swap(_M_pback_init, __rhs._M_pback_init);
    173     }
    174 #endif
    175 
    176   template<typename _CharT, typename _Traits>
    177     typename basic_filebuf<_CharT, _Traits>::__filebuf_type*
    178     basic_filebuf<_CharT, _Traits>::
    179     open(const char* __s, ios_base::openmode __mode)
    180     {
    181       __filebuf_type *__ret = 0;
    182       if (!this->is_open())
    183 	{
    184 	  _M_file.open(__s, __mode);
    185 	  if (this->is_open())
    186 	    {
    187 	      _M_allocate_internal_buffer();
    188 	      _M_mode = __mode;
    189 
    190 	      // Setup initial buffer to 'uncommitted' mode.
    191 	      _M_reading = false;
    192 	      _M_writing = false;
    193 	      _M_set_buffer(-1);
    194 
    195 	      // Reset to initial state.
    196 	      _M_state_last = _M_state_cur = _M_state_beg;
    197 
    198 	      // 27.8.1.3,4
    199 	      if ((__mode & ios_base::ate)
    200 		  && this->seekoff(0, ios_base::end, __mode)
    201 		  == pos_type(off_type(-1)))
    202 		this->close();
    203 	      else
    204 		__ret = this;
    205 	    }
    206 	}
    207       return __ret;
    208     }
    209 
    210 #if _GLIBCXX_HAVE__WFOPEN && _GLIBCXX_USE_WCHAR_T
    211   template<typename _CharT, typename _Traits>
    212     basic_filebuf<_CharT, _Traits>*
    213     basic_filebuf<_CharT, _Traits>::
    214     open(const wchar_t* __s, ios_base::openmode __mode)
    215     {
    216       __filebuf_type *__ret = 0;
    217       if (!this->is_open())
    218 	{
    219 	  _M_file.open(__s, __mode);
    220 	  if (this->is_open())
    221 	    {
    222 	      _M_allocate_internal_buffer();
    223 	      _M_mode = __mode;
    224 
    225 	      // Setup initial buffer to 'uncommitted' mode.
    226 	      _M_reading = false;
    227 	      _M_writing = false;
    228 	      _M_set_buffer(-1);
    229 
    230 	      // Reset to initial state.
    231 	      _M_state_last = _M_state_cur = _M_state_beg;
    232 
    233 	      // 27.8.1.3,4
    234 	      if ((__mode & ios_base::ate)
    235 		  && this->seekoff(0, ios_base::end, __mode)
    236 		  == pos_type(off_type(-1)))
    237 		this->close();
    238 	      else
    239 		__ret = this;
    240 	    }
    241 	}
    242       return __ret;
    243     }
    244 #endif // HAVE__WFOPEN && USE_WCHAR_T
    245 
    246   template<typename _CharT, typename _Traits>
    247     typename basic_filebuf<_CharT, _Traits>::__filebuf_type*
    248     basic_filebuf<_CharT, _Traits>::
    249     close()
    250     {
    251       if (!this->is_open())
    252 	return 0;
    253 
    254       bool __testfail = false;
    255       {
    256 	// NB: Do this here so that re-opened filebufs will be cool...
    257 	struct __close_sentry
    258 	{
    259 	  basic_filebuf *__fb;
    260 	  __close_sentry (basic_filebuf *__fbi): __fb(__fbi) { }
    261 	  ~__close_sentry ()
    262 	  {
    263 	    __fb->_M_mode = ios_base::openmode(0);
    264 	    __fb->_M_pback_init = false;
    265 	    __fb->_M_destroy_internal_buffer();
    266 	    __fb->_M_reading = false;
    267 	    __fb->_M_writing = false;
    268 	    __fb->_M_set_buffer(-1);
    269 	    __fb->_M_state_last = __fb->_M_state_cur = __fb->_M_state_beg;
    270 	  }
    271 	} __cs (this);
    272 
    273 	__try
    274 	  {
    275 	    if (!_M_terminate_output())
    276 	      __testfail = true;
    277 	  }
    278 	__catch(...)
    279 	  {
    280 	    _M_file.close();
    281 	    __throw_exception_again;
    282 	  }
    283       }
    284 
    285       if (!_M_file.close())
    286 	__testfail = true;
    287 
    288       if (__testfail)
    289 	return 0;
    290       else
    291 	return this;
    292     }
    293 
    294   template<typename _CharT, typename _Traits>
    295     streamsize
    296     basic_filebuf<_CharT, _Traits>::
    297     showmanyc()
    298     {
    299       streamsize __ret = -1;
    300       const bool __testin = _M_mode & ios_base::in;
    301       if (__testin && this->is_open())
    302 	{
    303 	  // For a stateful encoding (-1) the pending sequence might be just
    304 	  // shift and unshift prefixes with no actual character.
    305 	  __ret = this->egptr() - this->gptr();
    306 
    307 #if _GLIBCXX_HAVE_DOS_BASED_FILESYSTEM
    308 	  // About this workaround, see libstdc++/20806.
    309 	  const bool __testbinary = _M_mode & ios_base::binary;
    310 	  if (__check_facet(_M_codecvt).encoding() >= 0
    311 	      && __testbinary)
    312 #else
    313 	  if (__check_facet(_M_codecvt).encoding() >= 0)
    314 #endif
    315 	    __ret += _M_file.showmanyc() / _M_codecvt->max_length();
    316 	}
    317       return __ret;
    318     }
    319 
    320   template<typename _CharT, typename _Traits>
    321     typename basic_filebuf<_CharT, _Traits>::int_type
    322     basic_filebuf<_CharT, _Traits>::
    323     underflow()
    324     {
    325       int_type __ret = traits_type::eof();
    326       const bool __testin = _M_mode & ios_base::in;
    327       if (__testin)
    328 	{
    329 	  if (_M_writing)
    330 	    {
    331 	      if (overflow() == traits_type::eof())
    332 		return __ret;
    333 	      _M_set_buffer(-1);
    334 	      _M_writing = false;
    335 	    }
    336 	  // Check for pback madness, and if so switch back to the
    337 	  // normal buffers and jet outta here before expensive
    338 	  // fileops happen...
    339 	  _M_destroy_pback();
    340 
    341 	  if (this->gptr() < this->egptr())
    342 	    return traits_type::to_int_type(*this->gptr());
    343 
    344 	  // Get and convert input sequence.
    345 	  const size_t __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1;
    346 
    347 	  // Will be set to true if ::read() returns 0 indicating EOF.
    348 	  bool __got_eof = false;
    349 	  // Number of internal characters produced.
    350 	  streamsize __ilen = 0;
    351 	  codecvt_base::result __r = codecvt_base::ok;
    352 	  if (__check_facet(_M_codecvt).always_noconv())
    353 	    {
    354 	      __ilen = _M_file.xsgetn(reinterpret_cast<char*>(this->eback()),
    355 				      __buflen);
    356 	      if (__ilen == 0)
    357 		__got_eof = true;
    358 	    }
    359 	  else
    360 	    {
    361               // Worst-case number of external bytes.
    362 	      // XXX Not done encoding() == -1.
    363 	      const int __enc = _M_codecvt->encoding();
    364 	      streamsize __blen; // Minimum buffer size.
    365 	      streamsize __rlen; // Number of chars to read.
    366 	      if (__enc > 0)
    367 		__blen = __rlen = __buflen * __enc;
    368 	      else
    369 		{
    370 		  __blen = __buflen + _M_codecvt->max_length() - 1;
    371 		  __rlen = __buflen;
    372 		}
    373 	      const streamsize __remainder = _M_ext_end - _M_ext_next;
    374 	      __rlen = __rlen > __remainder ? __rlen - __remainder : 0;
    375 
    376 	      // An imbue in 'read' mode implies first converting the external
    377 	      // chars already present.
    378 	      if (_M_reading && this->egptr() == this->eback() && __remainder)
    379 		__rlen = 0;
    380 
    381 	      // Allocate buffer if necessary and move unconverted
    382 	      // bytes to front.
    383 	      if (_M_ext_buf_size < __blen)
    384 		{
    385 		  char* __buf = new char[__blen];
    386 		  if (__remainder)
    387 		    __builtin_memcpy(__buf, _M_ext_next, __remainder);
    388 
    389 		  delete [] _M_ext_buf;
    390 		  _M_ext_buf = __buf;
    391 		  _M_ext_buf_size = __blen;
    392 		}
    393 	      else if (__remainder)
    394 		__builtin_memmove(_M_ext_buf, _M_ext_next, __remainder);
    395 
    396 	      _M_ext_next = _M_ext_buf;
    397 	      _M_ext_end = _M_ext_buf + __remainder;
    398 	      _M_state_last = _M_state_cur;
    399 
    400 	      do
    401 		{
    402 		  if (__rlen > 0)
    403 		    {
    404 		      // Sanity check!
    405 		      // This may fail if the return value of
    406 		      // codecvt::max_length() is bogus.
    407 		      if (_M_ext_end - _M_ext_buf + __rlen > _M_ext_buf_size)
    408 			{
    409 			  __throw_ios_failure(__N("basic_filebuf::underflow "
    410 					      "codecvt::max_length() "
    411 					      "is not valid"));
    412 			}
    413 		      streamsize __elen = _M_file.xsgetn(_M_ext_end, __rlen);
    414 		      if (__elen == 0)
    415 			__got_eof = true;
    416 		      else if (__elen == -1)
    417 			break;
    418 		      _M_ext_end += __elen;
    419 		    }
    420 
    421 		  char_type* __iend = this->eback();
    422 		  if (_M_ext_next < _M_ext_end)
    423 		    __r = _M_codecvt->in(_M_state_cur, _M_ext_next,
    424 					 _M_ext_end, _M_ext_next,
    425 					 this->eback(),
    426 					 this->eback() + __buflen, __iend);
    427 		  if (__r == codecvt_base::noconv)
    428 		    {
    429 		      size_t __avail = _M_ext_end - _M_ext_buf;
    430 		      __ilen = std::min(__avail, __buflen);
    431 		      traits_type::copy(this->eback(),
    432 					reinterpret_cast<char_type*>
    433 					(_M_ext_buf), __ilen);
    434 		      _M_ext_next = _M_ext_buf + __ilen;
    435 		    }
    436 		  else
    437 		    __ilen = __iend - this->eback();
    438 
    439 		  // _M_codecvt->in may return error while __ilen > 0: this is
    440 		  // ok, and actually occurs in case of mixed encodings (e.g.,
    441 		  // XML files).
    442 		  if (__r == codecvt_base::error)
    443 		    break;
    444 
    445 		  __rlen = 1;
    446 		}
    447 	      while (__ilen == 0 && !__got_eof);
    448 	    }
    449 
    450 	  if (__ilen > 0)
    451 	    {
    452 	      _M_set_buffer(__ilen);
    453 	      _M_reading = true;
    454 	      __ret = traits_type::to_int_type(*this->gptr());
    455 	    }
    456 	  else if (__got_eof)
    457 	    {
    458 	      // If the actual end of file is reached, set 'uncommitted'
    459 	      // mode, thus allowing an immediate write without an
    460 	      // intervening seek.
    461 	      _M_set_buffer(-1);
    462 	      _M_reading = false;
    463 	      // However, reaching it while looping on partial means that
    464 	      // the file has got an incomplete character.
    465 	      if (__r == codecvt_base::partial)
    466 		__throw_ios_failure(__N("basic_filebuf::underflow "
    467 				    "incomplete character in file"));
    468 	    }
    469 	  else if (__r == codecvt_base::error)
    470 	    __throw_ios_failure(__N("basic_filebuf::underflow "
    471 				"invalid byte sequence in file"));
    472 	  else
    473 	    __throw_ios_failure(__N("basic_filebuf::underflow "
    474 				"error reading the file"), errno);
    475 	}
    476       return __ret;
    477     }
    478 
    479   template<typename _CharT, typename _Traits>
    480     typename basic_filebuf<_CharT, _Traits>::int_type
    481     basic_filebuf<_CharT, _Traits>::
    482     pbackfail(int_type __i)
    483     {
    484       int_type __ret = traits_type::eof();
    485       const bool __testin = _M_mode & ios_base::in;
    486       if (__testin)
    487 	{
    488 	  if (_M_writing)
    489 	    {
    490 	      if (overflow() == traits_type::eof())
    491 		return __ret;
    492 	      _M_set_buffer(-1);
    493 	      _M_writing = false;
    494 	    }
    495 	  // Remember whether the pback buffer is active, otherwise below
    496 	  // we may try to store in it a second char (libstdc++/9761).
    497 	  const bool __testpb = _M_pback_init;
    498 	  const bool __testeof = traits_type::eq_int_type(__i, __ret);
    499 	  int_type __tmp;
    500 	  if (this->eback() < this->gptr())
    501 	    {
    502 	      this->gbump(-1);
    503 	      __tmp = traits_type::to_int_type(*this->gptr());
    504 	    }
    505 	  else if (this->seekoff(-1, ios_base::cur) != pos_type(off_type(-1)))
    506 	    {
    507 	      __tmp = this->underflow();
    508 	      if (traits_type::eq_int_type(__tmp, __ret))
    509 		return __ret;
    510 	    }
    511 	  else
    512 	    {
    513 	      // At the beginning of the buffer, need to make a
    514 	      // putback position available.  But the seek may fail
    515 	      // (f.i., at the beginning of a file, see
    516 	      // libstdc++/9439) and in that case we return
    517 	      // traits_type::eof().
    518 	      return __ret;
    519 	    }
    520 
    521 	  // Try to put back __i into input sequence in one of three ways.
    522 	  // Order these tests done in is unspecified by the standard.
    523 	  if (!__testeof && traits_type::eq_int_type(__i, __tmp))
    524 	    __ret = __i;
    525 	  else if (__testeof)
    526 	    __ret = traits_type::not_eof(__i);
    527 	  else if (!__testpb)
    528 	    {
    529 	      _M_create_pback();
    530 	      _M_reading = true;
    531 	      *this->gptr() = traits_type::to_char_type(__i);
    532 	      __ret = __i;
    533 	    }
    534 	}
    535       return __ret;
    536     }
    537 
    538   template<typename _CharT, typename _Traits>
    539     typename basic_filebuf<_CharT, _Traits>::int_type
    540     basic_filebuf<_CharT, _Traits>::
    541     overflow(int_type __c)
    542     {
    543       int_type __ret = traits_type::eof();
    544       const bool __testeof = traits_type::eq_int_type(__c, __ret);
    545       const bool __testout = (_M_mode & ios_base::out
    546 			      || _M_mode & ios_base::app);
    547       if (__testout)
    548 	{
    549           if (_M_reading)
    550             {
    551               _M_destroy_pback();
    552               const int __gptr_off = _M_get_ext_pos(_M_state_last);
    553               if (_M_seek(__gptr_off, ios_base::cur, _M_state_last)
    554                   == pos_type(off_type(-1)))
    555                 return __ret;
    556             }
    557 	  if (this->pbase() < this->pptr())
    558 	    {
    559 	      // If appropriate, append the overflow char.
    560 	      if (!__testeof)
    561 		{
    562 		  *this->pptr() = traits_type::to_char_type(__c);
    563 		  this->pbump(1);
    564 		}
    565 
    566 	      // Convert pending sequence to external representation,
    567 	      // and output.
    568 	      if (_M_convert_to_external(this->pbase(),
    569 					 this->pptr() - this->pbase()))
    570 		{
    571 		  _M_set_buffer(0);
    572 		  __ret = traits_type::not_eof(__c);
    573 		}
    574 	    }
    575 	  else if (_M_buf_size > 1)
    576 	    {
    577 	      // Overflow in 'uncommitted' mode: set _M_writing, set
    578 	      // the buffer to the initial 'write' mode, and put __c
    579 	      // into the buffer.
    580 	      _M_set_buffer(0);
    581 	      _M_writing = true;
    582 	      if (!__testeof)
    583 		{
    584 		  *this->pptr() = traits_type::to_char_type(__c);
    585 		  this->pbump(1);
    586 		}
    587 	      __ret = traits_type::not_eof(__c);
    588 	    }
    589 	  else
    590 	    {
    591 	      // Unbuffered.
    592 	      char_type __conv = traits_type::to_char_type(__c);
    593 	      if (__testeof || _M_convert_to_external(&__conv, 1))
    594 		{
    595 		  _M_writing = true;
    596 		  __ret = traits_type::not_eof(__c);
    597 		}
    598 	    }
    599 	}
    600       return __ret;
    601     }
    602 
    603   template<typename _CharT, typename _Traits>
    604     bool
    605     basic_filebuf<_CharT, _Traits>::
    606     _M_convert_to_external(_CharT* __ibuf, streamsize __ilen)
    607     {
    608       // Sizes of external and pending output.
    609       streamsize __elen;
    610       streamsize __plen;
    611       if (__check_facet(_M_codecvt).always_noconv())
    612 	{
    613 	  __elen = _M_file.xsputn(reinterpret_cast<char*>(__ibuf), __ilen);
    614 	  __plen = __ilen;
    615 	}
    616       else
    617 	{
    618 	  // Worst-case number of external bytes needed.
    619 	  // XXX Not done encoding() == -1.
    620 	  streamsize __blen = __ilen * _M_codecvt->max_length();
    621 	  char* __buf = static_cast<char*>(__builtin_alloca(__blen));
    622 
    623 	  char* __bend;
    624 	  const char_type* __iend;
    625 	  codecvt_base::result __r;
    626 	  __r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen,
    627 				__iend, __buf, __buf + __blen, __bend);
    628 
    629 	  if (__r == codecvt_base::ok || __r == codecvt_base::partial)
    630 	    __blen = __bend - __buf;
    631 	  else if (__r == codecvt_base::noconv)
    632 	    {
    633 	      // Same as the always_noconv case above.
    634 	      __buf = reinterpret_cast<char*>(__ibuf);
    635 	      __blen = __ilen;
    636 	    }
    637 	  else
    638 	    __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external "
    639 				    "conversion error"));
    640   
    641 	  __elen = _M_file.xsputn(__buf, __blen);
    642 	  __plen = __blen;
    643 
    644 	  // Try once more for partial conversions.
    645 	  if (__r == codecvt_base::partial && __elen == __plen)
    646 	    {
    647 	      const char_type* __iresume = __iend;
    648 	      streamsize __rlen = this->pptr() - __iend;
    649 	      __r = _M_codecvt->out(_M_state_cur, __iresume,
    650 				    __iresume + __rlen, __iend, __buf,
    651 				    __buf + __blen, __bend);
    652 	      if (__r != codecvt_base::error)
    653 		{
    654 		  __rlen = __bend - __buf;
    655 		  __elen = _M_file.xsputn(__buf, __rlen);
    656 		  __plen = __rlen;
    657 		}
    658 	      else
    659 		__throw_ios_failure(__N("basic_filebuf::_M_convert_to_external "
    660 					"conversion error"));
    661 	    }
    662 	}
    663       return __elen == __plen;
    664     }
    665 
    666   template<typename _CharT, typename _Traits>
    667     streamsize
    668     basic_filebuf<_CharT, _Traits>::
    669     xsgetn(_CharT* __s, streamsize __n)
    670     {
    671       // Clear out pback buffer before going on to the real deal...
    672       streamsize __ret = 0;
    673       if (_M_pback_init)
    674 	{
    675 	  if (__n > 0 && this->gptr() == this->eback())
    676 	    {
    677 	      *__s++ = *this->gptr(); // emulate non-underflowing sbumpc
    678 	      this->gbump(1);
    679 	      __ret = 1;
    680 	      --__n;
    681 	    }
    682 	  _M_destroy_pback();
    683 	}
    684       else if (_M_writing)
    685 	{
    686 	  if (overflow() == traits_type::eof())
    687 	    return __ret;
    688 	  _M_set_buffer(-1);
    689 	  _M_writing = false;
    690 	}
    691  
    692       // Optimization in the always_noconv() case, to be generalized in the
    693       // future: when __n > __buflen we read directly instead of using the
    694       // buffer repeatedly.
    695       const bool __testin = _M_mode & ios_base::in;
    696       const streamsize __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1;
    697  
    698       if (__n > __buflen && __check_facet(_M_codecvt).always_noconv()
    699 	  && __testin)
    700 	{
    701 	  // First, copy the chars already present in the buffer.
    702 	  const streamsize __avail = this->egptr() - this->gptr();
    703 	  if (__avail != 0)
    704 	    {
    705 	      traits_type::copy(__s, this->gptr(), __avail);
    706 	      __s += __avail;
    707 	      this->setg(this->eback(), this->gptr() + __avail, this->egptr());
    708 	      __ret += __avail;
    709 	      __n -= __avail;
    710 	    }
    711  
    712 	  // Need to loop in case of short reads (relatively common
    713 	  // with pipes).
    714 	  streamsize __len;
    715 	  for (;;)
    716 	    {
    717 	      __len = _M_file.xsgetn(reinterpret_cast<char*>(__s), __n);
    718 	      if (__len == -1)
    719 		__throw_ios_failure(__N("basic_filebuf::xsgetn "
    720 					"error reading the file"), errno);
    721 	      if (__len == 0)
    722 		break;
    723  
    724 	      __n -= __len;
    725 	      __ret += __len;
    726 	      if (__n == 0)
    727 		break;
    728 
    729 	      __s += __len;
    730 	    }
    731 
    732 	  if (__n == 0)
    733 	    {
    734 	      // Set _M_reading. Buffer is already in initial 'read' mode.
    735 	      _M_reading = true;
    736 	    }
    737 	  else if (__len == 0)
    738 	    {
    739 	      // If end of file is reached, set 'uncommitted'
    740 	      // mode, thus allowing an immediate write without
    741 	      // an intervening seek.
    742 	      _M_set_buffer(-1);
    743 	      _M_reading = false;
    744 	    }
    745 	}
    746       else
    747 	__ret += __streambuf_type::xsgetn(__s, __n);
    748  
    749       return __ret;
    750     }
    751 
    752   template<typename _CharT, typename _Traits>
    753     streamsize
    754     basic_filebuf<_CharT, _Traits>::
    755     xsputn(const _CharT* __s, streamsize __n)
    756     {
    757       streamsize __ret = 0;
    758       // Optimization in the always_noconv() case, to be generalized in the
    759       // future: when __n is larger than the available capacity we write
    760       // directly instead of using the buffer.
    761       const bool __testout = (_M_mode & ios_base::out
    762 			      || _M_mode & ios_base::app);
    763       if (__check_facet(_M_codecvt).always_noconv()
    764 	  && __testout && !_M_reading)
    765 	{
    766 	  streamsize __bufavail = this->epptr() - this->pptr();
    767 
    768 	  // Don't mistake 'uncommitted' mode buffered with unbuffered.
    769 	  if (!_M_writing && _M_buf_size > 1)
    770 	    __bufavail = _M_buf_size - 1;
    771 
    772 	  if (__n >= __bufavail)
    773 	    {
    774 	      const streamsize __buffill = this->pptr() - this->pbase();
    775 	      const char* __buf = reinterpret_cast<const char*>(this->pbase());
    776 	      __ret = _M_file.xsputn_2(__buf, __buffill,
    777 				       reinterpret_cast<const char*>(__s),
    778 				       __n);
    779 	      if (__ret == __buffill + __n)
    780 		{
    781 		  _M_set_buffer(0);
    782 		  _M_writing = true;
    783 		}
    784 	      if (__ret > __buffill)
    785 		__ret -= __buffill;
    786 	      else
    787 		__ret = 0;
    788 	    }
    789 	  else
    790 	    __ret = __streambuf_type::xsputn(__s, __n);
    791 	}
    792        else
    793 	 __ret = __streambuf_type::xsputn(__s, __n);
    794        return __ret;
    795     }
    796 
    797   template<typename _CharT, typename _Traits>
    798     typename basic_filebuf<_CharT, _Traits>::__streambuf_type*
    799     basic_filebuf<_CharT, _Traits>::
    800     setbuf(char_type* __s, streamsize __n)
    801     {
    802       if (!this->is_open())
    803 	{
    804 	  if (__s == 0 && __n == 0)
    805 	    _M_buf_size = 1;
    806 	  else if (__s && __n > 0)
    807 	    {
    808 	      // This is implementation-defined behavior, and assumes that
    809 	      // an external char_type array of length __n exists and has
    810 	      // been pre-allocated. If this is not the case, things will
    811 	      // quickly blow up. When __n > 1, __n - 1 positions will be
    812 	      // used for the get area, __n - 1 for the put area and 1
    813 	      // position to host the overflow char of a full put area.
    814 	      // When __n == 1, 1 position will be used for the get area
    815 	      // and 0 for the put area, as in the unbuffered case above.
    816 	      _M_buf = __s;
    817 	      _M_buf_size = __n;
    818 	    }
    819 	}
    820       return this;
    821     }
    822 
    823 
    824   // According to 27.8.1.4 p11 - 13, seekoff should ignore the last
    825   // argument (of type openmode).
    826   template<typename _CharT, typename _Traits>
    827     typename basic_filebuf<_CharT, _Traits>::pos_type
    828     basic_filebuf<_CharT, _Traits>::
    829     seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode)
    830     {
    831       int __width = 0;
    832       if (_M_codecvt)
    833 	__width = _M_codecvt->encoding();
    834       if (__width < 0)
    835 	__width = 0;
    836 
    837       pos_type __ret = pos_type(off_type(-1));
    838       const bool __testfail = __off != 0 && __width <= 0;
    839       if (this->is_open() && !__testfail)
    840 	{
    841 	  // tellg and tellp queries do not affect any state, unless
    842 	  // ! always_noconv and the put sequence is not empty.
    843 	  // In that case, determining the position requires converting the
    844 	  // put sequence. That doesn't use ext_buf, so requires a flush.
    845 	  bool __no_movement = __way == ios_base::cur && __off == 0
    846 	    && (!_M_writing || _M_codecvt->always_noconv());
    847 
    848 	  // Ditch any pback buffers to avoid confusion.
    849 	  if (!__no_movement)
    850 	    _M_destroy_pback();
    851 
    852 	  // Correct state at destination. Note that this is the correct
    853 	  // state for the current position during output, because
    854 	  // codecvt::unshift() returns the state to the initial state.
    855 	  // This is also the correct state at the end of the file because
    856 	  // an unshift sequence should have been written at the end.
    857 	  __state_type __state = _M_state_beg;
    858 	  off_type __computed_off = __off * __width;
    859 	  if (_M_reading && __way == ios_base::cur)
    860 	    {
    861 	      __state = _M_state_last;
    862 	      __computed_off += _M_get_ext_pos(__state);
    863 	    }
    864 	  if (!__no_movement)
    865 	    __ret = _M_seek(__computed_off, __way, __state);
    866 	  else
    867 	    {
    868 	      if (_M_writing)
    869 		__computed_off = this->pptr() - this->pbase();
    870 	      
    871 	      off_type __file_off = _M_file.seekoff(0, ios_base::cur);
    872 	      if (__file_off != off_type(-1))
    873 		{
    874 		  __ret = __file_off + __computed_off;
    875 		  __ret.state(__state);
    876 		}
    877 	    }
    878 	}
    879       return __ret;
    880     }
    881 
    882   // _GLIBCXX_RESOLVE_LIB_DEFECTS
    883   // 171. Strange seekpos() semantics due to joint position
    884   // According to the resolution of DR 171, seekpos should ignore the last
    885   // argument (of type openmode).
    886   template<typename _CharT, typename _Traits>
    887     typename basic_filebuf<_CharT, _Traits>::pos_type
    888     basic_filebuf<_CharT, _Traits>::
    889     seekpos(pos_type __pos, ios_base::openmode)
    890     {
    891       pos_type __ret =  pos_type(off_type(-1));
    892       if (this->is_open())
    893 	{
    894 	  // Ditch any pback buffers to avoid confusion.
    895 	  _M_destroy_pback();
    896 	  __ret = _M_seek(off_type(__pos), ios_base::beg, __pos.state());
    897 	}
    898       return __ret;
    899     }
    900 
    901   template<typename _CharT, typename _Traits>
    902     typename basic_filebuf<_CharT, _Traits>::pos_type
    903     basic_filebuf<_CharT, _Traits>::
    904     _M_seek(off_type __off, ios_base::seekdir __way, __state_type __state)
    905     {
    906       pos_type __ret = pos_type(off_type(-1));
    907       if (_M_terminate_output())
    908 	{
    909 	  off_type __file_off = _M_file.seekoff(__off, __way);
    910 	  if (__file_off != off_type(-1))
    911 	    {
    912 	      _M_reading = false;
    913 	      _M_writing = false;
    914 	      _M_ext_next = _M_ext_end = _M_ext_buf;
    915 	      _M_set_buffer(-1);
    916 	      _M_state_cur = __state;
    917 	      __ret = __file_off;
    918 	      __ret.state(_M_state_cur);
    919 	    }
    920 	}
    921       return __ret;
    922     }
    923 
    924   // Returns the distance from the end of the ext buffer to the point
    925   // corresponding to gptr(). This is a negative value. Updates __state
    926   // from eback() correspondence to gptr().
    927   template<typename _CharT, typename _Traits>
    928     int basic_filebuf<_CharT, _Traits>::
    929     _M_get_ext_pos(__state_type& __state)
    930     {
    931       if (_M_codecvt->always_noconv())
    932         return this->gptr() - this->egptr();
    933       else
    934         {
    935           // Calculate offset from _M_ext_buf that corresponds to
    936           // gptr(). Precondition: __state == _M_state_last, which
    937           // corresponds to eback().
    938           const int __gptr_off =
    939             _M_codecvt->length(__state, _M_ext_buf, _M_ext_next,
    940                                this->gptr() - this->eback());
    941           return _M_ext_buf + __gptr_off - _M_ext_end;
    942         }
    943     }
    944     
    945   template<typename _CharT, typename _Traits>
    946     bool
    947     basic_filebuf<_CharT, _Traits>::
    948     _M_terminate_output()
    949     {
    950       // Part one: update the output sequence.
    951       bool __testvalid = true;
    952       if (this->pbase() < this->pptr())
    953 	{
    954 	  const int_type __tmp = this->overflow();
    955 	  if (traits_type::eq_int_type(__tmp, traits_type::eof()))
    956 	    __testvalid = false;
    957 	}
    958 
    959       // Part two: output unshift sequence.
    960       if (_M_writing && !__check_facet(_M_codecvt).always_noconv()
    961 	  && __testvalid)
    962 	{
    963 	  // Note: this value is arbitrary, since there is no way to
    964 	  // get the length of the unshift sequence from codecvt,
    965 	  // without calling unshift.
    966 	  const size_t __blen = 128;
    967 	  char __buf[__blen];
    968 	  codecvt_base::result __r;
    969 	  streamsize __ilen = 0;
    970 
    971 	  do
    972 	    {
    973 	      char* __next;
    974 	      __r = _M_codecvt->unshift(_M_state_cur, __buf,
    975 					__buf + __blen, __next);
    976 	      if (__r == codecvt_base::error)
    977 		__testvalid = false;
    978 	      else if (__r == codecvt_base::ok ||
    979 		       __r == codecvt_base::partial)
    980 		{
    981 		  __ilen = __next - __buf;
    982 		  if (__ilen > 0)
    983 		    {
    984 		      const streamsize __elen = _M_file.xsputn(__buf, __ilen);
    985 		      if (__elen != __ilen)
    986 			__testvalid = false;
    987 		    }
    988 		}
    989 	    }
    990 	  while (__r == codecvt_base::partial && __ilen > 0 && __testvalid);
    991 
    992 	  if (__testvalid)
    993 	    {
    994 	      // This second call to overflow() is required by the standard,
    995 	      // but it's not clear why it's needed, since the output buffer
    996 	      // should be empty by this point (it should have been emptied
    997 	      // in the first call to overflow()).
    998 	      const int_type __tmp = this->overflow();
    999 	      if (traits_type::eq_int_type(__tmp, traits_type::eof()))
   1000 		__testvalid = false;
   1001 	    }
   1002 	}
   1003       return __testvalid;
   1004     }
   1005 
   1006   template<typename _CharT, typename _Traits>
   1007     int
   1008     basic_filebuf<_CharT, _Traits>::
   1009     sync()
   1010     {
   1011       // Make sure that the internal buffer resyncs its idea of
   1012       // the file position with the external file.
   1013       int __ret = 0;
   1014       if (this->pbase() < this->pptr())
   1015 	{
   1016 	  const int_type __tmp = this->overflow();
   1017 	  if (traits_type::eq_int_type(__tmp, traits_type::eof()))
   1018 	    __ret = -1;
   1019 	}
   1020       return __ret;
   1021     }
   1022 
   1023   template<typename _CharT, typename _Traits>
   1024     void
   1025     basic_filebuf<_CharT, _Traits>::
   1026     imbue(const locale& __loc)
   1027     {
   1028       bool __testvalid = true;
   1029 
   1030       const __codecvt_type* const _M_codecvt_tmp
   1031 	= __try_use_facet<__codecvt_type>(__loc);
   1032 
   1033       if (this->is_open())
   1034 	{
   1035 	  // encoding() == -1 is ok only at the beginning.
   1036 	  if ((_M_reading || _M_writing)
   1037 	      && __check_facet(_M_codecvt).encoding() == -1)
   1038 	    __testvalid = false;
   1039 	  else
   1040 	    {
   1041 	      if (_M_reading)
   1042 		{
   1043 		  if (__check_facet(_M_codecvt).always_noconv())
   1044 		    {
   1045 		      if (_M_codecvt_tmp
   1046 			  && !__check_facet(_M_codecvt_tmp).always_noconv())
   1047 			__testvalid = this->seekoff(0, ios_base::cur, _M_mode)
   1048 			              != pos_type(off_type(-1));
   1049 		    }
   1050 		  else
   1051 		    {
   1052 		      // External position corresponding to gptr().
   1053 		      _M_ext_next = _M_ext_buf
   1054 			+ _M_codecvt->length(_M_state_last, _M_ext_buf,
   1055 					     _M_ext_next,
   1056 					     this->gptr() - this->eback());
   1057 		      const streamsize __remainder = _M_ext_end - _M_ext_next;
   1058 		      if (__remainder)
   1059 			__builtin_memmove(_M_ext_buf, _M_ext_next, __remainder);
   1060 
   1061 		      _M_ext_next = _M_ext_buf;
   1062 		      _M_ext_end = _M_ext_buf + __remainder;
   1063 		      _M_set_buffer(-1);
   1064 		      _M_state_last = _M_state_cur = _M_state_beg;
   1065 		    }
   1066 		}
   1067 	      else if (_M_writing && (__testvalid = _M_terminate_output()))
   1068 		_M_set_buffer(-1);
   1069 	    }
   1070 	}
   1071 
   1072       if (__testvalid)
   1073 	_M_codecvt = _M_codecvt_tmp;
   1074       else
   1075 	_M_codecvt = 0;
   1076     }
   1077 
   1078   // Inhibit implicit instantiations for required instantiations,
   1079   // which are defined via explicit instantiations elsewhere.
   1080 #if _GLIBCXX_EXTERN_TEMPLATE
   1081   extern template class basic_filebuf<char>;
   1082   extern template class basic_ifstream<char>;
   1083   extern template class basic_ofstream<char>;
   1084   extern template class basic_fstream<char>;
   1085 
   1086 #ifdef _GLIBCXX_USE_WCHAR_T
   1087   extern template class basic_filebuf<wchar_t>;
   1088   extern template class basic_ifstream<wchar_t>;
   1089   extern template class basic_ofstream<wchar_t>;
   1090   extern template class basic_fstream<wchar_t>;
   1091 #endif
   1092 #endif
   1093 
   1094 _GLIBCXX_END_NAMESPACE_VERSION
   1095 } // namespace std
   1096 
   1097 #endif
   1098