// File based streams -*- C++ -*-

// Copyright (C) 1997-1999 Cygnus Solutions
//
// 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 2, 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 COPYING.  If not, write to the Free
// Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.

// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.

//
// ISO C++ 14882: 27.8  File-based streams
//

#ifndef _CPP_BITS_FSTREAM_TCC
#define _CPP_BITS_FSTREAM_TCC 1

namespace std
{
  template<typename _CharT, typename _Traits>
    void
    basic_filebuf<_CharT, _Traits>::
    _M_init_filebuf(void)
    {
      _M_buf_unified = true; // Tie input to output for basic_streambuf.
      _M_fcvt = &use_facet<__codecvt_type>(this->getloc());
      _M_buf_size = static_cast<int_type>(BUFSIZ * sizeof(char_type)); 
      // _M_buf_size = 40 *  sizeof(char_type); // Debug overflow/underflow.
      try {
	_M_file = new __file_type;
      }
      catch(...) {
	delete _M_file;
	throw;
      }
    }

  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::
    basic_filebuf() 
    : __streambuf_type(), _M_last_overflowed(false),
    _M_state_cur(), _M_state_beg()
    { _M_init_filebuf(); }

  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::
    basic_filebuf(int __fd, const char* __name, ios_base::openmode __mode)
    : __streambuf_type(), _M_last_overflowed(false),
    _M_state_cur(), _M_state_beg()
    {
      _M_init_filebuf();
      _M_file->sys_open(__fd, __mode);

      if (this->is_open() && _M_buf_size)
	{
	  _M_mode = __mode;
	  
	  try {
	    _M_buf = new char_type[_M_buf_size];
	  }
	  catch(...) {
	    delete [] _M_buf;
	    throw;
	  }
	  
	  // For time being, set both (in/out) sets  of pointers.
	  this->_M_set_determinate(_M_buf_size);
	}
   }

  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::__filebuf_type* 
    basic_filebuf<_CharT, _Traits>::
    open(const char* __s, ios_base::openmode __mode)
    {
      __filebuf_type *__retval = NULL;
      if (! this->is_open())
	{
	  _M_file->open(__s, __mode);
	  
	  if (this->is_open() && __mode & ios_base::ate)
	    {
	      if (seekoff(0, ios_base::end, __mode) < 0 )
		this->close();
	    }
	  
	  if (this->is_open() && _M_buf_size)
	    {
	      _M_mode = __mode;
	      
	      try {
		_M_buf = new char_type[_M_buf_size];
		}
	      catch(...) {
		delete [] _M_buf;
		throw;
	      }
	      
	      // For time being, set both (in/out) sets  of pointers.
	      this->_M_set_indeterminate();
	      __retval = this;
	    }
	}
      return __retval;
    }

  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::__filebuf_type* 
    basic_filebuf<_CharT, _Traits>::close()
    {
      __filebuf_type *__retval = NULL;
      if (this->is_open())
	{
	  bool __testput = _M_out_cur && _M_out_beg < _M_out_cur;
	  if (__testput)
	    _M_really_overflow(traits_type::eof());
	  
	  if (_M_last_overflowed)
	    {
	      _M_output_unshift();
	      _M_really_overflow(traits_type::eof());
	    }
	  
	  if (_M_file->close())
	    {
	      _M_mode = ios_base::openmode(0);
	      if (_M_buf_size)
		delete [] _M_buf;
	      _M_buf = NULL;
	      this->setg(NULL, NULL, NULL);
	      this->setp(NULL, NULL);

	      __retval = this;
	    }
	}
      return __retval;
    }

