mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. 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@van.ml.org
------------------------------------------------------------------------------
io.cpp
*/

#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include "config.h"
#include "io.h"

#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif

#ifdef ultrix
// All ULTRIX I know have no prototypes for mmap(), munmap()
// Even so, mmap doesn't work on ULTRIX in my experience.
extern "C"
{
	caddr_t mmap( caddr_t, size_t, int, int, int, off_t );
	int munmap( caddr_t, size_t );
}
#endif

#define CACHE_SIZE 1

#define STR_OPEN 1
#define STR_EOF  2
#define STR_NOTMAPPED 4  // This means mmap() failed and class had to allocate.

#ifndef MAP_FILE
#define MAP_FILE 0  // non BSD systems.
#endif

#ifndef STDOUT_FILENO
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

OutputFile Cout(STDOUT_FILENO);

int StaticInput::getnum()
{
	const char * start;	
	skipwhite();

	if ( eof() )
	{
		error("Input::getnum() - EOF hit before data");
		return 0;
	}

	start = ptr;
	if ( !isdigit( *ptr) )
	{
		if ( (*ptr == '-') || (*ptr =='+') )
			ptr++;
		else
		{
			error("Input::getnum() - non-numeric character");
			return 0;
		}
	}

	while( isdigit( getch() )	)
		;

	if( !eof() )
		putback();

	return atoi( start );
}

float StaticInput::getfloat()
{
	const char * start;	
	skipwhite();

	if ( eof() )
	{
		error("Input::getfloat() - EOF hit before data");
		return 0;
	}

	start = ptr;

	if ( !isdigit( *ptr) && (*ptr != '-') && (*ptr != '+') )
	{
		error("Input::getfloat() - non-numeric character");
		return 0;
	}

	while( isdigit( getch() ) || *(ptr-1) == '.')
		;

	if( !eof() )
		putback();

	return atof( start );
}

unsigned long StaticInput::getlong()
{
	const char * start;	
	skipwhite();

	if ( eof() )
	{
		error("Input::getnum() - EOF hit before data");
		return 0;
	}

	start = ptr;

	if ( !isdigit( *ptr) && *ptr != '-' && *ptr != '+' )
	{
		error("Input::getlong() - non-numeric character");
		return 0;
	}

	while( isdigit( getch() )	)
		;

	if( !eof() )
		putback();

	return atol( start );
}




char * StaticInput::getword(char * trg)
{
	char * start = trg;
	skipwhite();
	
	if ( eof() )
	{
		error("Input::getword - EOF hit before data");
		return 0;
	}
	if ( (*ptr == '{') || (*ptr == '}') || (*ptr =='#') || (*ptr
		==';'))
	{
		*trg++ = *ptr++;
		*trg = '\0';
		return start;
	}
  
	while ( !eof() && !isspace(*ptr) && *ptr != '{' && *ptr != '}' &&
			*ptr != '#' && *ptr !=';' )
	{
		*trg++ = *ptr++;
	}

	*trg = '\0';
	return start;
}

char * StaticInput::getstring(char* trg)
{
	char * start = trg;
	skipwhite();
 
	if( eof() )
	{
		error("Input::getstring - EOF hit before data");
		return 0;
	}

	while ( !eof() && *ptr != TERM_CHAR )
	{
		if ( *ptr !=  '\r' )
		{
			*trg = *ptr;
			if ( *trg == '\n' )
				*(++trg) = '\r';
			trg++;
		}
		ptr++;
	}

	if ( !eof() )
		ptr++;

	*trg = '\0';
	return start;
}

char * StaticInput::getCstring(char* trg)
{
	int count;
	count = strlen( ptr )+1;
	memcpy( trg, ptr, count );
	ptr += count;
	return trg;
}

char * StaticInput::getline(char* trg)
{
	char * start = trg;
	skipwhite();
	if ( eof() )
	{
		error("Input::getline - EOF hit before data");
		return 0;
	}

	while( !eof() && *ptr != '\n' )
		*trg++ = *ptr++;

	*trg = '\0';
	return start;
}

