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