  template<typename _CharT, typename _Traits>
    streamsize 
    basic_filebuf<_CharT, _Traits>::showmanyc()
    {
      streamsize __retval = -1;
      if (_M_in_cur && _M_in_cur < _M_in_end)
	__retval = (_M_in_end - _M_in_cur) / sizeof(char_type);
      if (_M_is_indeterminate())
	{
	  bool __testeof = this->underflow() == traits_type::eof();
	  if (!__testeof && _M_in_cur && _M_in_cur < _M_in_end)
	    __retval = (_M_in_end - _M_in_cur) / sizeof(char_type);
	}
      _M_last_overflowed = false;	
      return __retval;
    }

  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::int_type 
    basic_filebuf<_CharT, _Traits>::underflow()
    {
      int_type __retval = traits_type::eof();
      bool __testpos = _M_in_cur && _M_in_cur >= _M_in_end;
      bool __testinit = _M_is_indeterminate();
      bool __testinout = _M_mode & ios_base::in && _M_mode & ios_base::out;
      
      if (__testinit || __testpos)
	{
	  // Part one: (Re)fill external buf (_M_file->_IO_*) from
	  // external byte sequence (whatever physical byte sink or
	  // FILE actually is.)
	  if (__testinout && __testpos && !__testinit)
	    {
	      // Write out buffer, then underflow.
	      this->overflow();
	      _M_set_indeterminate();
	    }
	  
	  char __conv_buf[_M_buf_size];
	  streamsize __size = _M_file->xsgetn(__conv_buf, _M_buf_size);
	  
	  // Part two: (Re)fill internal buf contents from external buf.
	  if (0 < __size && __size <= _M_buf_size)
	    {
	      _M_set_determinate(__size);
	      
	      char* __conv_cur = __conv_buf;
	      _M_state_beg = _M_state_cur;
	      __res_type __r = _M_fcvt->in(_M_state_cur, 
					__conv_buf,
					__conv_buf + __size,
					const_cast<const char*&>(__conv_cur), 
					_M_in_beg, _M_in_end, _M_in_cur);
	      
	      if (__r == codecvt_base::partial)
		{
		  // XXX Retry with larger _M_buf size.
		}
	      
	      // Set pointers to internal and external buffers correctly. . .
	      if (__r != codecvt_base::error)
		{
		  if (_M_mode & ios_base::out)
		    _M_out_cur = _M_in_cur;
		  __retval = traits_type::to_int_type(*_M_in_cur);
		}
	    }
	  
	  // Part three: Sync the current internal buffer position
	  // with the (now overshot) external buffer position.
	  if (__testinout && 0 < __size && __size <= _M_buf_size)
	    {
	      off_type __sync_off = 0 - __size;
	      off_type __fail = _M_file->seekoff(__sync_off, 
						 ios_base::cur, _M_mode);
	      if (__fail != __sync_off)
		{
		  // XXX Do error checking.
		}
	    }
	  
	}	      
      _M_last_overflowed = false;	
      return __retval;
    }
  
  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::int_type 
    basic_filebuf<_CharT, _Traits>::uflow()
    {
      int_type __retval = this->underflow();
      bool __testeof = __retval == traits_type::eof();
      bool __testpending = _M_in_cur && _M_in_cur < _M_in_end;
      
      if (!__testeof && __testpending)
	{
	  ++_M_in_cur;
	  if (_M_out_cur)
	    ++_M_out_cur;
	}
      _M_last_overflowed = false;	
      return __retval;    
    }
   
  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::int_type 
    basic_filebuf<_CharT, _Traits>::pbackfail(int_type __c)
    {
      int_type __retval = traits_type::eof();
      bool __testeof = traits_type::eq_int_type(__c, traits_type::eof());
      bool __testeq = _M_in_cur && 
	traits_type::eq(traits_type::to_char_type(__c), this->gptr()[-1]);
      bool __testpb = _M_in_cur && _M_in_beg < _M_in_cur;
      
      // Try to put back __c into input sequence in one of three ways.
      // Order these tests done in is unspecified by the standard.
      if (!__testeof && __testpb && __testeq)
	{
	  --_M_in_cur;
	  if (_M_mode & ios_base::out)
	    --_M_out_cur;
	  __retval = __c;
	}
      else if (!__testeof && __testpb && _M_out_cur)
	{
	  --_M_in_cur;
	  if (_M_mode & ios_base::out)
	    --_M_out_cur;
	  *_M_in_cur = __c;
	  __retval = __c;
	}
      else if (__testeof && __testpb)
	{
	  --_M_in_cur;
	  if (_M_mode & ios_base::out)
	    --_M_out_cur;
	  __retval = traits_type::not_eof(__c);
	}
      
      _M_last_overflowed = false;	
      return __retval;
    }

  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>::int_type 
    basic_filebuf<_CharT, _Traits>::overflow(int_type __c)
    {
      int_type __retval = traits_type::eof();
      bool __testpos = _M_out_cur && _M_out_cur >= _M_out_end;
      bool __testinit = _M_is_indeterminate();
      bool __testin = _M_mode & ios_base::in;
      bool __testout = _M_mode & ios_base::out;
      
      if (__testout)
	{
	  if (__testinit)
	    {
	      // May need to refill buffer.
	      if (__testin)
		{
		  // If still indeterminate after the call to
		  // underflow, this means that the input file is
		  // empty, and so now as a special case we can max
		  // out the buffers and treat this as an output file
		  // for all current intents and purposes.
		  this->underflow(); 
		  if (_M_is_indeterminate())
		    {
		      _M_out_end = _M_buf + _M_buf_size;
		      _M_in_end = _M_out_end;
		    }
		  ++_M_in_cur;
		}
	      else 
		_M_out_end = _M_buf + _M_buf_size;

	      *_M_out_cur = traits_type::to_char_type(__c);
	      ++_M_out_cur;
	      __retval = traits_type::not_eof(__c);
	    }
	  else if (__testpos)
	      this->_M_really_overflow(__c);
	}

      _M_last_overflowed = false;    // Set in _M_really_overflow, below.
      return __retval;
    }
  
