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

#define OSTREAM ostream

const int basebits = ios::dec|ios::oct|ios::hex ;

// At the cost of 100 bytes of table we can measurably speed
// up conversion (at least on a VAX)

static char digit1[] = {
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	'0','1','2','3','4','5','6','7','8','9',
	} ;

static char digit2[] = {
	'0','0','0','0','0','0','0','0','0','0',
	'1','1','1','1','1','1','1','1','1','1',
	'2','2','2','2','2','2','2','2','2','2',
	'3','3','3','3','3','3','3','3','3','3',
	'4','4','4','4','4','4','4','4','4','4',
	'5','5','5','5','5','5','5','5','5','5',
	'6','6','6','6','6','6','6','6','6','6',
	'7','7','7','7','7','7','7','7','7','7',
	'8','8','8','8','8','8','8','8','8','8',
	'9','9','9','9','9','9','9','9','9','9',
	} ;

static char* conv10(long i, char* bufend) 
/* p points to right end of a buffer. Function function returns
 * pointer to left end of converted number.  Number is not 0 terminated.
 *
 * Special care with negatives to avoid problems with
 * biggest negative number on 2's complement machines
 */
{
	register long j = i ;
	register char* p = bufend ;

	/* Above entered low order digit or 0 if i is zero */

	if ( j >= 0 ) {
		register int diff ;
		do {
			long register by100 = j/100 ;
			diff = (int)(j-100*by100) ;
			*p-- = digit1[diff] ;
			*p-- = digit2[diff] ;
			j = by100;
		} while ( j > 0 ) ;
		if ( diff<10 ) ++p ; //compensate for extra 0
	} else { // j < 0
		register int diff ;
		do {
			long register by100 = j/100 ;
			diff = (int)(100*by100-j) ;
			*p-- = digit1[diff] ;
			*p-- = digit2[diff] ;
			j = by100;
		} while ( j < 0 ) ;
		if ( diff<10 ) ++p ; //compensate for extra 0
	}
	return p+1 ;

}

static char* uconv10(unsigned long i, char* bufend) 
/* Same interface as conv10 except unsigned so we don't have
 * to worry about negatives */
{
	register unsigned long j = i ;
	register char* p = bufend ;
	register int diff ;

	do {
		long register by100 = j/100 ;
		diff = (int)(j-100*by100) ;
		*p-- = digit1[diff] ;
		*p-- = digit2[diff] ;
		j = by100;
	} while ( j > 0 ) ;
	if ( diff<10 ) ++p ; //compensate for extra 0
	
	return p+1 ;

}


static char* conv8(register unsigned long i, register char* p) 
{
	do {
		*p-- = (char)('0' + i%8) ;
		} while ( (i >>= 3) > 0 ) ;
	return p+1 ;
}

static char* conv16(register unsigned long i, register char* p) 
{
	do {
		register dig = (int)(i%16) ;
		if ( dig < 10 )	*p-- = (char)('0' + i%16) ;
		else		*p-- = (char)('a'-10 + dig) ;

		} while ( (i >>= 4) > 0 ) ;
	return p+1 ;
}


static char* conv16u(register unsigned long i, register char* p) 
{
	do {
		register dig = (int)(i%16) ;
		if ( dig < 10 )	*p-- = (char)('0' + i%16) ;
		else		*p-- = (char)('A'-10 + dig) ;

		} while ( (i >>= 4) > 0 ) ;
	return p+1 ;
}
ostream& OSTREAM::operator<<(const char* s)
{
	// I play some games so that if BREAKEVEN is <= 0 all
	// tests get set the right way at compile time 

#       if BREAKEVEN > 0
		static int	avglen = BREAKEVEN ;
					// running average of the lengths
					// of strings ;
#	else
		static const int avglen = BREAKEVEN ;
					// fixed constant so all tests
					// are fixed at compile time
#	endif

	register int fwidth = width(0) ;

	if (!opfx() ) return *this ;
	if ( s==0 ) return *this;
	register streambuf* nbp = bp ;
	register const char* p ;
	register int len ;
	register int pad ;

	register int leftjust = ( (flags()&left) != 0 ) ;
	if ( BREAKEVEN<0 
			|| BREAKEVEN>0 && avglen<=BREAKEVEN &&
			   (fwidth==0 || leftjust)){
		p = s ;
		while ( *p ) {
			if ( nbp->sputc(*p++) == EOF ) {
				setstate(badbit) ;
				break ;
			}
		}
		len = p-s ;
		pad = fwidth-len ;
	} else {
		len = strlen(s) ;
		pad = fwidth-len ;
		if ( pad>0 && !leftjust ) {
			while ( pad-- > 0 ) {
				if ( nbp->sputc(fill()) == EOF ) {
					setstate(badbit) ;
				}
			}
		}
		write(s,len) ;
	}

	if ( pad > 0  ) {
		while ( pad-- > 0 ) {
			if ( nbp->sputc(fill()) == EOF ) setstate(badbit) ;
		}
	}

 	if ( BREAKEVEN > 0 ) { // will be eliminated at compile time 
		avglen = (3*avglen + len) >> 2;
	}
	osfx() ;
	return *this;
}