char * StaticInput::getsmartline(char * trg)
{
	char * start = trg;
	skipwhite();
	if ( eof() )
	{
		error("Input::getline - EOF hit before data");
		return 0;
	}

again:
	while( !eof() && *ptr != '\n' )
		*trg++ = *ptr++;

	if ( !eof() )
	{
		if ( (start != trg) && *(trg-1) == '\\' )
		{
			ptr++;
			if ( *ptr == '\r' )
				ptr++;
			trg--;
			// YESSSS!!!!
			goto again;
		}
	}

	*trg = '\0';
	return trg;

}

void StaticInput::getbitfield( unsigned long * field )
{
	int fields = getnum();
	for( int i = 0; i < fields; i++ )
		field[i] = getnum();
}

void * StaticInput::read(void* trg, int siz)
{
	void * start = trg;
	if ( (highwater - ptr) < siz )
	{
		errorf("Input::read - requested %d more bytes than available.\n\r"
				"Requested: %d\n\rHighwater: %x\n\rPtr: %x\n\r",
				(siz - (highwater - ptr)), siz, highwater, ptr);
 		return 0;
	}
	memcpy( trg, ptr, siz);
	ptr += siz;
	return start;
}


void StaticInput::error(const char * str)
{
	Cout << getName() << ':' << getLineNo() << ": " << str << endl;
	mudpp_exit(1);
}

void StaticInput::errorf( const char * fmt, ... )
{
	char buf[BUF];
	va_list args;
	va_start( args, fmt );
	vsprintf( buf, fmt, args );
	error(buf);
	va_end(args);
}