  template<typename _CharT, typename _Traits>
    basic_filebuf<_CharT, _Traits>:: int_type 
    basic_filebuf<_CharT, _Traits>::
    _M_really_overflow(int_type __c)
    {
      int_type __retval = traits_type::eof();
      bool __testput = _M_out_cur && _M_out_beg < _M_out_cur;
      bool __testeof = traits_type::eq_int_type(__c, traits_type::eof());
      
      if (__testput)
	{
	  // Part one: Allocate temporary conversion buffer on
	  // stack. Convert internal buffer plus __c (ie,
	  // "pending sequence") to temporary conversion buffer.
	  int __plen = _M_out_cur - _M_out_beg;
	  if (!__testeof)
	    __plen += sizeof(char_type);	
	  char_type __pbuf[__plen];	      
	  if (! __testeof)
	    {
	      traits_type::copy(__pbuf, this->pbase(), 
				__plen - sizeof(char_type));
	      __pbuf[__plen - 1] = traits_type::to_char_type(__c);
	    }
	  else
	    traits_type::copy(__pbuf, this->pbase(), __plen);
	  char_type* __pend;
	  
	  char __conv_buf[__plen];
	  char* __conv_end;
	  
	  _M_state_beg = _M_state_cur;
	  __res_type __r = _M_fcvt->out(_M_state_cur, 
					__pbuf, __pbuf + __plen,
					const_cast<const char_type*&>(__pend),
					__conv_buf, __conv_buf + __plen,
					__conv_end);
	  
	  // Part two: (Re)spill converted "pending sequence"
	  // contents (now in temporary conversion buffer) to
	  // external buffer (_M_file->_IO_*) using
	  // _M_file->sys_write(), and do error (minimal) checking.
	  if (__r != codecvt_base::error)
	    {
	      streamsize __r = _M_file->xsputn(__conv_buf, __plen);
	      if (__r == __plen)
		{
		  _M_set_indeterminate();
		  __retval = traits_type::not_eof(__c);
		}
	    }
	}	      
      _M_last_overflowed = true;	
      return __retval;
    }

