mud++0.26/etc/
mud++0.26/etc/guilds/
mud++0.26/log/
mud++0.26/mudC/
mud++0.26/player/
mud++0.26/src/unix/
/*
....[@@@..[@@@..............[@.................. 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@hom.net 
MUD++ development mailing list    mudpp-list@mailhost.net
------------------------------------------------------------------------------
io.cc
*/

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

#include <stdlib.h>
#include <string.h>
#include "io.h"

// These are currently in string.cc
const char * ltoa( long );
inline const char * itoa( int x ) { return ltoa( x ); }
const char * ultoa( unsigned long );

IStream::IStream( const char *fname )
:	line(1), ptr(0), cache(0)
{
	IStream::open( fname );
}

int IStream::open( const char * fname )
{
	if( ( fd = ::open( fname, O_RDONLY ) ) < 0 )
		return -1;

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

	// 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 /* HAVE_MMAP */
	{
		// 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;
#ifndef WIN32
		cache = new char[ stats.st_size + 1 ];
		while( tot < stats.st_size )
		{
			if( ( num = read( fd, (cache + tot), stats.st_size - tot ) ) == -1 )
			{
				perror( "IStream::open:read" );
				mudpp_exit(0);
			} /* if */
			else if( ( tot += num ) == stats.st_size )
				break;

			continue;
		} /* while */
		*( cache + stats.st_size ) = 0;
#else 
/*
 * WIN32 returns 0 at end of file, and truncates CRLF to LF, returning
 * ONLY the number of LFs which means that the normal looping read
 * will likely never fill properly.
 * The above looping reader is not necessary for WIN32 anyway since
 * it can handle large files with one read call (I hope :).
 * The routine for WIN32 is a little bit longer in that it makes an effort to
 * use the minimal amount of memory for the cache[] given the CRLF 
 * translation.
 */
		char *temp;
		temp = new char[stats.st_size + 1];
		num = read( fd, temp, stats.st_size );
		if (num == -1)
		{
			perror( "IStream::open:read");
			mudpp_exit(0);
		}
		cache = new char[num+1];
		memcpy(cache, temp, num);
		*(  cache + num ) = 0;
		delete [] temp;
#endif /* WIN32 */
		flags |= STR_NOTMAPPED;
	} /* if ...mmap */

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


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;
#if(HAVE_MMAP)
		else if( ( munmap( cache, stats.st_size ) ) < 0 )
		{
			perror( "~IStream:munmap" );
			mudpp_exit(0);
		}
#endif /* HAVE_MMAP */
	}
}


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.
		perror( "OStream:OStream:open" );
		mudpp_exit(0);
	}
	
	flags |= STR_OPEN;
}

OStream::OStream( const char * fname, int options )
:	Stream(), ptr( cache ), highwater( cache + CACHE_SIZE )
{

	if ( options & OStream::APPEND )
	{	
		fd = ::open( fname, O_WRONLY | O_CREAT | O_APPEND, 0777 );
		if( fd < 0 )
		{
			// Add error handling later.
			perror( "OStream:OStream:open" );
			mudpp_exit(0);
	}
	
	flags |= STR_OPEN;
}
	else
	{
		perror("OStream::OStream - option other than APPEND specified");
		mudpp_exit(0);	
	}
}





OStream::~OStream()
{
	flush();
	if( (fd != STDIN_FILENO) && (fd != STDOUT_FILENO) )
		::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
	{
		if( ptr > cache )
		{
			write( fd, cache, (int)( ptr - cache ) );
			ptr = cache;
		}
		write( fd, x, strlen( x ) );
	}

	return *this;
}


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
	{
		if( ptr > cache )
		{
			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
	{
		if( ptr > cache )
		{
			write( fd, cache, (int)( ptr - cache ) );
			ptr = cache;
		}
		write( fd, p, len );
	}
	
	return *this;
}


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


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


OStream & OStream::operator << ( unsigned short int x )
{
	const char *p = itoa( (int) x );
	int len = strlen( p );
	if( ( ptr + len ) < highwater )
	{
		memcpy( ptr, p, len );
		ptr += len;
	}
	else
	{
		if( ptr > cache )
		{
			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.
		if( ptr > cache )
		{
			write( fd, cache, (int)( ptr - cache ) );
			ptr = cache;
		}
		write( fd, &x, 1 );
	}

	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( flags & STR_NOTMAPPED )
		delete [] cache;
#if(HAVE_MMAP)
	else if( ( munmap( cache, stats.st_size ) ) < 0 )
	{
		perror( "IStream::close:munmap" );
		mudpp_exit(0);
	}
#endif /* HAVE_MMAP */
#ifndef WIN32
	cache = ptr = 0;
#else
	cache = ptr = NULL;
#endif /* WIN32 */
}


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';
				ptr++;
				if( *ptr == '\r' )
					ptr++;
				return save;
			}
			else if( *ptr == '\r' )
			{
				line++;
				*buf = '\0';
				ptr++;
				if( *ptr == '\n' )
					ptr++;
				return save;
			}
			ptr++; buf++;
		}

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

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