static int dofield(
	ostream* ios,
	register char* pfx,
	int pwidth,
	register char* sfx,
	int swidth) 
{
	register streambuf* b = ios->rdbuf() ;
	register int w = ios->width(0)-(pwidth+swidth) ;
	register int f = (int)ios->flags() ;
	register int fchar = ios->fill() ; 

	if ( (f&ios::right) || !(f&(ios::left|ios::internal)) ) {
		while ( w-- > 0 ) {
			if ( b->sputc(fchar) == EOF ) return ios::badbit ;
			}
		}

	while ( *pfx ) {
		if ( b->sputc(*pfx++) == EOF ) return ios::badbit ;
		}

	if ( f&ios::internal ) {
		while ( w-- > 0 ) {
			if ( b->sputc(fchar) == EOF ) return ios::badbit ;
			}
		}
	while ( *sfx ) {
		if ( b->sputc(*sfx++) == EOF ) return ios::badbit ;
		}

	while ( w-- > 0 ) {
		if ( b->sputc(fchar) == EOF ) return ios::badbit ;
		}

	return 0 ;
	}

static const int dbufsize = 32 ;

ostream& OSTREAM::operator<<(long i)
{
	if (!opfx()) {
		width(0) ;
		return *this;
		}
	char buf[dbufsize];

	register char *p ;
	register char* pfx = "" ;
	register int pfxsize = 0 ;

	buf[dbufsize-1] = 0 ;
	switch( flags()&basebits ) {
		case ios::oct :
			p = conv8(i,&buf[dbufsize-2]) ;
			if ( (flags()&showbase) && i ) {
				pfx = "0" ; pfxsize = 1 ;
				}
			break ;
		case ios::hex :
			if ( flags()&uppercase ) {
				p=conv16u(i,&buf[dbufsize-2]);
				if ( flags()&showbase ) {
					pfx = "0X" ; pfxsize = 2 ;
					}
				}
			else {
				p=conv16(i,&buf[dbufsize-2]); 
				if ( flags()&showbase ) {
					pfx = "0x" ; pfxsize = 2 ;
					}
				}
			break ;
		default:
			p = conv10(i,&buf[dbufsize-2]) ;
			if ( i < 0 ) {
				pfx = "-" ; pfxsize = 1 ;
				}
			else if ( flags()&showpos ) {
				pfx = "+" ; pfxsize = 1 ;
				}
			break ;
		}
	register int err ;
	if ( err = dofield(this,pfx,pfxsize,p,&buf[dbufsize-1]-p) ) {
		setstate(err) ;
		}
	osfx() ;
	return *this ;
	}

ostream& OSTREAM::operator<<(unsigned long i)
{
	if (!opfx()) {
		width(0) ;
		return *this;
		}
	char buf[dbufsize];
	register char *p ;
	register char* pfx = "" ;
	register int pfxsize = 0 ;


	buf[dbufsize-1] = 0 ;
	switch( flags()&basebits ) {
		case ios::oct :
			p = conv8(i,&buf[dbufsize-2]) ;
			if ( (flags()&showbase) && i ) {
				pfx = "0" ; pfxsize = 1 ;
				}
			break ;
		case ios::hex :
			if ( flags()&uppercase ) {
				p=conv16u(i,&buf[dbufsize-2]);
				if ( flags()&showbase ) {
					pfx = "0X" ; pfxsize = 2 ;
					}
				}
			else {
				p=conv16(i,&buf[dbufsize-2]); 
				if ( flags()&showbase ) {
					pfx = "0x" ; pfxsize = 2 ;
					}
				}
			break ;
		default:
			p = uconv10(i,&buf[dbufsize-2]) ;
			break ;
		}
	register int err ;
	if ( err = dofield(this,pfx,pfxsize,p,&buf[dbufsize-1]-p) ) {
		setstate(err) ;
		}
	osfx() ;
	return *this ;
	}
	
ostream& OSTREAM::operator<<(register streambuf* b)
{
	register streambuf* nbp = bp;
	register int c;

	if (!opfx()) return *this;
	if ( !b ) {
		setstate(failbit) ;
		return *this ;
		}
	c = b->sgetc();
	while (c != EOF) {
		if (nbp->sputc(c) == EOF) {
			setstate(badbit) ;
			break;
			}
		c = b->snextc();
		}

	osfx() ;		
	return *this;
	}

ostream& OSTREAM::operator<<( void* p)
{
	long f = setf(ios::showbase|PTRBASE,  basebits|ios::showbase) ;
	*this << (long)p ;
	setf(f,~0) ;
	return *this ;
	}

/*  add this later
ostream& OSTREAM::operator<<( const void* p)
{
	long f = setf(ios::showbase|PTRBASE,  basebits|ios::showbase) ;
	*this << (long)p ;
	setf(f,~0) ;
	return *this ;
	}
*/

ostream& OSTREAM::operator<<(int x)
{
	*this << (long)x ;
	return *this ;
	}

ostream& OSTREAM::operator<<(unsigned int x)
{
	*this << (unsigned long)x ;
	return *this ;
	}

ostream& OSTREAM::complicated_put(char c)
{
	if ( opfx() ) {
		if (  bp->sputc(c) == EOF )  {
			setstate(eofbit|failbit) ;
		}
		osfx() ;
	}
	return *this ;
}
ostream& OSTREAM::ls_complicated(char c)
{
	put(c) ; osfx() ; return *this ;
}
ostream& OSTREAM::ls_complicated(unsigned char c) 
{
	put(c) ; osfx() ; return *this ;
}
