/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@falcon.mercer.peachnet.edu 
MUD++ development mailing list    mudpp-list@spice.com
------------------------------------------------------------------------------
io.cc
*/

// This is a quick hack to get around being dependant on iostream
// Not robust, etc.
// Basic one hour job.
// -Melvin

#include "io.h"

// These are currently in string.cc
const char * itoa( int );
const char * ltoa( unsigned long );

IStream::IStream( const char *fname )
:	line(1), ptr(0), cache(0)
{
	if( ( fd = ::open( fname, O_RDONLY ) ) < 0 )
	{
		return;
	}

	// Get stats on the file (size is what we need)
	if( fstat( fd, &stats ) < 0 )
	{
		return;
	}

	// Tell kernel to map this device to memory. If your mmap()
	// is failing then change the define below and it will skip to
	// allocator.

#if(HAVE_MMAP)
	if( ( cache = (char *)mmap( 0, stats.st_size + 1, PROT_READ,
								MAP_FILE | MAP_PRIVATE,
								fd, 0 ) ) == (caddr_t) -1)
#endif
	{
		// If mmap fails or is not available then we use our own allocation
		// We lose performance like this but the class internals
		// will still use the whole buffer just like if we mmap()

		int tot = 0;
		int num;
		cache = new char[ stats.st_size + 1 ];
		while( tot < stats.st_size )
		{
			if( ( num = read( fd, (cache + tot), stats.st_size - tot ) ) == -1 )
			{
				perror( "read" );
				exit(0);
			}
			else if( ( tot += num ) == stats.st_size )
			{
				break;
			}

			continue;
		}

		*( cache + stats.st_size ) = 0;
		flags |= STR_NOTMAPPED;
	}


	::close( fd ); // We dont need it, its all in RAM now.
	flags |= STR_OPEN; 
	ptr = cache;
} 


IStream::~IStream()
{
	if( cache )
	{
		// See if the device was ever mapped or did we fall to
		// backup with new char[] and read()
		if( flags & STR_NOTMAPPED )
		{
			delete [] cache;
		}
		else if( ( munmap( cache, stats.st_size ) ) < 0 )
		{
			perror( "munmap" );
			exit(0);
		}
	}
}




OStream Cout;


OStream::OStream( const char * fname )
:	Stream(), ptr( cache ), highwater( cache + CACHE_SIZE )
{
	fd = ::open( fname, O_WRONLY | O_CREAT | O_TRUNC, 0777 );
	if( fd < 0 )
	{
		// Add error handling later.
		exit(0);
	}
	
	flags |= STR_OPEN;
}


OStream::~OStream()
{
	flush();
	if( fd > 2 )
		::close( fd );
}


OStream & OStream::operator << ( const char * x )
{
	if( ! x )
		return *this;

	int len = strlen( x );
	if( ( ptr + len ) < highwater )
	{
		memcpy( ptr, x, len );
		ptr += len;
	}
	else
	{
		write( fd, cache, (int)( ptr - cache ) );
		ptr = cache;
		write( fd, x, strlen( x ) );
	}

	return *this;
}


OStream & OStream::operator << ( char * x )
{
	return ( *this << (const char *)x );
}


OStream & OStream::operator << ( int x )
{
	const char *p = itoa( x );
	int len = strlen( p );
	if( ( ptr + len ) < highwater )
	{
		memcpy( ptr, p, len );
		ptr += len;
	}
	else
	{
		write( fd, cache, (int)( ptr - cache ) );
		ptr = cache;
		write( fd, p, len );
	}
	
	return *this;
}


OStream & OStream::operator << ( long x )
{
	const char *p = ltoa( x );
	int len = strlen( p );
	if( ( ptr + len ) < highwater )
	{
		memcpy( ptr, p, len );
		ptr += len;
	}
	else
	{
		write( fd, cache, (int)( ptr - cache ) );
		ptr = cache;
		write( fd, p, len );
	}
	
	return *this;
}


OStream & OStream::operator << ( unsigned long x )
{
	const char *p = ltoa( x );
	int len = strlen( p );
	if( ( ptr + len ) < highwater )
	{
		memcpy( ptr, p, len );
		ptr += len;
	}
	else
	{
		write( fd, cache, (int)( ptr - cache ) );
		ptr = cache;
		write( fd, p, len );
	}
	
	return *this;
}


OStream & OStream::operator << ( char x )
{
	if( ptr < highwater )
	{
		*ptr = x;
		ptr++;
	}
	else
	{
		// Using iovec may be nice here.
		write( fd, cache, CACHE_SIZE );
		write( fd, &x, 1 );
		ptr = cache;
	}

	return *this;
}


void OStream::flush()
{
	if( ptr > cache )
	{
		write( fd, cache, (int)( ptr - cache ) );
		ptr = cache;
	}
}


int OStream::open()
{
	return -1;
}


int OStream::open( const char * )
{
	return -1;
}


void OStream::close()
{
	if( fd >= 0 )
		::close( fd );

	fd = -1;
}



// IStream class implementation, memory mapped

void IStream::close()
{
	if( !cache )
		return;

	if( ( munmap( cache, stats.st_size ) ) < 0 )
	{
		perror( "munmap" );
		exit(0);
	}

	cache = ptr = 0;
}


void IStream::putback()
{
	if( cache )
		if( ptr > cache )
		{
			ptr--;
			if( *ptr == '\n' )
				line--;
		}
}


char IStream::get( char & ch )
{
	if( cache )
	{
		if( ( ch = *ptr ) )
		{
			if( *ptr == '\n' )
				line++;
			return *ptr++;
		}
		return *ptr; // EOF = 0
	}

	ch = '\0';
	return '\0';
}


char * IStream::getline( char * buf )
{
	char *save = buf;

	if( cache )
	{
		while( *ptr )
		{
			*buf = *ptr;
			if( *ptr++ == '\n' )
			{
				line++;
				*buf = '\0';
				return save;
			}

			buf++;
		}

		*buf = '\0';
		return save;
	}

	*buf = '\0';
	return buf;
}