void InFile::error( const char * str )
{
	Cout << fname << ':' << getLineNo() << ": " << str << endl;
	mudpp_exit(0);
}


char *InFile::getstring( char *buf )
{
	char *ch = buf;

	// Dont have to check eof() here, get returns 0 if EOF which isn't space
	while( isspace( get( *ch ) ) )
		;
 
	if( eof() )
	{
		*buf = '\0';
		return buf;
	}

	if( *ch == TERM_CHAR )
	{
		*ch = '\0';
		return buf;
	} 

	ch++;
	// Ignore \r and do our own \n\r translation
	// Some clients need \r some don't. No unix flavor
	// client should need the \r but VAX and others do.
	while( !eof() && ( get( *ch ) != TERM_CHAR ) )
	{
		if( *ch != '\r' )
		{
			if( *ch == '\n' )
				*(++ch) = '\r';
			ch++;
		}
	}

/*
	if( eof() && *ch != TERM_CHAR )
		error( "getstring: TERM_CHAR not found - unexpected end of file");
*/
	*ch = '\0';
	return buf;
}


int InFile::getnum()
{ 
	char buf[256];
	char *ch = buf;

	// Dont have to check eof() here, get returns 0 if EOF which isn't space
	while( isspace( get( *ch ) ) )
		;

	if( eof() )
	{
		*(++ch) = '\0';
		return atoi( buf );
	}
	
	if( !isdigit( *ch ) && *ch != '-' && *ch != '+' )
	{
		*(++ch) = '\0';
		Cout << "buf so far =[" << buf << "]\n";
		error( "InFile::getnum.1() - non-numeric character" );
	}

	while( isdigit( get( *(++ch) ) ) )
		;

	if( !eof() )
		putback();

	if( !eof() )
		while( isspace( get( *ch ) ) )
			if( *ch == '\n' )
				break;

	if( *ch != '\n' )
		putback();

	*ch = '\0';
	return atoi( buf );
}


unsigned long InFile::getlong()
{ 
	char  buf[256];
	char *ch = buf;

	// Dont have to check eof() here, get returns 0 if EOF which isn't space
	while( isspace( get( *ch ) ) )
		;

	if( !isdigit( *ch ) && *ch != '-' && *ch != '+' )
	{
		*(++ch) = '\0';
		Cout << "buf so far =[" << buf << "]\n";
		error("InFile::getnum.1() - non-numeric character");
	}

	while( isdigit( get( *(++ch) ) ) )
		;

	if( !eof() )
		putback();

	if( !eof() )
		while( isspace( get( *ch ) ) )
			if( *ch == '\n' )
				break;

	if( *ch != '\n' )
		putback();

	*ch = '\0';
	return atol( buf );
}


// For my database scheme the chars '{', '}', '#', ';' are
// considered words. getword treats them that way
// getstring does not, using '~` to end strings
// This is mainly for multiline strings.

char *InFile::getword  ( char *buf )
{
	char *ch = buf;
  
	// Dont have to check eof() here, get returns 0 if EOF which isn't space
	while( isspace( get( *ch ) ) )
		;
  
	if( *ch == '{' || *ch == '}' || *ch == '#' || *ch == ';' )
	{
		*(ch+1) = '\0';
		return buf;
	}

	while( !eof() && !isspace( get( *++ch ) ) )
		if( *ch == '{' || *ch == '}' )
		{
			putback(); 
			break;
		}

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


// Bit fields are written in file in the following format:
//
// <number of fields N> <field 1> <field2> ... <fieldN>
//
void InFile::getbitfield( unsigned long * field )
{
	int fields = getnum();
	for( int i = 0; i < fields; i++ )
		field[i] = getlong();
}