/*ident	"@(#)cls4:lib/stream/streambuf.c	1.1" */
/*******************************************************************************
 
C++ source for the C++ Language System, Release 3.0.  This product
is a new release of the original cfront developed in the computer
science research center of AT&T Bell Laboratories.

Copyright (c) 1991 AT&T and UNIX System Laboratories, Inc.
Copyright (c) 1984, 1989, 1990 AT&T.  All Rights Reserved.

THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System
Laboratories, Inc.  The copyright notice above does not evidence
any actual or intended publication of such source code.

*******************************************************************************/

#include <iostream.h>
#include "streamdefs.h"
#include <string.h>
#include <memory.h>

/*
	Allocate some space for the buffer.
	Returns:	EOF on error
			0 on success
*/
int streambuf::doallocate()
{
	char *buf = new char[STREAMBUFSIZE] ;
	if ( !buf ) return EOF ;
	setb(buf,buf+STREAMBUFSIZE,1) ;
	return 0;
}

/*
	Come here on a put to a full buffer.  Allocate the buffer if 
	it is uninitialized.
	Returns:	EOF on error
			the argument on success
*/
int streambuf::overflow(int c)
{
	if ( c==EOF ) 			return zapeof(c) ;
	if ( allocate() == EOF)		return EOF;

	if ( x_pptr <= x_epptr )  	return sputc(c) ;
	else				return EOF ;
}

/*
	Fill a buffer.
	Returns:	EOF on error or end of input
			next character on success
*/
int streambuf::underflow()
{
	if ( x_pptr > x_egptr ) setg(x_eback,x_gptr,x_pptr) ;

	if ( x_egptr > x_gptr )	return 0 ;
	else 		    	return EOF ;
}

int streambuf::pbackfail(int)
{
	return EOF;
}

int streambuf::sync()
{
	// It's unclear exactly what this should do.  Should it reset
	// the buffer or what.  One theory (that used to be in the code.
	// was that it should insert a 0.  Which seems to be the
	// right thing for "strings".
	// if ( x_pptr && x_epptr > x_pptr )  sputc(0) ;
	return EOF ;
}


streampos streambuf::seekpos(streampos p, int m)
{
	return seekoff(p, ios::beg, m) ;
	}

streampos streambuf::seekoff(streampos,ios::seek_dir,int) 
{
	return EOF ;
}

int streambuf::xsputn(register const char* s, int n)
{
	register int req = n ;
	if ( unbuffered() ) {
		while( req-- > 0 ) {
			if ( sputc(*s++) == EOF ) return n-req-1 ;
		}
		return n ;
	}
	register int avail = x_epptr-x_pptr ;
	while ( avail < req ) {
		memcpy(x_pptr,s,avail) ;
		s += avail ;
		pbump(avail) ;
		req -= avail ;
		if ( overflow(zapeof(*s++)) == EOF ) return n-req ;
		--req ;
		avail = x_epptr-x_pptr ;
	}
	memcpy(x_pptr,s,req ) ;
	pbump(req) ;
	return n ;
}

int streambuf::xsgetn(register char* s, int n)
{
	register char* p = s ;
	register int req = n ;
	if (  unbuffered()  ) {
		while (req-- > 0 ) {
			register int c ;
			if ( (c=sbumpc() ) != EOF )  *p++ = c ;
			else 			return p-s ;
		}
	}

	if ( req <= 0 ) return 0 ;

	register int avail = x_egptr-x_gptr ;
	while (  avail < req ) {
		memcpy(p,x_gptr,avail) ;
		p += avail ; 
		req -= avail ;
		gbump(avail) ;
		if ( underflow()==EOF ) return p-s ;
		avail = x_egptr-x_gptr ;
		}

	memcpy(p,x_gptr,req) ;
	gbump(req) ;
	return n ;
}


streambuf* streambuf::setbuf(char* p , int len)
{

	if ( x_base ) return 0 ;
	if ( len <= 0 || p == 0 ) {
		// make it unbuffered
		setb(0,0,0) ;
		setg(0,0,0) ;
		setp(0,0);
		unbuffered(1) ; 
		}
	else {
		setb(p,p+len,0) ;
		setg(p,p,p) ;
		setp(p,p+len) ;
		unbuffered(0) ;
		} 
	return this;
}

streambuf* streambuf::setbuf(unsigned char* p, int len)
{
	return setbuf((char*)p,len) ;
}

streambuf::streambuf() :
	x_unbuf(0), alloc(0)
{
	setb(0,0,0);
	setg(0,0,0);
	setp(0,0);
}

streambuf::streambuf(char* p, int l) :
	x_unbuf(0), alloc(0)
{
	setb(0,0,0);
	setbuf(p,l) ;
}

streambuf::~streambuf() 
{
	sync() ;
	if (x_base && alloc) delete x_base;
	}

int streambuf::x_snextc()
{
	// called by snextc to handle overflow
	if ( x_egptr==0 || x_gptr != x_egptr ) {
		// we stepped beyond x_gptr meaning snextc was called when
		// x_gtr == x_egptr rather than when x_gptr+1=x_egptr.
		underflow() ;
		pbump(1) ;
		}
	return sgetc() ;
	}
