/*ident	"@(#)cls4:lib/stream/strstream.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 <string.h>
#include <strstream.h>

typedef unsigned int Size_t ;	
			// Some day <memory.h> will be ANSIfied and
			// this will be declared there.
			// Until then make it compatible with declaration
			// of third argument of memcpy.

static const int initsize = 16*sizeof(int) ;
static const int increment = 2 ; 	

static const int arbitrary = 1024 ;	// used by ignore_oflow.

static const int ios_atend = ios::ate ;
static const int ios_input = ios::in ;
static const int ios_output = ios::out ;
static const int ios_append = ios::app ;
static const int seek_cur = ios::cur ;
static const int seek_end = ios::end ;
static const int seek_beg = ios::beg ;

strstreambuf::strstreambuf(void* (*a)(long), void (*f)(void*)) :
	afct(a),
	ffct(f),
	froozen(1),
	auto_extend(initsize),
	ignore_oflow(0)
{
	}

void strstreambuf::init(char* b, int size, char* pstart)
{
	if ( b && size > 0 ) {
		setb(b,b+size) ;
		}
	else if ( b && size == 0 ) {
		setb(b,b+strlen(b) ) ;
		}
	else if ( b && size < 0 ) {
		ignore_oflow = 1 ;
		setb(b,b+arbitrary) ;
		}
	else if ( !b && size > initsize ) {
		auto_extend = size ;
		setb(0,0) ;
		}
	else {
		setb(0,0) ;
		}

	if ( pstart && base() && base() <= pstart && pstart <= ebuf() ) {
		setp(pstart,ebuf()) ;
		}
	else {
		setp(0,0) ;
		}
	
	if ( base() ) {
		if (pstart) setg(base(),base(),pstart);
		else setg(base(),base(),ebuf()) ;
		auto_extend = 0 ;
		froozen = 1 ;
		}
	else {
		setg(0,0,0) ;
		}
	}

strstreambuf::strstreambuf(char* b, int size, char* pstart) :
	afct(0),
	ffct(0),
	froozen(0),
	auto_extend(0),
	ignore_oflow(0)
{
	init(b,size,pstart) ; 
	}

strstreambuf::strstreambuf(unsigned char* b, int size, unsigned char* pstart) :
	afct(0),
	ffct(0),
	froozen(0),
	auto_extend(0),
	ignore_oflow(0)
{
	init((char*)b,size,(char*)pstart) ; 
	}


strstreambuf::strstreambuf(int ae) :
	afct(0),
	ffct(0),
	froozen(1),
	auto_extend(ae>0?ae:initsize),
	ignore_oflow(0)
{
	}

strstreambuf::strstreambuf() :
	afct(0),
	ffct(0),
	froozen(1),
	auto_extend(initsize),
	ignore_oflow(0)
{
	}


int strstreambuf::doallocate()
{
	if ( auto_extend < initsize ) auto_extend = initsize ;
	char* newspace  = afct ? (char*)(*afct)(auto_extend) 
			       : new char[auto_extend] ;

	if ( !newspace ) return EOF ;

	froozen = 0 ;
	setb(newspace,newspace+auto_extend,0) ;
			// Will free this space ourselves if neccessary

	setp(newspace,newspace+auto_extend) ;
	setg(newspace,newspace,newspace) ;

	return 0 ;
	}

int strstreambuf::overflow(int c)
{
	if ( !base() ) {
		allocate() ;
		if ( !base() ) return EOF ;
		}
	else if ( epptr() > pptr() + 1 ) {
		// There is space already available so we
		// don't have to do anything.  This is presumably
		// triggered by an explicit flush or something like
		// that. 
		}
	else if ( auto_extend && !froozen ) {
		Size_t inserted=pptr()-base() ; // number of chars previously
						// inserted into buffer

		Size_t extracted=gptr()-base() ;
						// number of chars previously
						// extracted from buffer

		// after we copy chars from current space to a new
		// (larger) area we have to adjust pointers to take into
		// acount previous activities.

		Size_t newsize = (Size_t)increment*blen() + 4 ;
		if ( newsize < auto_extend ) newsize = auto_extend ;
		char* newspace = afct ? (char*)(*afct)(newsize) 
			     	      : new char[newsize] ;

		if ( !newspace ) return EOF ;

		memcpy(newspace,base(),inserted) ;

		if ( ffct )	(*ffct)(base()) ;
		else		delete base() ;

		setb(newspace,newspace+newsize,0) ;
		setp(base()+inserted,ebuf()) ;
		setg(base(),base()+extracted,pptr()) ;
		}
	else if ( ignore_oflow ) {
		setp(pptr(),pptr()+arbitrary) ; 
		}
	else {
		return EOF ;
		}
	if ( c!=EOF ) return sputc(c) ;
	}

int strstreambuf::underflow()
{
	if ( !pptr() ) return EOF ;
	if ( !egptr() ) return EOF ;

	setg(base(),egptr(),pptr()) ;
	
	if ( egptr() <= gptr() )	return EOF ;
	else				return *gptr() ;
	}

void strstreambuf::freeze(int n) 
{
	froozen = n ;
	}


char* strstreambuf::str() 
{
	sputc(0);
	freeze() ;
	return base() ;
	}

strstreambuf::~strstreambuf()
{
	if ( !froozen && base() ) {
		if ( ffct ) 	ffct(base()) ;
		else		delete base() ;
		}
	}

streambuf* strstreambuf::setbuf(char* p, int size) 
{
	if ( p == 0 && ( base()==0 || auto_extend ) ) {
		auto_extend = size ;
		return this ;
		}
	else {
		return 0 ;
		}
	}

streampos strstreambuf::seekoff(streamoff o, ios::seek_dir d, int m)
{
	switch (d) {
	case seek_beg : break ;
	case seek_cur : {
		if ( (m&ios_input) ) {
			o += gptr()-base() ;
			}
		else  {
			o += pptr()-base() ;
			}
		} break ;
	case seek_end : {
		if ( gptr()<egptr() && egptr()>pptr() ) {
			o += egptr()-base() ;
			}
		else if ( pptr()<epptr() ) {
			o += pptr()-base();
			}
		} break ;
	    } // end switch
	if ( o < 0 ) return streampos(EOF) ;
	if ( o >= blen() && !ignore_oflow ) return streampos(EOF) ;
	if ( m&ios_input ) setg(base(), base()+o, egptr() ) ;
	if ( m&ios_output) setp(base()+o, epptr() ) ;
	return o ;
	}

strstreambase::strstreambase(char* str, int size, char* pstart)
	:  buf(str,size,pstart) { init(&buf) ; }

strstreambase::strstreambase() : buf() { init(&buf) ; }

strstreambase::~strstreambase() { }

strstreambuf* strstreambase::rdbuf() { return &buf ; }

istrstream::istrstream(char* str)
	: strstreambase(str,strlen(str),0) { }

istrstream::istrstream(char* str, int size)
	: strstreambase(str, size , 0) { }

istrstream::istrstream(const char* str)
	: strstreambase((char *)str,strlen(str),0) { }

istrstream::istrstream(const char* str, int size)
	: strstreambase((char *)str, size , 0) { }

istrstream::~istrstream() { }


static char* pstart(char* str, int size, int m)
{
	if ( (m&(ios_append|ios_output)) == 0 )	return str+size ;
	else if ( (m&(ios_append|ios_atend)) )	return str+strlen(str) ;
	else					return str ;
	}

ostrstream::ostrstream(char* str, int size, int m)
	: strstreambase(str, size, pstart(str,size, (m|ios_output)) )
{ }

ostrstream::ostrstream() : strstreambase() { }


ostrstream::~ostrstream()
{
	if (!strstreambase::rdbuf()->isfrozen())
		ios::rdbuf()->sputc(0) ;
	}

char* ostrstream::str() 
{
	return strstreambase::rdbuf()->str() ;
	}

int ostrstream::pcount() {
	//return ios::rdbuf()->out_waiting() ;
	return strstreambase::rdbuf()->pcount();
	}

int strstreambuf::pcount(){
	return pptr()-base();
}

strstream::strstream() : strstreambase() { }

strstream::strstream(char* str, int size, int m) 
	: strstreambase(str,size,pstart(str,size,m)) 
{ }

char* strstream::str() 
{
	return strstreambase::rdbuf()->str() ;
	}


strstream::~strstream() { }
