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<typename _CharT, typename _Traits>
-    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<char*>(__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<char*>(this->gptr()) : _M_ext_end - __off;
+	      char *__dst_buf = __cvt && __cvt->always_noconv()?
+		    reinterpret_cast<char*>(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<char*>(_M_buf) : NULL;
+		  _M_buf = reinterpret_cast<char_type*>(__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,311 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-require-fileio "" }
+
+#include <locale>
+#include <fstream>
+#include <cstring>
+#include <iostream>
+//#include <testsuite_hooks.h>
+#define VERIFY(x)
+
+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<char>
+{
+  typedef string_state state_type;
+  typedef std::fpos<string_state> pos_type;
+};
+
+
+namespace std
+{
+  template<>
+  class codecvt<char, char, string_state>
+  : 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<char, char, string_state>::id;
+}
+
+
+class reverse_word_cvt : public std::codecvt<char, char, string_state>
+{
+  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<size_t &>(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<size_t const &>(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<size_t const &>(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<char, char, string_state>(refs), word_size(n) { }
+};
+
+
+void test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  std::locale good_loc(std::locale(),
+		       new std::codecvt<char, char, string_state>);
+  std::locale evil_loc(std::locale(), new reverse_word_cvt(5));
+  
+  std::basic_filebuf<char, string_state_traits> 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<string_state> 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,312 @@
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-require-fileio "" }
+
+#include <locale>
+#include <fstream>
+#include <cstring>
+#include <iostream>
+//#include <testsuite_hooks.h>
+#define VERIFY(x)
+
+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<char>
+{
+  typedef string_state state_type;
+  typedef std::fpos<string_state> pos_type;
+};
+
+
+namespace std
+{
+  template<>
+  class codecvt<char, char, string_state>
+  : 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<char, char, string_state>::id;
+}
+
+
+class reverse_word_cvt : public std::codecvt<char, char, string_state>
+{
+  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<size_t &>(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<size_t const &>(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<size_t const &>(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<char, char, string_state>(refs), word_size(n) { }
+};
+
+
+void test01()
+{
+  bool test __attribute__((unused)) = true;
+
+  std::locale good_loc(std::locale(),
+		       new std::codecvt<char, char, string_state>);
+  std::locale evil_loc(std::locale(), new reverse_word_cvt(5));
+  
+  std::basic_filebuf<char, string_state_traits> 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<string_state> 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;
+}
+
