Index: include/std/fstream =================================================================== --- include/std/fstream (revision 164549) +++ include/std/fstream (working copy) @@ -125,8 +125,8 @@ * * NB: _M_reading == true && _M_writing == true is unused. */ - bool _M_reading; - bool _M_writing; + bool _M_codecvt_always_noconv; + bool _M_post_overflow; //@{ /** @@ -163,6 +163,18 @@ const char* _M_ext_next; char* _M_ext_end; + bool + _M_reading() const + { + return this->eback() != this->egptr(); + } + + bool + _M_writing() const + { + return this->pbase() != this->epptr(); + } + /** * Initializes pback buffers, and moves normal buffers to safety. * Assumptions: Index: include/ext/stdio_filebuf.h =================================================================== --- include/ext/stdio_filebuf.h (revision 164549) +++ include/ext/stdio_filebuf.h (working copy) @@ -131,8 +131,6 @@ this->_M_mode = __mode; this->_M_buf_size = __size; this->_M_allocate_internal_buffer(); - this->_M_reading = false; - this->_M_writing = false; this->_M_set_buffer(-1); } } @@ -148,8 +146,6 @@ this->_M_mode = __mode; this->_M_buf_size = __size; this->_M_allocate_internal_buffer(); - this->_M_reading = false; - this->_M_writing = false; this->_M_set_buffer(-1); } } Index: include/bits/fstream.tcc =================================================================== --- include/bits/fstream.tcc (revision 164549) +++ include/bits/fstream.tcc (working copy) @@ -78,15 +78,12 @@ 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_state_last(), _M_buf(0), _M_buf_size(BUFSIZ), _M_buf_allocated(false), + _M_codecvt_always_noconv(false), _M_post_overflow(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 basic_filebuf<_CharT, _Traits>::__filebuf_type* @@ -94,8 +91,10 @@ open(const char* __s, ios_base::openmode __mode) { __filebuf_type *__ret = 0; - if (!this->is_open()) + if (!this->is_open() && has_facet<__codecvt_type>(this->getloc())) { + _M_codecvt = &use_facet<__codecvt_type>(this->getloc()); + _M_codecvt_always_noconv = _M_codecvt->always_noconv(); _M_file.open(__s, __mode); if (this->is_open()) { @@ -103,8 +102,6 @@ _M_mode = __mode; // Setup initial buffer to 'uncommitted' mode. - _M_reading = false; - _M_writing = false; _M_set_buffer(-1); // Reset to initial state. @@ -142,8 +139,6 @@ __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; } @@ -188,10 +183,10 @@ #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 + if (_M_codecvt->encoding() >= 0 && __testbinary) #else - if (__check_facet(_M_codecvt).encoding() >= 0) + if (_M_codecvt->encoding() >= 0) #endif __ret += _M_file.showmanyc() / _M_codecvt->max_length(); } @@ -207,13 +202,12 @@ const bool __testin = _M_mode & ios_base::in; if (__testin) { - if (_M_writing) + if (_M_writing()) { __ret = overflow(); if (__ret == 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 @@ -231,7 +225,7 @@ // Number of internal characters produced. streamsize __ilen = 0; codecvt_base::result __r = codecvt_base::ok; - if (__check_facet(_M_codecvt).always_noconv()) + if (_M_codecvt_always_noconv) { __ilen = _M_file.xsgetn(reinterpret_cast(this->eback()), __buflen); @@ -257,7 +251,7 @@ // An imbue in 'read' mode implies first converting the external // chars already present. - if (_M_reading && this->egptr() == this->eback() && __remainder) + if (_M_reading() && this->egptr() == this->eback() && __remainder) __rlen = 0; // Allocate buffer if necessary and move unconverted @@ -332,7 +326,6 @@ if (__ilen > 0) { _M_set_buffer(__ilen); - _M_reading = true; __ret = traits_type::to_int_type(*this->gptr()); } else if (__got_eof) @@ -341,7 +334,6 @@ // 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) @@ -355,6 +347,7 @@ __throw_ios_failure(__N("basic_filebuf::underflow " "error reading the file")); } + _M_post_overflow = false; return __ret; } @@ -367,13 +360,12 @@ const bool __testin = _M_mode & ios_base::in; if (__testin) { - if (_M_writing) + if (_M_writing()) { __ret = overflow(); if (__ret == 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). @@ -410,7 +402,6 @@ else if (!__testpb) { _M_create_pback(); - _M_reading = true; *this->gptr() = traits_type::to_char_type(__i); __ret = __i; } @@ -428,7 +419,7 @@ const bool __testout = _M_mode & ios_base::out; if (__testout) { - if (_M_reading) + if (_M_reading()) { _M_destroy_pback(); const int __gptr_off = _M_get_ext_pos(_M_state_last); @@ -456,11 +447,10 @@ } else if (_M_buf_size > 1) { - // Overflow in 'uncommitted' mode: set _M_writing, set + // Overflow in 'uncommitted' mode: create put area, 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); @@ -474,11 +464,11 @@ 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); } } } + _M_post_overflow = true; return __ret; } @@ -490,7 +480,7 @@ // Sizes of external and pending output. streamsize __elen; streamsize __plen; - if (__check_facet(_M_codecvt).always_noconv()) + if (_M_codecvt_always_noconv) { __elen = _M_file.xsputn(reinterpret_cast(__ibuf), __ilen); __plen = __ilen; @@ -570,8 +560,8 @@ 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 && !_M_writing) + if (__n > __buflen && _M_codecvt_always_noconv + && __testin && !_M_writing()) { // First, copy the chars already present in the buffer. const streamsize __avail = this->egptr() - this->gptr(); @@ -611,7 +601,6 @@ if (__n == 0) { _M_set_buffer(0); - _M_reading = true; } else if (__len == 0) { @@ -619,7 +608,6 @@ // mode, thus allowing an immediate write without // an intervening seek. _M_set_buffer(-1); - _M_reading = false; } } else @@ -638,15 +626,14 @@ // using the buffer. streamsize __ret = 0; const bool __testout = _M_mode & ios_base::out; - if (__check_facet(_M_codecvt).always_noconv() - && __testout && !_M_reading) + if (_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) + if (!_M_writing() && _M_buf_size > 1) __bufavail = _M_buf_size - 1; const streamsize __limit = std::min(__chunk, __bufavail); @@ -660,7 +647,7 @@ if (__ret == __buffill + __n) { _M_set_buffer(0); - _M_writing = true; + _M_post_overflow = true; } if (__ret > __buffill) __ret -= __buffill; @@ -724,7 +711,7 @@ // 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()); + && (!_M_writing() || _M_codecvt_always_noconv); // Ditch any pback buffers to avoid confusion. if (!__no_movement) @@ -737,7 +724,7 @@ // 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) + if (_M_reading() && __way == ios_base::cur) { __state = _M_state_last; __computed_off += _M_get_ext_pos(__state); @@ -746,7 +733,7 @@ __ret = _M_seek(__computed_off, __way, __state); else { - if (_M_writing) + if (_M_writing()) __computed_off = this->pptr() - this->pbase(); off_type __file_off = _M_file.seekoff(0, ios_base::cur); @@ -757,6 +744,7 @@ } } } + _M_post_overflow = false; return __ret; } @@ -776,6 +764,7 @@ _M_destroy_pback(); __ret = _M_seek(off_type(__pos), ios_base::beg, __pos.state()); } + _M_post_overflow = false; return __ret; } @@ -790,8 +779,6 @@ 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; @@ -807,9 +794,9 @@ // from eback() correspondence to gptr(). template int basic_filebuf<_CharT, _Traits>:: - _M_get_ext_pos(__state_type& __state) + _M_get_ext_pos(__state_type &__state) { - if (_M_codecvt->always_noconv()) + if (_M_codecvt_always_noconv) return this->gptr() - this->egptr(); else { @@ -838,8 +825,7 @@ } // Part two: output unshift sequence. - if (_M_writing && !__check_facet(_M_codecvt).always_noconv() - && __testvalid) + if (_M_post_overflow && !_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, @@ -906,54 +892,34 @@ 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 (_M_codecvt->encoding() == -1 // 27.8.1.4/17 + && typeid (*_M_codecvt) != typeid (__new_codecvt_tmp) + && seekoff(0,ios_base::cur) != pos_type(off_type(0))) + __throw_ios_failure(__N("basic_filebuf::imbue " + "cannot leave stateful cvt midstream")); + + The above is very high-minded, but 27.8.1.4/19 mentions that imbue + is intended to reconstruct the entire file into the new encoding. + Hence not working midstream: no pos_type value besides pos_type() + can be translated across encodings. This leaves a dilemma: either + convert the file, which really isn't our job, or disallow imbue + for any non-empty file. Disallowing some subset when encoding == -1 + doesn't make sense; that has no more potential to corrupt the file + than any other case. The least surprising, least restrictive + behavior is to leave the user responsible for shooting his own foot. + + The most we can do is flush and unshift, for determinism. + */ + + if (_M_writing() && !_M_terminate_output() + || _M_reading() && overflow() == traits_type::eof()) + __throw_ios_failure("basic_filebuf::imbue could not flush"); + + _M_codecvt = &use_facet<__codecvt_type>(__loc); + _M_codecvt_always_noconv = _M_codecvt->always_noconv(); } - - if (__testvalid) - _M_codecvt = _M_codecvt_tmp; - else - _M_codecvt = 0; } // Inhibit implicit instantiations for required instantiations, Index: testsuite/27_io/basic_filebuf/seekoff/wchar_t/12790-2.cc =================================================================== --- testsuite/27_io/basic_filebuf/seekoff/wchar_t/12790-2.cc (revision 164549) +++ testsuite/27_io/basic_filebuf/seekoff/wchar_t/12790-2.cc (working copy) @@ -68,7 +68,7 @@ fb.sputc(L'a'); VERIFY( !cvt->unshift_called ); - fb.pubseekoff(0, ios_base::cur); + fb.pubseekoff(0, ios_base::beg); VERIFY( cvt->unshift_called ); } Index: testsuite/27_io/basic_filebuf/seekoff/char/12790-2.cc =================================================================== --- testsuite/27_io/basic_filebuf/seekoff/char/12790-2.cc (revision 164549) +++ testsuite/27_io/basic_filebuf/seekoff/char/12790-2.cc (working copy) @@ -70,7 +70,7 @@ fb.sputc('a'); VERIFY( !cvt->unshift_called ); - fb.pubseekoff(0, ios_base::cur); + fb.pubseekoff(0, ios_base::beg); VERIFY( cvt->unshift_called ); }