/***
*streamb.cxx - fuctions for streambuf class.
*
*	Copyright (c) 1990-1992, Microsoft Corporation.  All rights reserved.
*
*Purpose:
*	Functions for streambuf class.
*
*Revision History:
*	09-10-90  WAJ	Initial version.
*	07-02-91  KRS	Initial version completed.
*	08-20-91  KRS	Treat chars as unsigned; fix sgetn() function.
*	09-06-91  KRS	Do a sync() in ~streambuf before destroying buffer.
*	11-18-91  KRS	Split off stream1.cxx for input-specific code.
*	12-09-91  KRS	Fix up xsputn/xsgetn usage.
*	03-03-92  KRS	Added mthread lock init calls to constructors.
*	06-02-92  KRS	CAV #1745: Don't confuse 0xFF with EOF in xsputn()
*			call to overflow().
*
*******************************************************************************/

#include <cruntime.h>
#include <internal.h>
#include <string.h>
#include <stdlib.h>
#include <iostream.h>
#pragma hdrstop


#ifndef BUFSIZ
#define BUFSIZ 512
#endif

/***
*streambuf::streambuf() -
*
*Purpose:
*	Default constructor.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

streambuf::streambuf()
{
    _fAlloc = 0;
    _fUnbuf = 0;
    x_lastc = EOF;
    _base = NULL;
    _ebuf = NULL;
    _pbase = NULL;
    _pptr = NULL;
    _epptr = NULL;
    _eback = NULL;
    _gptr = NULL;
    _egptr = NULL;

#ifdef MTHREAD
    LockFlg = 1;		// default is no locking
    _mtlockinit(lockptr());
#endif  /* MTHREAD */

}

/***
*streambuf::streambuf(char* pBuf, int cbBuf) -
*
*Purpose:
*	Constructor which specifies a buffer area.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

streambuf::streambuf( char* pBuf, int cbBuf )
{
    _fAlloc = 0;
    _fUnbuf = 0;
    x_lastc = EOF;
    _base = pBuf;
    _ebuf = pBuf + (unsigned)cbBuf;
    _pbase = NULL;
    _pptr = NULL;
    _epptr = NULL;
    _eback = NULL;
    _gptr = NULL;
    _egptr = NULL;

    if( pBuf == NULL || cbBuf == 0 ){
	_fUnbuf = 1;
	_base = NULL;
	_ebuf = NULL;
    }

#ifdef MTHREAD
    LockFlg = 1;		// default is no locking
    _mtlockinit(lockptr());
#endif  /* MTHREAD */

}


