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