void InputFile::open()
{
	int fd;

	if( ( fd = ::open( filename, O_RDONLY | O_BINARY ) ) < 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( ( tcache = (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 num;
#if !defined(WIN32) && !defined(__CYGWIN32__)
		int tot = 0;
		tcache = new char[ stats.st_size + 1 ];
		while( tot < stats.st_size )
		{
			if( ( num = ::read( fd, (tcache + tot), stats.st_size - tot ) ) == -1 )
			{
				perror( "MPPIStream::open:read" );
				mudpp_exit(0);
			} /* if */
			else if( ( tot += num ) == stats.st_size )
				break;

			continue;
		} /* while */
		*( tcache + 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( "MPPIStream::open:read");
			mudpp_exit(0);
		}
		tcache = new char[num+1];
		memcpy(tcache, temp, num);
		*(  tcache + num ) = 0;
		delete [] temp;
#endif /* WIN32 */
		flags |= STR_NOTMAPPED;
	} /* if ...mmap */

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

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

	if( flags & STR_NOTMAPPED )
		delete [] tcache;
#if(HAVE_MMAP)
	else if( ( munmap( tcache, stats.st_size ) ) < 0 )
	{
		Cout << "Unmapped file " << getName() << " of size " << size() << " or "<< stats.st_size<<endl;
		perror( "MPPIStream::close:munmap" );
		mudpp_exit(0);
	}
#endif /* HAVE_MMAP */

	cache = NULL;
	ptr = NULL;
	tcache = NULL;
}


void Output::write( const void * src, size_t len )
{
	if ( !len )
		return;

	if( ( ptr + len ) < highwater )
	{
		memcpy( ptr, src, len );
		ptr += len;
	}
	else
	{
		flush();
		if ( (ptr + len) < highwater )
		{
			memcpy(ptr,src,len);
			ptr += len;
		}
		else
		{
			largewrite(src,len);
		}
	}
	return;
}

void Output::putbitfield( const unsigned long * field, int num )
{
	*this << num;

	for( int i = 0; i < num; i++ )
	{
		*this << ' ';
		*this << field[i]; 
	}
}


Output & Output::operator << ( const char * x )
{
	if( ! x )
		Error::dump("Null pointer passed to Output::operator << char *");
	int len = strlen( x );
	write(x,len);
	return *this;
}


Output & Output::operator << ( int x )
{
	const char *p = itoa( x );
	int len = strlen( p );
	write(p,len);
	return *this;
}


Output & Output::operator << ( long x )
{
	const char *p = ltoa( x );
	int len = strlen( p );
	write(p,len);
	return *this;
}


Output & Output::operator << ( unsigned long x )
{
	const char *p = ultoa( x );
	int len = strlen( p );
	write(p,len);
	return *this;
}


Output & Output::operator << ( short int x)
{
	const char *p = itoa( (int) x );
	int len = strlen( p );
	write(p,len);
	return *this;
}


Output & Output::operator << ( unsigned short int x )
{
	const char *p = itoa( (int) x );
	int len = strlen( p );
	write(p,len);
	return *this;
}


Output & Output::operator << ( char x )
{
	if( ptr >= highwater )
		flush();

	*ptr = x;
	ptr++;
	return *this;
}


void OutputBuffer::flush()
{
	if ( ptr == buf )
		return;
	int len = ptr - buf;
	if ( len >= (size - usedsize) )
	{
		memcpy( data+usedsize, buf, len);
		usedsize += len;
	}
	else
	{
		size <<= 1;
		size += len;
		char * ndata = new char[size];
		memcpy( ndata, data, usedsize );
		memcpy( ndata+ usedsize, buf, len );
		usedsize += len;
		delete [] data;
		data = ndata;
	}

	ptr = buf;
}

void OutputBuffer::largewrite( const void * x, size_t len )
{
	if ( ptr != buf )
		flush();

	if ( len >= (unsigned int) (size - usedsize) )
	{
		memcpy( data+usedsize, x, len);
		usedsize += len;
	}
	else
	{
		size <<= 1;
		size += len;
		char * ndata = new char[size];
		memcpy( ndata, data, usedsize );
		memcpy( ndata+ usedsize, x, len );
		usedsize += len;
		delete [] data;
		data = ndata;
	}
}

const char * OutputBuffer::getDataAsString()
{
	*this << '\0';
	flush();
	usedsize--;	// \0 is needed only for a moment
	return data;
}

void OutputFile::open(OutputFileMode mode)
{
	if ( mode == Write )
		fd = ::open( filename, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_FILE_MODE );
	else
		fd = ::open( filename, O_BINARY | O_WRONLY | O_CREAT | O_APPEND, DEFAULT_FILE_MODE );

	if ( fd == -1 )
	{
		active = false;
		perror("Cannot open file for writing");
		mudpp_exit(1);
	}
	active = true;
}

void OutputFile::flush()
{
	if ( ptr == buf )
		return;
	::write( fd, buf, ptr - buf );
	ptr = buf;
}

void OutputFile::close()
{
	flush();
	if ( fd == -1 )
		return;
	if ( !systemfd )
		::close(fd);
	active = false;
}

void OutputFile::largewrite( const void *x, size_t len )
{
	if ( ptr != buf )
		::write(fd, buf, ptr - buf );
	::write(fd, x, len );
	ptr = buf;
}

const char DynamicInput::emptyChar = '\0';

void DynamicInput::pull()
{
	int err;
	int toread;
	while(1)
	{		
		toread = endptr - topptr;
		err = read(topptr, toread);
		if ( !err )
		{
			return;
		}
		else if ( err > 0 )
		{
			if ( err == toread )
			{
				// We have hit end of buffer
				// is most of buffer filled ?
				if ( (getptr - startptr) < (size >>1) )
				{
					// YES - would be nice to resize
					// watch out for order of uptaded ptrs
					size = size << 1;
					char * nstart = new char[size];
					memcpy( nstart, getptr, endptr - getptr);
					topptr = nstart + (topptr - getptr) ;
					eobptr = nstart + (eobptr - getptr);
					delete startptr;
					startptr = nstart;
					getptr = nstart;
					endptr = nstart + size;
					continue;
				}
				else
				{
					// NO - just memmove to start of buffer and update ptrs
					int offset = getptr - startptr;
					memmove( startptr, getptr, endptr - getptr );
					getptr = startptr;
					eobptr -= offset;
					topptr = endptr - offset;
					continue;
				}
			}
			else
			{
				topptr += err;
				continue; // or return ?
			}
		}
		else
		{
			// some error ocurred
			return;
		}
	}
}

bool DynamicInput::scanForLine()
{
	return false;
}

const char * DynamicInput::getLine()
{
	if ( !gotline && !scanForLine() )
		return &emptyChar;
	char * line = getptr;
	*eobptr = '\0';
	getptr = eobptr+1;
	skipWhite();
	return line;
}