| // File based streams -*- C++ -*- |
| |
| // Copyright (C) 1997-2013 Free Software Foundation, Inc. |
| // |
| // This file is part of the GNU ISO C++ Library. This library is free |
| // software; you can redistribute it and/or modify it under the |
| // terms of the GNU General Public License as published by the |
| // Free Software Foundation; either version 3, or (at your option) |
| // any later version. |
| |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| // Under Section 7 of GPL version 3, you are granted additional |
| // permissions described in the GCC Runtime Library Exception, version |
| // 3.1, as published by the Free Software Foundation. |
| |
| // You should have received a copy of the GNU General Public License and |
| // a copy of the GCC Runtime Library Exception along with this program; |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| // <http://www.gnu.org/licenses/>. |
| |
| /** @file bits/fstream.tcc |
| * This is an internal header file, included by other library headers. |
| * Do not attempt to use it directly. @headername{fstream} |
| */ |
| |
| // |
| // ISO C++ 14882: 27.8 File-based streams |
| // |
| |
| #ifndef _FSTREAM_TCC |
| #define _FSTREAM_TCC 1 |
| |
| #pragma GCC system_header |
| |
| #include <bits/cxxabi_forced.h> |
| |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| |
| template<typename _CharT, typename _Traits> |
| void |
| basic_filebuf<_CharT, _Traits>:: |
| _M_allocate_internal_buffer() |
| { |
| // Allocate internal buffer only if one doesn't already exist |
| // (either allocated or provided by the user via setbuf). |
| if (!_M_buf_allocated && !_M_buf) |
| { |
| _M_buf = new char_type[_M_buf_size]; |
| _M_buf_allocated = true; |
| } |
| } |
| |
| template<typename _CharT, typename _Traits> |
| void |
| basic_filebuf<_CharT, _Traits>:: |
| _M_destroy_internal_buffer() throw() |
| { |
| if (_M_buf_allocated) |
| { |
| delete [] _M_buf; |
| _M_buf = 0; |
| _M_buf_allocated = false; |
| } |
| delete [] _M_ext_buf; |
| _M_ext_buf = 0; |
| _M_ext_buf_size = 0; |
| _M_ext_next = 0; |
| _M_ext_end = 0; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| basic_filebuf<_CharT, _Traits>:: |
| basic_filebuf() : __streambuf_type(), _M_lock(), _M_file(&_M_lock), |
| _M_mode(ios_base::openmode(0)), _M_state_beg(), _M_state_cur(), |
| _M_state_last(), _M_buf(0), _M_buf_size(BUFSIZ), |
| _M_buf_allocated(false), _M_reading(false), _M_writing(false), _M_pback(), |
| _M_pback_cur_save(0), _M_pback_end_save(0), _M_pback_init(false), |
| _M_codecvt(0), _M_ext_buf(0), _M_ext_buf_size(0), _M_ext_next(0), |
| _M_ext_end(0) |
| { |
| if (has_facet<__codecvt_type>(this->_M_buf_locale)) |
| _M_codecvt = &use_facet<__codecvt_type>(this->_M_buf_locale); |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::__filebuf_type* |
| basic_filebuf<_CharT, _Traits>:: |
| open(const char* __s, ios_base::openmode __mode) |
| { |
| __filebuf_type *__ret = 0; |
| if (!this->is_open()) |
| { |
| _M_file.open(__s, __mode); |
| if (this->is_open()) |
| { |
| _M_allocate_internal_buffer(); |
| _M_mode = __mode; |
| |
| // Setup initial buffer to 'uncommitted' mode. |
| _M_reading = false; |
| _M_writing = false; |
| _M_set_buffer(-1); |
| |
| // Reset to initial state. |
| _M_state_last = _M_state_cur = _M_state_beg; |
| |
| // 27.8.1.3,4 |
| if ((__mode & ios_base::ate) |
| && this->seekoff(0, ios_base::end, __mode) |
| == pos_type(off_type(-1))) |
| this->close(); |
| else |
| __ret = this; |
| } |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::__filebuf_type* |
| basic_filebuf<_CharT, _Traits>:: |
| close() |
| { |
| if (!this->is_open()) |
| return 0; |
| |
| bool __testfail = false; |
| { |
| // NB: Do this here so that re-opened filebufs will be cool... |
| struct __close_sentry |
| { |
| basic_filebuf *__fb; |
| __close_sentry (basic_filebuf *__fbi): __fb(__fbi) { } |
| ~__close_sentry () |
| { |
| __fb->_M_mode = ios_base::openmode(0); |
| __fb->_M_pback_init = false; |
| __fb->_M_destroy_internal_buffer(); |
| __fb->_M_reading = false; |
| __fb->_M_writing = false; |
| __fb->_M_set_buffer(-1); |
| __fb->_M_state_last = __fb->_M_state_cur = __fb->_M_state_beg; |
| } |
| } __cs (this); |
| |
| __try |
| { |
| if (!_M_terminate_output()) |
| __testfail = true; |
| } |
| __catch(__cxxabiv1::__forced_unwind&) |
| { |
| _M_file.close(); |
| __throw_exception_again; |
| } |
| __catch(...) |
| { __testfail = true; } |
| } |
| |
| if (!_M_file.close()) |
| __testfail = true; |
| |
| if (__testfail) |
| return 0; |
| else |
| return this; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| streamsize |
| basic_filebuf<_CharT, _Traits>:: |
| showmanyc() |
| { |
| streamsize __ret = -1; |
| const bool __testin = _M_mode & ios_base::in; |
| if (__testin && this->is_open()) |
| { |
| // For a stateful encoding (-1) the pending sequence might be just |
| // shift and unshift prefixes with no actual character. |
| __ret = this->egptr() - this->gptr(); |
| |
| #if _GLIBCXX_HAVE_DOS_BASED_FILESYSTEM |
| // About this workaround, see libstdc++/20806. |
| const bool __testbinary = _M_mode & ios_base::binary; |
| if (__check_facet(_M_codecvt).encoding() >= 0 |
| && __testbinary) |
| #else |
| if (__check_facet(_M_codecvt).encoding() >= 0) |
| #endif |
| __ret += _M_file.showmanyc() / _M_codecvt->max_length(); |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::int_type |
| basic_filebuf<_CharT, _Traits>:: |
| underflow() |
| { |
| int_type __ret = traits_type::eof(); |
| const bool __testin = _M_mode & ios_base::in; |
| if (__testin) |
| { |
| if (_M_writing) |
| { |
| if (overflow() == traits_type::eof()) |
| return __ret; |
| _M_set_buffer(-1); |
| _M_writing = false; |
| } |
| // Check for pback madness, and if so switch back to the |
| // normal buffers and jet outta here before expensive |
| // fileops happen... |
| _M_destroy_pback(); |
| |
| if (this->gptr() < this->egptr()) |
| return traits_type::to_int_type(*this->gptr()); |
| |
| // Get and convert input sequence. |
| const size_t __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1; |
| |
| // Will be set to true if ::read() returns 0 indicating EOF. |
| bool __got_eof = false; |
| // Number of internal characters produced. |
| streamsize __ilen = 0; |
| codecvt_base::result __r = codecvt_base::ok; |
| if (__check_facet(_M_codecvt).always_noconv()) |
| { |
| __ilen = _M_file.xsgetn(reinterpret_cast<char*>(this->eback()), |
| __buflen); |
| if (__ilen == 0) |
| __got_eof = true; |
| } |
| else |
| { |
| // Worst-case number of external bytes. |
| // XXX Not done encoding() == -1. |
| const int __enc = _M_codecvt->encoding(); |
| streamsize __blen; // Minimum buffer size. |
| streamsize __rlen; // Number of chars to read. |
| if (__enc > 0) |
| __blen = __rlen = __buflen * __enc; |
| else |
| { |
| __blen = __buflen + _M_codecvt->max_length() - 1; |
| __rlen = __buflen; |
| } |
| const streamsize __remainder = _M_ext_end - _M_ext_next; |
| __rlen = __rlen > __remainder ? __rlen - __remainder : 0; |
| |
| // An imbue in 'read' mode implies first converting the external |
| // chars already present. |
| if (_M_reading && this->egptr() == this->eback() && __remainder) |
| __rlen = 0; |
| |
| // Allocate buffer if necessary and move unconverted |
| // bytes to front. |
| if (_M_ext_buf_size < __blen) |
| { |
| char* __buf = new char[__blen]; |
| if (__remainder) |
| __builtin_memcpy(__buf, _M_ext_next, __remainder); |
| |
| delete [] _M_ext_buf; |
| _M_ext_buf = __buf; |
| _M_ext_buf_size = __blen; |
| } |
| else if (__remainder) |
| __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder); |
| |
| _M_ext_next = _M_ext_buf; |
| _M_ext_end = _M_ext_buf + __remainder; |
| _M_state_last = _M_state_cur; |
| |
| do |
| { |
| if (__rlen > 0) |
| { |
| // Sanity check! |
| // This may fail if the return value of |
| // codecvt::max_length() is bogus. |
| if (_M_ext_end - _M_ext_buf + __rlen > _M_ext_buf_size) |
| { |
| __throw_ios_failure(__N("basic_filebuf::underflow " |
| "codecvt::max_length() " |
| "is not valid")); |
| } |
| streamsize __elen = _M_file.xsgetn(_M_ext_end, __rlen); |
| if (__elen == 0) |
| __got_eof = true; |
| else if (__elen == -1) |
| break; |
| _M_ext_end += __elen; |
| } |
| |
| char_type* __iend = this->eback(); |
| if (_M_ext_next < _M_ext_end) |
| __r = _M_codecvt->in(_M_state_cur, _M_ext_next, |
| _M_ext_end, _M_ext_next, |
| this->eback(), |
| this->eback() + __buflen, __iend); |
| if (__r == codecvt_base::noconv) |
| { |
| size_t __avail = _M_ext_end - _M_ext_buf; |
| __ilen = std::min(__avail, __buflen); |
| traits_type::copy(this->eback(), |
| reinterpret_cast<char_type*> |
| (_M_ext_buf), __ilen); |
| _M_ext_next = _M_ext_buf + __ilen; |
| } |
| else |
| __ilen = __iend - this->eback(); |
| |
| // _M_codecvt->in may return error while __ilen > 0: this is |
| // ok, and actually occurs in case of mixed encodings (e.g., |
| // XML files). |
| if (__r == codecvt_base::error) |
| break; |
| |
| __rlen = 1; |
| } |
| while (__ilen == 0 && !__got_eof); |
| } |
| |
| if (__ilen > 0) |
| { |
| _M_set_buffer(__ilen); |
| _M_reading = true; |
| __ret = traits_type::to_int_type(*this->gptr()); |
| } |
| else if (__got_eof) |
| { |
| // If the actual end of file is reached, set 'uncommitted' |
| // mode, thus allowing an immediate write without an |
| // intervening seek. |
| _M_set_buffer(-1); |
| _M_reading = false; |
| // However, reaching it while looping on partial means that |
| // the file has got an incomplete character. |
| if (__r == codecvt_base::partial) |
| __throw_ios_failure(__N("basic_filebuf::underflow " |
| "incomplete character in file")); |
| } |
| else if (__r == codecvt_base::error) |
| __throw_ios_failure(__N("basic_filebuf::underflow " |
| "invalid byte sequence in file")); |
| else |
| __throw_ios_failure(__N("basic_filebuf::underflow " |
| "error reading the file")); |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::int_type |
| basic_filebuf<_CharT, _Traits>:: |
| pbackfail(int_type __i) |
| { |
| int_type __ret = traits_type::eof(); |
| const bool __testin = _M_mode & ios_base::in; |
| if (__testin) |
| { |
| if (_M_writing) |
| { |
| if (overflow() == traits_type::eof()) |
| return __ret; |
| _M_set_buffer(-1); |
| _M_writing = false; |
| } |
| // Remember whether the pback buffer is active, otherwise below |
| // we may try to store in it a second char (libstdc++/9761). |
| const bool __testpb = _M_pback_init; |
| const bool __testeof = traits_type::eq_int_type(__i, __ret); |
| int_type __tmp; |
| if (this->eback() < this->gptr()) |
| { |
| this->gbump(-1); |
| __tmp = traits_type::to_int_type(*this->gptr()); |
| } |
| else if (this->seekoff(-1, ios_base::cur) != pos_type(off_type(-1))) |
| { |
| __tmp = this->underflow(); |
| if (traits_type::eq_int_type(__tmp, __ret)) |
| return __ret; |
| } |
| else |
| { |
| // At the beginning of the buffer, need to make a |
| // putback position available. But the seek may fail |
| // (f.i., at the beginning of a file, see |
| // libstdc++/9439) and in that case we return |
| // traits_type::eof(). |
| return __ret; |
| } |
| |
| // Try to put back __i into input sequence in one of three ways. |
| // Order these tests done in is unspecified by the standard. |
| if (!__testeof && traits_type::eq_int_type(__i, __tmp)) |
| __ret = __i; |
| else if (__testeof) |
| __ret = traits_type::not_eof(__i); |
| else if (!__testpb) |
| { |
| _M_create_pback(); |
| _M_reading = true; |
| *this->gptr() = traits_type::to_char_type(__i); |
| __ret = __i; |
| } |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::int_type |
| basic_filebuf<_CharT, _Traits>:: |
| overflow(int_type __c) |
| { |
| int_type __ret = traits_type::eof(); |
| const bool __testeof = traits_type::eq_int_type(__c, __ret); |
| const bool __testout = _M_mode & ios_base::out; |
| if (__testout) |
| { |
| if (_M_reading) |
| { |
| _M_destroy_pback(); |
| const int __gptr_off = _M_get_ext_pos(_M_state_last); |
| if (_M_seek(__gptr_off, ios_base::cur, _M_state_last) |
| == pos_type(off_type(-1))) |
| return __ret; |
| } |
| if (this->pbase() < this->pptr()) |
| { |
| // If appropriate, append the overflow char. |
| if (!__testeof) |
| { |
| *this->pptr() = traits_type::to_char_type(__c); |
| this->pbump(1); |
| } |
| |
| // Convert pending sequence to external representation, |
| // and output. |
| if (_M_convert_to_external(this->pbase(), |
| this->pptr() - this->pbase())) |
| { |
| _M_set_buffer(0); |
| __ret = traits_type::not_eof(__c); |
| } |
| } |
| else if (_M_buf_size > 1) |
| { |
| // Overflow in 'uncommitted' mode: set _M_writing, set |
| // the buffer to the initial 'write' mode, and put __c |
| // into the buffer. |
| _M_set_buffer(0); |
| _M_writing = true; |
| if (!__testeof) |
| { |
| *this->pptr() = traits_type::to_char_type(__c); |
| this->pbump(1); |
| } |
| __ret = traits_type::not_eof(__c); |
| } |
| else |
| { |
| // Unbuffered. |
| char_type __conv = traits_type::to_char_type(__c); |
| if (__testeof || _M_convert_to_external(&__conv, 1)) |
| { |
| _M_writing = true; |
| __ret = traits_type::not_eof(__c); |
| } |
| } |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| bool |
| basic_filebuf<_CharT, _Traits>:: |
| _M_convert_to_external(_CharT* __ibuf, streamsize __ilen) |
| { |
| // Sizes of external and pending output. |
| streamsize __elen; |
| streamsize __plen; |
| if (__check_facet(_M_codecvt).always_noconv()) |
| { |
| __elen = _M_file.xsputn(reinterpret_cast<char*>(__ibuf), __ilen); |
| __plen = __ilen; |
| } |
| else |
| { |
| // Worst-case number of external bytes needed. |
| // XXX Not done encoding() == -1. |
| streamsize __blen = __ilen * _M_codecvt->max_length(); |
| char* __buf = static_cast<char*>(__builtin_alloca(__blen)); |
| |
| char* __bend; |
| const char_type* __iend; |
| codecvt_base::result __r; |
| __r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen, |
| __iend, __buf, __buf + __blen, __bend); |
| |
| if (__r == codecvt_base::ok || __r == codecvt_base::partial) |
| __blen = __bend - __buf; |
| else if (__r == codecvt_base::noconv) |
| { |
| // Same as the always_noconv case above. |
| __buf = reinterpret_cast<char*>(__ibuf); |
| __blen = __ilen; |
| } |
| else |
| __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external " |
| "conversion error")); |
| |
| __elen = _M_file.xsputn(__buf, __blen); |
| __plen = __blen; |
| |
| // Try once more for partial conversions. |
| if (__r == codecvt_base::partial && __elen == __plen) |
| { |
| const char_type* __iresume = __iend; |
| streamsize __rlen = this->pptr() - __iend; |
| __r = _M_codecvt->out(_M_state_cur, __iresume, |
| __iresume + __rlen, __iend, __buf, |
| __buf + __blen, __bend); |
| if (__r != codecvt_base::error) |
| { |
| __rlen = __bend - __buf; |
| __elen = _M_file.xsputn(__buf, __rlen); |
| __plen = __rlen; |
| } |
| else |
| __throw_ios_failure(__N("basic_filebuf::_M_convert_to_external " |
| "conversion error")); |
| } |
| } |
| return __elen == __plen; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| streamsize |
| basic_filebuf<_CharT, _Traits>:: |
| xsgetn(_CharT* __s, streamsize __n) |
| { |
| // Clear out pback buffer before going on to the real deal... |
| streamsize __ret = 0; |
| if (_M_pback_init) |
| { |
| if (__n > 0 && this->gptr() == this->eback()) |
| { |
| *__s++ = *this->gptr(); // emulate non-underflowing sbumpc |
| this->gbump(1); |
| __ret = 1; |
| --__n; |
| } |
| _M_destroy_pback(); |
| } |
| else if (_M_writing) |
| { |
| if (overflow() == traits_type::eof()) |
| return __ret; |
| _M_set_buffer(-1); |
| _M_writing = false; |
| } |
| |
| // Optimization in the always_noconv() case, to be generalized in the |
| // future: when __n > __buflen we read directly instead of using the |
| // buffer repeatedly. |
| const bool __testin = _M_mode & ios_base::in; |
| const streamsize __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1; |
| |
| if (__n > __buflen && __check_facet(_M_codecvt).always_noconv() |
| && __testin) |
| { |
| // First, copy the chars already present in the buffer. |
| const streamsize __avail = this->egptr() - this->gptr(); |
| if (__avail != 0) |
| { |
| traits_type::copy(__s, this->gptr(), __avail); |
| __s += __avail; |
| this->setg(this->eback(), this->gptr() + __avail, |
| this->egptr()); |
| __ret += __avail; |
| __n -= __avail; |
| } |
| |
| // Need to loop in case of short reads (relatively common |
| // with pipes). |
| streamsize __len; |
| for (;;) |
| { |
| __len = _M_file.xsgetn(reinterpret_cast<char*>(__s), |
| __n); |
| if (__len == -1) |
| __throw_ios_failure(__N("basic_filebuf::xsgetn " |
| "error reading the file")); |
| if (__len == 0) |
| break; |
| |
| __n -= __len; |
| __ret += __len; |
| if (__n == 0) |
| break; |
| |
| __s += __len; |
| } |
| |
| if (__n == 0) |
| { |
| _M_set_buffer(0); |
| _M_reading = true; |
| } |
| else if (__len == 0) |
| { |
| // If end of file is reached, set 'uncommitted' |
| // mode, thus allowing an immediate write without |
| // an intervening seek. |
| _M_set_buffer(-1); |
| _M_reading = false; |
| } |
| } |
| else |
| __ret += __streambuf_type::xsgetn(__s, __n); |
| |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| streamsize |
| basic_filebuf<_CharT, _Traits>:: |
| xsputn(const _CharT* __s, streamsize __n) |
| { |
| streamsize __ret = 0; |
| // Optimization in the always_noconv() case, to be generalized in the |
| // future: when __n is sufficiently large we write directly instead of |
| // using the buffer. |
| const bool __testout = _M_mode & ios_base::out; |
| if (__check_facet(_M_codecvt).always_noconv() |
| && __testout && !_M_reading) |
| { |
| // Measurement would reveal the best choice. |
| const streamsize __chunk = 1ul << 10; |
| streamsize __bufavail = this->epptr() - this->pptr(); |
| |
| // Don't mistake 'uncommitted' mode buffered with unbuffered. |
| if (!_M_writing && _M_buf_size > 1) |
| __bufavail = _M_buf_size - 1; |
| |
| const streamsize __limit = std::min(__chunk, __bufavail); |
| if (__n >= __limit) |
| { |
| const streamsize __buffill = this->pptr() - this->pbase(); |
| const char* __buf = reinterpret_cast<const char*>(this->pbase()); |
| __ret = _M_file.xsputn_2(__buf, __buffill, |
| reinterpret_cast<const char*>(__s), |
| __n); |
| if (__ret == __buffill + __n) |
| { |
| _M_set_buffer(0); |
| _M_writing = true; |
| } |
| if (__ret > __buffill) |
| __ret -= __buffill; |
| else |
| __ret = 0; |
| } |
| else |
| __ret = __streambuf_type::xsputn(__s, __n); |
| } |
| else |
| __ret = __streambuf_type::xsputn(__s, __n); |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::__streambuf_type* |
| basic_filebuf<_CharT, _Traits>:: |
| setbuf(char_type* __s, streamsize __n) |
| { |
| if (!this->is_open()) |
| { |
| if (__s == 0 && __n == 0) |
| _M_buf_size = 1; |
| else if (__s && __n > 0) |
| { |
| // This is implementation-defined behavior, and assumes that |
| // an external char_type array of length __n exists and has |
| // been pre-allocated. If this is not the case, things will |
| // quickly blow up. When __n > 1, __n - 1 positions will be |
| // used for the get area, __n - 1 for the put area and 1 |
| // position to host the overflow char of a full put area. |
| // When __n == 1, 1 position will be used for the get area |
| // and 0 for the put area, as in the unbuffered case above. |
| _M_buf = __s; |
| _M_buf_size = __n; |
| } |
| } |
| return this; |
| } |
| |
| |
| // According to 27.8.1.4 p11 - 13, seekoff should ignore the last |
| // argument (of type openmode). |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::pos_type |
| basic_filebuf<_CharT, _Traits>:: |
| seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode) |
| { |
| int __width = 0; |
| if (_M_codecvt) |
| __width = _M_codecvt->encoding(); |
| if (__width < 0) |
| __width = 0; |
| |
| pos_type __ret = pos_type(off_type(-1)); |
| const bool __testfail = __off != 0 && __width <= 0; |
| if (this->is_open() && !__testfail) |
| { |
| // tellg and tellp queries do not affect any state, unless |
| // ! always_noconv and the put sequence is not empty. |
| // In that case, determining the position requires converting the |
| // put sequence. That doesn't use ext_buf, so requires a flush. |
| bool __no_movement = __way == ios_base::cur && __off == 0 |
| && (!_M_writing || _M_codecvt->always_noconv()); |
| |
| // Ditch any pback buffers to avoid confusion. |
| if (!__no_movement) |
| _M_destroy_pback(); |
| |
| // Correct state at destination. Note that this is the correct |
| // state for the current position during output, because |
| // codecvt::unshift() returns the state to the initial state. |
| // This is also the correct state at the end of the file because |
| // an unshift sequence should have been written at the end. |
| __state_type __state = _M_state_beg; |
| off_type __computed_off = __off * __width; |
| if (_M_reading && __way == ios_base::cur) |
| { |
| __state = _M_state_last; |
| __computed_off += _M_get_ext_pos(__state); |
| } |
| if (!__no_movement) |
| __ret = _M_seek(__computed_off, __way, __state); |
| else |
| { |
| if (_M_writing) |
| __computed_off = this->pptr() - this->pbase(); |
| |
| off_type __file_off = _M_file.seekoff(0, ios_base::cur); |
| if (__file_off != off_type(-1)) |
| { |
| __ret = __file_off + __computed_off; |
| __ret.state(__state); |
| } |
| } |
| } |
| return __ret; |
| } |
| |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS |
| // 171. Strange seekpos() semantics due to joint position |
| // According to the resolution of DR 171, seekpos should ignore the last |
| // argument (of type openmode). |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::pos_type |
| basic_filebuf<_CharT, _Traits>:: |
| seekpos(pos_type __pos, ios_base::openmode) |
| { |
| pos_type __ret = pos_type(off_type(-1)); |
| if (this->is_open()) |
| { |
| // Ditch any pback buffers to avoid confusion. |
| _M_destroy_pback(); |
| __ret = _M_seek(off_type(__pos), ios_base::beg, __pos.state()); |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| typename basic_filebuf<_CharT, _Traits>::pos_type |
| basic_filebuf<_CharT, _Traits>:: |
| _M_seek(off_type __off, ios_base::seekdir __way, __state_type __state) |
| { |
| pos_type __ret = pos_type(off_type(-1)); |
| if (_M_terminate_output()) |
| { |
| off_type __file_off = _M_file.seekoff(__off, __way); |
| if (__file_off != off_type(-1)) |
| { |
| _M_reading = false; |
| _M_writing = false; |
| _M_ext_next = _M_ext_end = _M_ext_buf; |
| _M_set_buffer(-1); |
| _M_state_cur = __state; |
| __ret = __file_off; |
| __ret.state(_M_state_cur); |
| } |
| } |
| return __ret; |
| } |
| |
| // Returns the distance from the end of the ext buffer to the point |
| // corresponding to gptr(). This is a negative value. Updates __state |
| // from eback() correspondence to gptr(). |
| template<typename _CharT, typename _Traits> |
| int basic_filebuf<_CharT, _Traits>:: |
| _M_get_ext_pos(__state_type& __state) |
| { |
| if (_M_codecvt->always_noconv()) |
| return this->gptr() - this->egptr(); |
| else |
| { |
| // Calculate offset from _M_ext_buf that corresponds to |
| // gptr(). Precondition: __state == _M_state_last, which |
| // corresponds to eback(). |
| const int __gptr_off = |
| _M_codecvt->length(__state, _M_ext_buf, _M_ext_next, |
| this->gptr() - this->eback()); |
| return _M_ext_buf + __gptr_off - _M_ext_end; |
| } |
| } |
| |
| template<typename _CharT, typename _Traits> |
| bool |
| basic_filebuf<_CharT, _Traits>:: |
| _M_terminate_output() |
| { |
| // Part one: update the output sequence. |
| bool __testvalid = true; |
| if (this->pbase() < this->pptr()) |
| { |
| const int_type __tmp = this->overflow(); |
| if (traits_type::eq_int_type(__tmp, traits_type::eof())) |
| __testvalid = false; |
| } |
| |
| // Part two: output unshift sequence. |
| if (_M_writing && !__check_facet(_M_codecvt).always_noconv() |
| && __testvalid) |
| { |
| // Note: this value is arbitrary, since there is no way to |
| // get the length of the unshift sequence from codecvt, |
| // without calling unshift. |
| const size_t __blen = 128; |
| char __buf[__blen]; |
| codecvt_base::result __r; |
| streamsize __ilen = 0; |
| |
| do |
| { |
| char* __next; |
| __r = _M_codecvt->unshift(_M_state_cur, __buf, |
| __buf + __blen, __next); |
| if (__r == codecvt_base::error) |
| __testvalid = false; |
| else if (__r == codecvt_base::ok || |
| __r == codecvt_base::partial) |
| { |
| __ilen = __next - __buf; |
| if (__ilen > 0) |
| { |
| const streamsize __elen = _M_file.xsputn(__buf, __ilen); |
| if (__elen != __ilen) |
| __testvalid = false; |
| } |
| } |
| } |
| while (__r == codecvt_base::partial && __ilen > 0 && __testvalid); |
| |
| if (__testvalid) |
| { |
| // This second call to overflow() is required by the standard, |
| // but it's not clear why it's needed, since the output buffer |
| // should be empty by this point (it should have been emptied |
| // in the first call to overflow()). |
| const int_type __tmp = this->overflow(); |
| if (traits_type::eq_int_type(__tmp, traits_type::eof())) |
| __testvalid = false; |
| } |
| } |
| return __testvalid; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| int |
| basic_filebuf<_CharT, _Traits>:: |
| sync() |
| { |
| // Make sure that the internal buffer resyncs its idea of |
| // the file position with the external file. |
| int __ret = 0; |
| if (this->pbase() < this->pptr()) |
| { |
| const int_type __tmp = this->overflow(); |
| if (traits_type::eq_int_type(__tmp, traits_type::eof())) |
| __ret = -1; |
| } |
| return __ret; |
| } |
| |
| template<typename _CharT, typename _Traits> |
| void |
| basic_filebuf<_CharT, _Traits>:: |
| imbue(const locale& __loc) |
| { |
| bool __testvalid = true; |
| |
| const __codecvt_type* _M_codecvt_tmp = 0; |
| if (__builtin_expect(has_facet<__codecvt_type>(__loc), true)) |
| _M_codecvt_tmp = &use_facet<__codecvt_type>(__loc); |
| |
| if (this->is_open()) |
| { |
| // encoding() == -1 is ok only at the beginning. |
| if ((_M_reading || _M_writing) |
| && __check_facet(_M_codecvt).encoding() == -1) |
| __testvalid = false; |
| else |
| { |
| if (_M_reading) |
| { |
| if (__check_facet(_M_codecvt).always_noconv()) |
| { |
| if (_M_codecvt_tmp |
| && !__check_facet(_M_codecvt_tmp).always_noconv()) |
| __testvalid = this->seekoff(0, ios_base::cur, _M_mode) |
| != pos_type(off_type(-1)); |
| } |
| else |
| { |
| // External position corresponding to gptr(). |
| _M_ext_next = _M_ext_buf |
| + _M_codecvt->length(_M_state_last, _M_ext_buf, |
| _M_ext_next, |
| this->gptr() - this->eback()); |
| const streamsize __remainder = _M_ext_end - _M_ext_next; |
| if (__remainder) |
| __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder); |
| |
| _M_ext_next = _M_ext_buf; |
| _M_ext_end = _M_ext_buf + __remainder; |
| _M_set_buffer(-1); |
| _M_state_last = _M_state_cur = _M_state_beg; |
| } |
| } |
| else if (_M_writing && (__testvalid = _M_terminate_output())) |
| _M_set_buffer(-1); |
| } |
| } |
| |
| if (__testvalid) |
| _M_codecvt = _M_codecvt_tmp; |
| else |
| _M_codecvt = 0; |
| } |
| |
| // Inhibit implicit instantiations for required instantiations, |
| // which are defined via explicit instantiations elsewhere. |
| #if _GLIBCXX_EXTERN_TEMPLATE |
| extern template class basic_filebuf<char>; |
| extern template class basic_ifstream<char>; |
| extern template class basic_ofstream<char>; |
| extern template class basic_fstream<char>; |
| |
| #ifdef _GLIBCXX_USE_WCHAR_T |
| extern template class basic_filebuf<wchar_t>; |
| extern template class basic_ifstream<wchar_t>; |
| extern template class basic_ofstream<wchar_t>; |
| extern template class basic_fstream<wchar_t>; |
| #endif |
| #endif |
| |
| _GLIBCXX_END_NAMESPACE_VERSION |
| } // namespace std |
| |
| #endif |