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