Index: include/std/fstream =================================================================== --- include/std/fstream (revision 165349) +++ include/std/fstream (working copy) @@ -355,7 +355,7 @@ pos_type _M_seek(off_type __off, ios_base::seekdir __way, __state_type __state); - int + ptrdiff_t _M_get_ext_pos(__state_type &__state); virtual int Index: include/bits/fstream.tcc =================================================================== --- include/bits/fstream.tcc (revision 165349) +++ include/bits/fstream.tcc (working copy) @@ -429,8 +429,8 @@ 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) + off_type __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; } @@ -714,7 +714,7 @@ basic_filebuf<_CharT, _Traits>:: seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode) { - int __width = 0; + int __width = 1; if (_M_codecvt) __width = _M_codecvt->encoding(); if (__width < 0) @@ -729,7 +729,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 || _M_codecvt->always_noconv()); // Ditch any pback buffers to avoid confusion. if (!__no_movement) @@ -745,7 +745,7 @@ if (_M_reading && __way == ios_base::cur) { __state = _M_state_last; - __computed_off += _M_get_ext_pos(__state); + __computed_off -= _M_get_ext_pos(__state); } if (!__no_movement) __ret = _M_seek(__computed_off, __way, __state); @@ -807,15 +807,14 @@ 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(). + // Returns the distance from the point corresponding to gptr() to the end of + // the ext buffer. Updates __state from eback() correspondence to gptr(). template - int basic_filebuf<_CharT, _Traits>:: + ptrdiff_t basic_filebuf<_CharT, _Traits>:: _M_get_ext_pos(__state_type& __state) { if (_M_codecvt->always_noconv()) - return this->gptr() - this->egptr(); + return this->egptr() - this->gptr(); else { // Calculate offset from _M_ext_buf that corresponds to @@ -824,7 +823,7 @@ 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; + return _M_ext_end - _M_ext_buf - __gptr_off; } } @@ -846,11 +845,8 @@ 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]; + streamsize __blen = _M_codecvt->max_length(); + char* __buf = static_cast(__builtin_alloca(__blen)); codecvt_base::result __r; streamsize __ilen = 0; @@ -911,55 +907,94 @@ 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); - + __codecvt_type const *__cvt = has_facet<__codecvt_type>(__loc)? + &use_facet<__codecvt_type>(__loc) : 0; 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 + /* 27.8.1.4/17 may be implemented as follows, but it's pointless. + (Also, facets being same is ill-defined. Not all facets are + stateless, but enough are to make this the most useful test.) + if (_M_codecvt->encoding() == -1 + && 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")); */ + if (_M_writing) { - if (_M_reading) + if (!_M_terminate_output()) + __throw_ios_failure("basic_filebuf::imbue could not flush"); + _M_set_buffer(-1); + _M_writing = false; + } + else if (_M_reading || !_M_codecvt) + { + _M_destroy_pback(); + if (_M_codecvt) + try + { + this->sgetc(); // Skip any termination/unshift sequence. + } + catch (ios_base::failure const &__exc) + { + if (_M_ext_next == _M_ext_end) + throw; // Rethrow I/O error, ignore codecvt error. + // Unfortunately above ignores I/O errors before first conv. + if (__builtin_strlen(__exc.what()) != 54) + throw; // string != "... invalid byte sequence in file" + } + + // Retrace back to gptr() position. + off_type __off = _M_codecvt? + _M_get_ext_pos(_M_state_last) : _M_ext_end - _M_ext_next; + + char *__src_buf = _M_codecvt && _M_codecvt->always_noconv()? + reinterpret_cast(this->gptr()) : _M_ext_end - __off; + char *__dst_buf = __cvt && __cvt->always_noconv()? + reinterpret_cast(this->eback()) : _M_ext_buf; + + if (__dst_buf == _M_ext_buf && _M_ext_buf == NULL && __off != 0) { - 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; - } + __dst_buf = _M_ext_buf = new char[__off]; + _M_ext_buf_size = __off; + } + + /* If there isn't room in the main buffer for the remainder of the + ext buffer, swap them. (And after memmove, deallocate ext buf.) + The reverse never happens: If the ext buffer receives the main + buffer, it must have just been allocated by the above "if." */ + else if (__dst_buf != _M_ext_buf && _M_buf_size < __off) + { + __dst_buf = _M_ext_buf; + // dst_buf == eback implies noconv, so char_type is char + _M_ext_buf = _M_buf_allocated? + reinterpret_cast(_M_buf) : NULL; + _M_buf = reinterpret_cast(__dst_buf); + _M_buf_allocated = true; + _M_buf_size = _M_ext_buf_size; + } + + __builtin_memmove(__dst_buf, __src_buf, __off); + + if (__dst_buf == _M_ext_buf) + { + // Clear get area to force reconversion. + _M_ext_next = _M_ext_buf; + _M_ext_end = _M_ext_buf + __off; + _M_reading = __cvt != 0 && __off != 0; + _M_set_buffer(-1); + } + else + { + delete [] _M_ext_buf; + _M_ext_next = _M_ext_end = _M_ext_buf = NULL; + _M_ext_buf_size = 0; + _M_reading = __off != 0; + _M_set_buffer(_M_reading? __off : -1); } - else if (_M_writing && (__testvalid = _M_terminate_output())) - _M_set_buffer(-1); } + _M_state_last = _M_state_cur = _M_state_beg; } - - if (__testvalid) - _M_codecvt = _M_codecvt_tmp; - else - _M_codecvt = 0; + _M_codecvt = __cvt; } // Inhibit implicit instantiations for required instantiations, Index: testsuite/27_io/basic_filebuf/imbue/1.cc =================================================================== --- testsuite/27_io/basic_filebuf/imbue/1.cc (revision 0) +++ testsuite/27_io/basic_filebuf/imbue/1.cc (revision 0) @@ -0,0 +1,309 @@ +// Copyright (C) 2010 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. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-require-fileio "" } + +#include +#include +#include +#include + +struct string_state +{ + char s[20000]; // random storage + // assumption: no padding here + size_t n; // # chars in current block + + char *end() { return s + sizeof s; } +}; + +struct string_state_traits : std::char_traits +{ + typedef string_state state_type; + typedef std::fpos pos_type; +}; + + +namespace std +{ + template<> + class codecvt + : public locale::facet, public codecvt_base + { + protected: + virtual result do_out(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return noconv; } + + virtual result do_unshift(string_state &ss, char *tf, char *tl, + char *&tn) const + { return noconv; } + + virtual result do_in(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return noconv; } + + virtual int do_length(string_state &ss, char const *ff, char const *fl, + size_t tn) const + { return tn < fl - ff? tn : fl - ff; } + + virtual int + do_max_length() const throw() + { return 1; } + + virtual int + do_encoding() const throw() + { return 1; } + + virtual bool + do_always_noconv() const throw() + { return true; } + + public: + result out(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return do_out(ss, ff, fl, fn, tf, tl, tn); } + + result unshift(string_state &ss, char *tf, char *tl, + char *&tn) const + { return do_unshift(ss, tf, tl, tn); } + + result in(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return do_in(ss, ff, fl, fn, tf, tl, tn); } + + int length(string_state &ss, char const *ff, char const *fl, + size_t tn) const + { return do_length(ss, ff, fl, tn); } + + int + max_length() const throw() + { return do_max_length(); } + + int + encoding() const throw() + { return do_encoding(); } + + bool + always_noconv() const throw() + { return do_always_noconv(); } + + explicit codecvt(size_t refs = 0) : facet(refs) { } + + static locale::id id; + }; + + locale::id codecvt::id; +} + + +class reverse_word_cvt : public std::codecvt +{ + size_t word_size; + + size_t block_size() const + { return word_size + sizeof word_size; } + + result do_out(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { + fn = ff; + tn = tf; + + if (tl - tn < block_size()) return error; + + for (;;) + { + char *d = ss.end() - ss.n; + do + { + * --d = * fn++; + ++ ss.n; + } + while (ss.n != word_size && fn != fl); + + if (ss.n == word_size) + { + memcpy( tn, d, block_size() ); + tn += block_size(); + ss.n = 0; + } + + if (fn == fl) return ok; + if (tl - tn < block_size()) return partial; + } + } + + result do_unshift(string_state &ss, char *tf, char *tl, char *&tn) const + { + tn = tf; + if (ss.n == 0) return noconv; + if (tl - tn < block_size()) return error; + + reinterpret_cast(tn[word_size]) = ss.n; + memcpy( tn, ss.end() - word_size, block_size() ); + tn += block_size(); + ss.n = 0; + return ok; + } + + result do_in(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { + fn = ff; + tn = tf; + + if (fl - fn < block_size()) return partial; + + for (;;) + { + size_t n_max = reinterpret_cast(fn[word_size]); + if (n_max <= 0 || n_max > word_size) return error; + do + { + * tn++ = fn[ word_size - ++ ss.n ]; + } + while (ss.n != n_max && tn != tl); + + if (ss.n == n_max) + { + fn += block_size(); + ss.n = 0; + } + + if (fn == fl) return ok; + if (fl - fn < block_size()) return partial; + if (tn == tl) return partial; + } + } + + int do_length(string_state &ss, char const *ff, char const *fl, + size_t tn) const + { + char const *fn = ff; + if (fl - fn < block_size()) return 0; + + for (;;) + { + size_t n_max = reinterpret_cast(fn[word_size]); + if (n_max <= 0 || n_max > word_size) return fn - ff; + int n = tn < n_max - ss.n? tn : n_max - ss.n; + ss.n += n; + tn -= n; + + if (ss.n == n_max) + { + fn += block_size(); + ss.n = 0; + } + if (tn == 0 || fl - fn < block_size()) return fn - ff; + } + } + + int + do_max_length() const throw() + { return block_size(); } + + int + do_encoding() const throw() + { return -1; } + + bool + do_always_noconv() const throw() + { return false; } + +public: + reverse_word_cvt(size_t n, size_t refs = 0) + : std::codecvt(refs), word_size(n) { } +}; + + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::locale good_loc(std::locale(), + new std::codecvt); + std::locale evil_loc(std::locale(), new reverse_word_cvt(5)); + + std::basic_filebuf fb; + fb.open("imbue_1.tst", std::ios::in | std::ios::out | std::ios::trunc); + char const se[] = "encoded text", sne[] = "non-encoded text"; + + fb.pubimbue(good_loc); // uncommitted, invalid => noconv + fb.sputn(sne, sizeof sne); + fb.pubimbue(evil_loc); // write, noconv => conversion + fb.sputn(se, sizeof se); + std::fpos p = fb.pubseekoff(0, std::ios::cur); + fb.pubimbue(good_loc); // write, conversion => noconv + fb.sputn(sne, sizeof sne); + + fb.pubseekoff(sizeof sne, std::ios::beg); + + char buf[6]; + fb.pubimbue(evil_loc); // uncommitted, noconv => conversion + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, se, sizeof buf) == 0 ); + + p = fb.pubseekoff(0, std::ios::cur); + for (int i = 0; i < sizeof se - sizeof buf; ++i) fb.sbumpc(); + fb.pubimbue(good_loc); // read, conversion => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); + + fb.pubimbue(std::locale()); // read, conversion => invalid + fb.pubseekoff(0, std::ios::beg); // seek while invalid => uncommitted + + fb.pubimbue(evil_loc); // uncommitted, invalid => conversion + fb.pubseekpos(p); + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, se + sizeof buf, sizeof buf) == 0 ); + + for (int i = 0; i < sizeof se - 2 * sizeof buf; ++i) fb.sbumpc(); + fb.pubimbue(good_loc); // read, conversion => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); + + fb.pubseekoff(0, std::ios::beg); + fb.sbumpc(); + fb.sungetc(); + fb.pubimbue(std::locale()); // read+putback, noconv => invalid + fb.pubimbue(good_loc); // read, invalid => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); + + for (int i = 0; i < sizeof sne - sizeof buf; ++i) fb.sbumpc(); + fb.sbumpc(); + fb.sungetc(); + fb.pubimbue(std::locale()); // read+putback, noconv => invalid + fb.pubimbue(evil_loc); // read, invalid => conversion + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, se, sizeof buf) == 0 ); + + for (int i = 0; i < sizeof se - sizeof buf; ++i) fb.sbumpc(); + fb.pubimbue(std::locale()); // read, conversion => invalid + fb.pubimbue(good_loc); // read, invalid => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); +} + +int main() +{ + test01(); + return 0; +} + Index: testsuite/27_io/basic_filebuf/imbue/2.cc =================================================================== --- testsuite/27_io/basic_filebuf/imbue/2.cc (revision 0) +++ testsuite/27_io/basic_filebuf/imbue/2.cc (revision 0) @@ -0,0 +1,310 @@ +// Copyright (C) 2010 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. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-require-fileio "" } + +#include +#include +#include +#include + +struct string_state +{ + char s[20000]; // random storage + // assumption: no padding here + size_t n; // # chars in current block + + char *end() { return s + sizeof s; } +}; + +struct string_state_traits : std::char_traits +{ + typedef string_state state_type; + typedef std::fpos pos_type; +}; + + +namespace std +{ + template<> + class codecvt + : public locale::facet, public codecvt_base + { + protected: + virtual result do_out(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return noconv; } + + virtual result do_unshift(string_state &ss, char *tf, char *tl, + char *&tn) const + { return noconv; } + + virtual result do_in(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return noconv; } + + virtual int do_length(string_state &ss, char const *ff, char const *fl, + size_t tn) const + { return tn < fl - ff? tn : fl - ff; } + + virtual int + do_max_length() const throw() + { return 1; } + + virtual int + do_encoding() const throw() + { return 1; } + + virtual bool + do_always_noconv() const throw() + { return true; } + + public: + result out(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return do_out(ss, ff, fl, fn, tf, tl, tn); } + + result unshift(string_state &ss, char *tf, char *tl, + char *&tn) const + { return do_unshift(ss, tf, tl, tn); } + + result in(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { return do_in(ss, ff, fl, fn, tf, tl, tn); } + + int length(string_state &ss, char const *ff, char const *fl, + size_t tn) const + { return do_length(ss, ff, fl, tn); } + + int + max_length() const throw() + { return do_max_length(); } + + int + encoding() const throw() + { return do_encoding(); } + + bool + always_noconv() const throw() + { return do_always_noconv(); } + + explicit codecvt(size_t refs = 0) : facet(refs) { } + + static locale::id id; + }; + + locale::id codecvt::id; +} + + +class reverse_word_cvt : public std::codecvt +{ + size_t word_size; + + size_t block_size() const + { return word_size + sizeof word_size; } + + result do_out(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { + fn = ff; + tn = tf; + + if (tl - tn < block_size()) return error; + + for (;;) + { + char *d = ss.end() - ss.n; + do + { + * --d = * fn++; + ++ ss.n; + } + while (ss.n != word_size && fn != fl); + + if (ss.n == word_size) + { + memcpy( tn, d, block_size() ); + tn += block_size(); + ss.n = 0; + } + + if (fn == fl) return ok; + if (tl - tn < block_size()) return partial; + } + } + + result do_unshift(string_state &ss, char *tf, char *tl, char *&tn) const + { + tn = tf; + if (ss.n == 0) return noconv; + if (tl - tn < block_size()) return error; + + reinterpret_cast(tn[word_size]) = ss.n; + memcpy( tn, ss.end() - word_size, block_size() ); + tn += block_size(); + ss.n = 0; + return ok; + } + + result do_in(string_state &ss, char const *ff, char const *fl, + char const *&fn, char *tf, char *tl, char *&tn) const + { + fn = ff; + tn = tf; + + if (fl - fn < block_size()) return partial; + + for (;;) + { + size_t n_max = reinterpret_cast(fn[word_size]); + if (n_max <= 0 || n_max > word_size) return error; + do + { + * tn++ = fn[ word_size - ++ ss.n ]; + } + while (ss.n != n_max && tn != tl); + + if (ss.n == n_max) + { + fn += block_size(); + ss.n = 0; + } + + if (fn == fl) return ok; + if (fl - fn < block_size()) return partial; + if (tn == tl) return partial; + } + } + + int do_length(string_state &ss, char const *ff, char const *fl, + size_t tn) const + { + char const *fn = ff; + if (fl - fn < block_size()) return 0; + + for (;;) + { + size_t n_max = reinterpret_cast(fn[word_size]); + if (n_max <= 0 || n_max > word_size) return fn - ff; + int n = tn < n_max - ss.n? tn : n_max - ss.n; + ss.n += n; + tn -= n; + + if (ss.n == n_max) + { + fn += block_size(); + ss.n = 0; + } + if (tn == 0 || fl - fn < block_size()) return fn - ff; + } + } + + int + do_max_length() const throw() + { return block_size(); } + + int + do_encoding() const throw() + { return -1; } + + bool + do_always_noconv() const throw() + { return false; } + +public: + reverse_word_cvt(size_t n, size_t refs = 0) + : std::codecvt(refs), word_size(n) { } +}; + + +void test01() +{ + bool test __attribute__((unused)) = true; + + std::locale good_loc(std::locale(), + new std::codecvt); + std::locale evil_loc(std::locale(), new reverse_word_cvt(5)); + + std::basic_filebuf fb; + fb.pubsetbuf(0,0); + fb.open("imbue_1.tst", std::ios::in | std::ios::out | std::ios::trunc); + char const se[] = "encoded text", sne[] = "non-encoded text"; + + fb.pubimbue(good_loc); // uncommitted, invalid => noconv + fb.sputn(sne, sizeof sne); + fb.pubimbue(evil_loc); // write, noconv => conversion + fb.sputn(se, sizeof se); + std::fpos p = fb.pubseekoff(0, std::ios::cur); + fb.pubimbue(good_loc); // write, conversion => noconv + fb.sputn(sne, sizeof sne); + + fb.pubseekoff(sizeof sne, std::ios::beg); + + char buf[6]; + fb.pubimbue(evil_loc); // uncommitted, noconv => conversion + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, se, sizeof buf) == 0 ); + + p = fb.pubseekoff(0, std::ios::cur); + for (int i = 0; i < sizeof se - sizeof buf; ++i) fb.sbumpc(); + fb.pubimbue(good_loc); // read, conversion => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); + + fb.pubimbue(std::locale()); // read, conversion => invalid + fb.pubseekoff(0, std::ios::beg); // seek while invalid => uncommitted + + fb.pubimbue(evil_loc); // uncommitted, invalid => conversion + fb.pubseekpos(p); + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, se + sizeof buf, sizeof buf) == 0 ); + + for (int i = 0; i < sizeof se - 2 * sizeof buf; ++i) fb.sbumpc(); + fb.pubimbue(good_loc); // read, conversion => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); + + fb.pubseekoff(0, std::ios::beg); + fb.sbumpc(); + fb.sungetc(); + fb.pubimbue(std::locale()); // read+putback, noconv => invalid + fb.pubimbue(good_loc); // read, invalid => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); + + for (int i = 0; i < sizeof sne - sizeof buf; ++i) fb.sbumpc(); + fb.sbumpc(); + fb.sungetc(); + fb.pubimbue(std::locale()); // read+putback, noconv => invalid + fb.pubimbue(evil_loc); // read, invalid => conversion + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, se, sizeof buf) == 0 ); + + for (int i = 0; i < sizeof se - sizeof buf; ++i) fb.sbumpc(); + fb.pubimbue(std::locale()); // read, conversion => invalid + fb.pubimbue(good_loc); // read, invalid => noconv + fb.sgetn(buf, sizeof buf); + VERIFY( strncmp(buf, sne, sizeof buf) == 0 ); +} + +int main() +{ + test01(); + return 0; +} +