/***
*virtual streambuf::~streambuf() -
*
*Purpose:
*	Destructor.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

streambuf::~streambuf()
{
    lock();		// make sure no one else still using it
//    x_lastc = EOF;
    sync();	// make sure buffer empty before possibly destroying it
    if( (_fAlloc) && (_base) )
	delete _base;
}


/***
* virtual streambuf * streambuf::setbuf(char * p, int len) -
*
*Purpose:
*	Offers the array at p with len bytes to be used as a reserve area.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

streambuf * streambuf::setbuf(char * p, int len)
{
    if (!_base)
	{
	if ((!p) || (!len))
	    _fUnbuf = 1;	// mark as unbuffered
	else
	    {
	    _base = p;
	    _ebuf = p + (unsigned)len;
	    _fUnbuf = 0;
	    }
	return (this);
	}
    return((streambuf *)NULL);
}


/***
*virtual int streambuf::xsputn( char* pBuf, int cbBuf ) -
*
*Purpose:
*	Tries to output cbBuf characters.  Returns number of characters
*	that were outputted.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int streambuf::xsputn( const char* pBuf, int cbBuf )
{
    int	cbOut;
#if 0
    if (!this)
	printf("xsputn: this = NULL\n");
#endif
    for (cbOut = 0; cbBuf--; cbOut++)
	{
	if ((_fUnbuf) || (_pptr >= _epptr))
	    {
// UNDONE: can be optimized like sgetn !!
	    if (overflow((unsigned char)*pBuf)==EOF)	// 0-extend 0xFF !=EOF
		break;
	    }
	else
	    {
	    *(_pptr++) = *pBuf;
	    }
	pBuf++;
	}
    return cbOut;
}

/***
*virtual int streambuf::xsgetn( char* pBuf, int cbBuf ) -
*
*Purpose:
*	Tries to input cbBuf characters.  Returns number of characters
*	that were read from streambuf.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

// #pragma intrinsic(memcpy,__min)

int streambuf::xsgetn( char * pBuf, int cbBuf)
{
    int count;
    int cbIn = 0;
    if (_fUnbuf)
	{
	if (x_lastc==EOF)
	    x_lastc=underflow();
		
	while (cbBuf--)
	    {
	    if (x_lastc==EOF) 
		break;
	    *(pBuf++) = (char)x_lastc;
	    cbIn++;
	    x_lastc=underflow();
	    }
	}
    else
	{
	while (cbBuf)
	    {
	    if (underflow()==EOF)	// make sure something to read
		break;
	    count = __min(egptr() - gptr(),cbBuf);
	    if (count>0)
		{
	        memcpy(pBuf,gptr(),count);
		pBuf  += count;
		_gptr += count;
		cbIn  += count;
		cbBuf -= count;
		}
	    }
	}
    return cbIn;
}
// #pragma function(memcpy, __min)

/***
*virtual int streambuf::sync() -
*
*Purpose:
*	Tries to flush all data in put area and give back any data in the
*	get area (if possible), leaving both areas empty on exit.
*	Default behavior is to fail unless buffers empty.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int streambuf::sync()
{
    if ((gptr() <_egptr) || (_pptr > _pbase))
	{
	return EOF;
	}
    return 0;
}

#if 0
/***
*int streambuf::sputn( char* pBuf, int cbBuf ) -
*
*Purpose:
*	Tries to output cbBuf characters.  Returns number of characters
*	that were output.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int streambuf::sputn( const char* pBuf, int cbBuf )
{
// UNDONE:
	return xsputn(pBuf, cbBuf);
}

/***
*int streambuf::sgetn( char* pBuf, int cbBuf ) -
*
*Purpose:
*	Tries to input cbBuf characters.  Returns number of characters
*	that were read from streambuf.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int streambuf::sgetn( char * pBuf, int cbBuf)
{
// UNDONE:
	return xsgetn(pBuf, cbBuf);
}
#endif

/***
*int streambuf::allocate() -
*
*Purpose:
*	Tries to set up a Reserve Area.  If one already exists, or if
*	unbuffered, just returns 0.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int streambuf::allocate()
{
    if ((_fUnbuf) || (_base))
	return 0;
    if (doallocate()==EOF) return EOF;

    return(1);
}

/***
*virtual int streambuf::doallocate() -
*
*Purpose:
*	Tries to set up a Reserve Area.  Returns EOF if unsuccessful.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

int streambuf::doallocate()
{
    char * tptr;
    if (!( tptr = new char[BUFSIZ]))
	return(EOF);
    setb(tptr, tptr + BUFSIZ, 1);
    return(1);
}

/***
*void streambuf::unbuffered(int fUnbuf) -
*
*Purpose:
*	Sets unbuffered status.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

// void streambuf::unbuffered(int fUnbuf)
// {
//    _fUnbuf = fUnbuf;
// if ((_fUnbuf) && (_base)) delete _base;	// UNDONE: not doc'd as such ??
// }

/***
*void streambuf::setb(char * b, char * eb, int a = 0) -
*
*Purpose:
*	Sets up reserve area.
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

void streambuf::setb(char * b, char * eb, int a )
{
    if ((_fAlloc) && (_base)) delete _base;
    _base = b;
    _fAlloc = a;
    _ebuf = eb;
}

#if 0
int streambuf::overflow(int d)
{
     printf("Error: *%p streambuf::overflow(%d) called!\n",this,d);
     return EOF;
}

int streambuf::underflow()
{
     printf("Error: *%p streambuf::underflow() called!\n",this);
     return EOF;
}
#endif

/***
*virtual streampos streambuf::seekoff(streamoff off, ios::seekdir dir, int mode)
*
*Purpose:
*	seekoff member function.  seek forward or backward in the stream.
*	Default behavior: returns EOF.
*
*Entry:
*	off  = offset (+ or -) to seek by
*	dir  = one of ios::beg, ios::end, or ios::cur.
*	mode = ios::in or ios::out.
*
*Exit:
*	Returns new file position or EOF if error or seeking not supported.
*
*Exceptions:
*	Returns EOF if error.
*
*******************************************************************************/
streampos streambuf::seekoff(streamoff,ios::seek_dir,int)
{
return EOF;
}

/***
*virtual streampos streambuf::seekpos(streampos pos, int mode) -
*
*Purpose:
*	seekoff member function.  seek to absolute file position.
*	Default behavior: returns seekoff(streamoff(pos), ios::beg, mode).
*
*Entry:
*	pos  = absolute offset to seek to
*	mode = ios::in or ios::out.
*
*Exit:
*	Returns new file position or EOF if error or seeking not supported.
*
*Exceptions:
*	Returns EOF if error.
*
*******************************************************************************/
streampos streambuf::seekpos(streampos pos,int mode)
{
return seekoff(streamoff(pos), ios::beg, mode);
}

/***
*virtual int streambuf::pbackfail(int c) - handle failure of putback
*
*Purpose:
*	pbackfail member function.  Handle exception of pback function.
*	Default behavior: returns EOF.  See spec. for details.
*
*	Note: the following implementation gives default behavior, thanks
*	to the default seekoff, but also supports derived classes properly:
*
*Entry:
*	c = character to put back
*
*Exit:
*	Returns c if successful or EOF on error.
*
*Exceptions:
*	Returns EOF if error.  Behavior is undefined if c was not the
*	previous character in the stream.
*
*******************************************************************************/
int streambuf::pbackfail(int c)
{
    if (eback()<gptr()) return sputbackc((char)c); // CONSIDER: should never happen

    if (seekoff( -1, ios::cur, ios::in)==EOF)  // always EOF for streambufs
	return EOF;
    if (!unbuffered() && egptr())
	{
	memmove((gptr()+1),gptr(),(egptr()-(gptr()+1)));
	*gptr()=(char)c;
	}
    return(c);
}