   template<typename _CharT, typename _Traits>
     basic_filebuf<_CharT, _Traits>::pos_type
    basic_filebuf<_CharT, _Traits>::
    seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __mode)
     {
       pos_type __retval =  pos_type(off_type(-1)); 
       int __width = _M_fcvt->encoding();
       bool __testfail = __off != 0  && __width <= 0;
       bool __testopen = this->is_open();
       
       if (__testopen && !__testfail)
	 {
	   off_type __computed_off = 0;
	   bool __testput = _M_out_cur && _M_out_beg < _M_out_cur;
	   bool __testlastout = _M_last_overflowed || __testput;
	   
	   if (__testlastout && (__way != ios_base::cur || __off != 0))
	     {
	       // Part one: update the output sequence.
	       _M_really_overflow(*_M_out_cur);
	       
	       // Part two: output unshift sequence.
	       _M_output_unshift();
	     }
	   
	   if (__width < 0)
	     __width = 0;
	   __computed_off = __width * __off;
	   off_type __pos = _M_file->seekoff(__computed_off, __way, __mode);
	   if (__pos != -1)
	     {
	       _M_set_indeterminate();
	       this->underflow();
	       __retval = pos_type(__pos);
	     }
	 }
       
       _M_last_overflowed = false;	
       return __retval;
     }

  template<typename _CharT, typename _Traits>
    void 
    basic_filebuf<_CharT, _Traits>::_M_output_unshift()
      {
#if 0
	// XXX Not complete, or correct.
	int __width = _M_fcvt->encoding();

	if (__width < 0)
	  {
	    // Part one: call codecvt::unshift
	    int __unsft_len = 0;
	    char_type __unsft_buf[_M_buf_size];
	    char_type* __unsft_cur; // XXX Set to external buf.
	    _M_state_beg = _M_state_cur;
	    __res_type __r = _M_fcvt->unshift(_M_state_cur, 
					      __unsft_buf,
					      __unsft_buf + _M_buf_size,
					      __unsft_cur);
	    
	    // Note, for char_type == char, wchar_t unshift
	    // should store no charachers.
	    if (__r == codecvt_base::ok || __r == codecvt_base::noconv)
	      __unsft_len = __unsft_cur - __unsft_buf;
		    
	    // "Output the resulting sequence."
	    if (__unsft_len)
	      {
		int __plen = _M_out_cur - _M_out_beg;
		int __rlen = __plen  + __unsft_len;
		char_type __rbuf[__rlen];
		char_type* __rend;
		traits_type::copy(__rbuf, this->pbase(), __plen);
		traits_type::copy(__rbuf + __plen, __unsft_buf, 
				  __unsft_len);

		char __conv_buf[__rlen];
		char* __conv_end;
		
		_M_state_beg = _M_state_cur; // XXX Needed?
		__r = _M_fcvt->out(_M_state_cur, 
				  __rbuf, __rbuf + __rlen,
				  const_cast<const char_type*&>(__rend),
				  __conv_buf, 
				  __conv_buf + __rlen,
				  __conv_end);
		
		if (__r != codecvt_base::error)
		  {
		    streamsize __r = _M_file->xsputn(__conv_buf, 
						     __rlen);
		    if (__r == __rlen)
		      {
			_M_out_cur = _M_out_beg;
			if (_M_mode & ios_base::in)
			  _M_in_cur = _M_out_cur;
		      }
		    else
		      {
			// XXX Throw "wig out and die exception?"
		      }
		  }
	      }
	  }
#endif
      }

  template<typename _CharT, typename _Traits>
    void
    basic_filebuf<_CharT, _Traits>::imbue(const locale& __loc)
    {
      bool __testbeg = gptr() == eback() && pptr() == pbase();
      bool __teststate = _M_fcvt->encoding() == -1;
      
      _M_initialized = true;
      if (__testbeg && !__teststate && _M_locale != __loc)
	{
	  // XXX Will need to save these older values.
	  _M_locale = __loc;
	  _M_fcvt = &use_facet<__codecvt_type>(_M_locale);
	  _M_fctype = &use_facet<__ctype_type>(_M_locale); // XXX Necessary?
	}
      // NB this may require the reconversion of previously
      // converted chars. This in turn may cause the reconstruction
      // of the original file. YIKES!!
      // XXX The part in the above comment is not done.
      _M_last_overflowed = false;	
    }
  
} // namespace std

#endif // _CPP_BITS_FSTREAM_TCC










