/*
....[@@@..[@@@..............[@.................. 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;
}