/* ....[@@@..[@@@..............[@.................. 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@falcon.mercer.peachnet.edu MUD++ development mailing list mudpp-list@spice.com ------------------------------------------------------------------------------ string.cc */ // string.cc // Written by Fusion <msmith@falcon.mercer.peachnet.edu> // 25 Apr 1995 Casual hacking by Furey <mec@duracef.shout.net> #include "io.h" #include "./string.h" #include <stdarg.h> // Take a look at Robert B. Murray's book (C++ Strategies and Tactics) // for a good look at the design of a String class and the efficiencies // and deficiencies of copy on write use counts. He also does a profile // which prompted me to use use counts instead of plain copy. // R. Murray recommends implementing the assignment operator in case // the library is ever changed. This is really an exercise in safe // coding. Probably will never be used. const StringRep & StringRep::operator = ( const StringRep & x ) { int len = strlen( x.str ); if( this != &x ) { delete [] str; str = new char[ len + 1 ]; strcpy( str, x.str ); count = 1; sz = len + 1; } return *this; } // -- String class starts here -- char String::_argbuf[ 1024 ]; char * String::_argptr; char * String::_argnext; String::String() { static StringRep * repEmpty = new StringRep(""); repEmpty->count++; rep = repEmpty; } String::String( const char *s ) : rep(new StringRep(s)) { ; } String::String( const String &orig ) : rep(orig.rep) { rep->count++; } String::String( int asize ) : rep( new StringRep( asize ) ) { ; } String::~String() { if( rep->count == 1 ) delete rep; else rep->count--; } // The string memory only grows but never shrinks, // in favor of efficiency. The String( int ) constructor allows a // caller to specify how much free store to allocate thus allowing the // string virtually never to have to call the system for heap space again. String & String::operator = ( const char *x ) { if( rep->str == x ) return *this; if( rep->count == 1 ) { int tlen = strlen( x ); if( tlen < rep->sz ) { strcpy( rep->str, x ); return *this; } else { char * buf; buf = new char[ tlen + 1 ]; delete [] rep->str; strcpy( buf, x ); rep->str = buf; rep->sz = tlen + 1; } } else { rep->count--; rep = new StringRep( x ); } return *this; } String & String::operator = ( const String & x ) { if( &x == this ) return *this; x.rep->count++; if( rep->count == 1 ) delete rep; else rep->count--; rep = x.rep; return *this; } char & String::operator [] ( int i ) { // add check with len member later (maybe) // ** This needs to be changed to a copy-on-write ** if( i >= rep->sz ) abort(); return *(rep->str + i); } char String::operator [] ( int i ) const { // add check with len member later (maybe) return *(rep->str + i); } int String::len() const { return strlen( rep->str ); } //------------------------------------------------------------ // This may still be used if I decide to put the 'len' member // back into the String class. It makes for some very fast // operations when len is used instead of strlen() // but I'm not sure the extra memory overhead is worth it. //------------------------------------------------------------ void String::setLen() { // rep->len = strlen( rep->str ); } void String::setLen( int ) { // rep->len = l; } //------------------------------------------------------------ int String::size() const { return rep->sz; } char * String::dup() const { char *x = new char[ strlen( rep->str ) + 1 ]; strcpy( x, rep->str ); return x; } // Don't deallocate, just null out. void String::clr() { if( rep->count > 1 ) { rep->count--; rep = new StringRep(""); } else { *rep->str = '\0'; } } String & String::append( const String & x ) { int xlen = strlen( x.rep->str ); int len = strlen( rep->str ); if( xlen == 0 ) return *this; int tlen = xlen + len; if( rep->count == 1 ) { if( rep->sz > tlen ) { strcat( rep->str, x.rep->str ); } else { char *buf = new char [ tlen + 1 ]; strcpy( buf, rep->str ); strcat( buf, x.rep->str ); delete [] rep->str; rep->str = buf; rep->sz = tlen + 1; } } else { tlen = xlen + len; char *buf = new char [ tlen + 1 ]; strcpy( buf, rep->str ); strcat( buf, x.rep->str ); rep->count--; rep = new StringRep; rep->str = buf; rep->sz = tlen + 1; } return *this; } String & String::append( const char * x ) { int xlen = strlen( x ); int len = strlen( rep->str ); if( xlen == 0 ) return *this; int tlen = xlen + len; if( rep->count == 1 ) { if( rep->sz > tlen ) { strcat( rep->str, x ); } else { char *buf = new char [ tlen + 1 ]; strcpy( buf, rep->str ); strcat( buf, x ); delete [] rep->str; rep->str = buf; rep->sz = tlen + 1; } } else { char *buf = new char [ tlen + 1 ]; strcpy( buf, rep->str ); strcat( buf, x ); rep->count--; rep = new StringRep; rep->str = buf; rep->sz = tlen + 1; } return *this; } String & String::append( const char x ) { if( x == '\0' ) return *this; int len = strlen( rep->str ); if( rep->count == 1 ) { if( len + 1 < rep->sz ) { *(rep->str + len) = x; len++; *(rep->str + len) = '\0'; } else { char *buf = new char [ len + 2 ]; strcpy( buf, rep->str ); *(buf + len) = x; len++; *(buf + len) = '\0'; delete [] rep->str; rep->str = buf; rep->sz = len + 1; } } else { int tlen = len + 1; char *buf = new char [ tlen + 1 ]; strcpy( buf, rep->str ); *(buf + len) = x; *(buf + tlen) = '\0'; rep->count--; rep = new StringRep; rep->str = buf; rep->sz = tlen + 1; } return *this; } String String::operator + ( const String & x ) const { String temp = *this; temp.append( x ); return temp; } String String::operator + ( const char * x ) const { String temp = *this; temp.append( x ); return temp; } String String::operator + ( const char x ) const { String temp = *this; temp.append( x ); return temp; } String String::operator + ( int x ) const { String temp = *this; temp.append( itoa( x ) ); return temp; } String String::operator + ( unsigned long x ) const { String temp = *this; temp.append( ltoa( x ) ); return temp; } // Don't return a reference here because temp is destroyed String String::asUpper() const { String temp = *this; temp.toUpper(); return temp; } // Don't return a reference here because temp is destroyed String String::asLower() const { String temp = *this; temp.toLower(); return temp; } String String::asProper() const { String temp = *this; temp.toProper(); return temp; } int String::asInt() const { return atoi( rep->str ); } String & String::toUpper() { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } char *ptr = rep->str; for( ; *ptr; ptr++ ) *ptr = toupper( *ptr ); return *this; } String & String::toLower() { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } char *ptr = rep->str; for( ; *ptr; ptr++ ) *ptr = tolower( *ptr ); return *this; } // Capitalize the first letter and lower the remaining letters String & String::toProper() { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } char *ptr = rep->str; *ptr = toupper( *ptr ); ptr++; for( ; *ptr; ptr++ ) *ptr = tolower( *ptr ); return *this; } String & String::crypt( const String & salt ) { char * ptr = ::crypt( rep->str, salt.chars() ); if( rep->count > 1 ) { rep->count--; rep = new StringRep( ptr ); } else { delete [] rep->str; rep->str = new char[ strlen( ptr ) + 1 ]; strcpy( rep->str, ptr ); } return *this; } // Remove trailing LF/CR from string. String & String::chop() { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } char *str = rep->str; while( *str && *str != '\n' && *str != '\r' ) str++; *str = '\0'; return *this; } // Strip leading space/cr/lf and trailing cr/lf String & String::clean() { } bool String::compare( const String & str ) const { char *s1 = rep->str; char *s2 = str.rep->str; if( !*s1 ) return false; else if( !*s2 ) return false; for( ; *s1; s1++, s2++ ) if( tolower( *s1 ) != tolower( *s2 ) ) return false; // End of s1, see if s2 has ended too if( *s2 ) return false; return true; } bool String::compare( const char * s2 ) const { char *s1 = rep->str; if( !*s1 ) return false; else if( !*s2 ) return false; for( ; *s1; s1++, s2++ ) if( tolower( *s1 ) != tolower( *s2 ) ) return false; // End of s1, see if s2 has ended too if( *s2 ) return false; return true; } // inline this by calling char * version (later :( ) bool String::compareWithCase( const String & str ) const { char *s1 = rep->str; char *s2 = str.rep->str; if( !*s1 ) return false; else if( !*s2 ) return false; for( ; *s1; s1++, s2++ ) if( *s1 != *s2 ) return false; // End of s1, see if s2 has ended too if( *s2 ) return false; return true; } bool String::compareWithCase( const char * s2 ) const { char *s1 = rep->str; if( !*s1 ) return false; else if( !*s2 ) return false; for( ; *s1; s1++, s2++ ) if( *s1 != *s2 ) return false; // End of s1, see if s2 has ended too if( *s2 ) return false; return true; } bool String::abbrev( const String & x ) const { char *s1 = rep->str; char *s2 = x.rep->str; for( ; ; ) { if( !*s1 || !*s2 ) return true; if( tolower( *s1 ) != tolower( *s2 ) ) return false; s1++; s2++; } // suppress warning return true; } bool String::abbrev( const char * x ) const { char *s1 = rep->str; for( ; ; ) { if( !*s1 || !*x ) return true; if( tolower( *s1 ) != tolower( *x ) ) return false; s1++; x++; } // suppress warning return true; } String String::getLine( int line ) const { String ret( 4096 ); char *src; int lines; int i; src = rep->str; for( lines = 1; lines < line; lines++ ) { while( *src && *src != '\n' ) src++; if( !*src ) break; src++; } if( *src ) { if( line > 1 && *src == '\r' ) src++; i = 0; while( *src && *src != '\n' ) { ret[ i ] = *src; i++; src++; } ret[i] = '\0'; } return ret; } String & String::operator << ( const char * x ) { append( x ); return *this; } String & String::operator << ( const String & x ) { append( x ); return *this; } String & String::operator << ( const char x ) { append( x ); return *this; } String & String::operator << ( int x ) { append( itoa( x ) ); return *this; } String & String::operator << ( unsigned long x ) { append( ltoa( x ) ); return *this; } OStream & operator << ( OStream & out, const String & str ) { out << str.chars(); return out; } /* IStream & operator >> ( IStream & in, String & ) { return in; } */ // A lame hack until I get time to write a vsprintf. // Ideally it will printf directly into the string rep // and bypass the buf/append // -Fusion int String::sprintf( const char *fmt, ... ) { char buf[ 8192 ]; // Yeah so dont sprintf anything huge yet :P int len; va_list args; va_start( args, fmt ); len = vsprintf( buf, fmt, args ); clr(); append( buf ); va_end( args ); return len; } int String::sprintfAdd( const char *fmt, ... ) { char buf[ 8192 ]; int len; va_list args; va_start( args, fmt ); len = vsprintf( buf, fmt, args ); append( buf ); va_end( args ); return len; } void String::shiftLeft() { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } if( *rep->str ) memmove( rep->str, (rep->str+1), strlen( rep->str ) ); } void String::shiftRight() { // Not coded yet. I needed shiftLeft() and it was easy, thats why its there. // Yawn - lazy. } bool String::isNumber() const { char * x = rep->str; for( ; *x; x++ ) if( !isdigit( *x ) ) return false; return true; } // ----- merging into String class ------- const char * ltoa( unsigned long x ) { const char * str = "0123456789"; unsigned long rem; static char buf[ 64 ]; char *ptr = buf + 63; do { ptr--; rem = (long)(x % 10); *ptr = *(str + rem); x /= 10; } while( x ); return ptr; } // Handles + or - numbers const char * itoa( int x ) { const char * str = "0123456789"; int rem; static char buf[ 64 ]; char *ptr = buf + 63; int neg = 0; if( x < 0 ) { neg = 1; x = abs( x ); } do { ptr--; rem = x % 10; *ptr = *(str + rem); x /= 10; } while( x ); if( neg ) *--ptr = '-'; return ptr; } /* THIS MAY BE REMOVED PENDING USAGE OF STRING CLASS */ /* Proper-ize the string */ char *str_cap( char *str ) { char *orig = str; if( !str ) return 0; *str = toupper( *str ); while( *++str ) *str = tolower( *str ); return orig; } /* THIS MAY BE REMOVED PENDING USAGE OF STRING CLASS */ int str_cmp( const char *s1, const char *s2 ) { if( !s1 || !s2 ) return 1; for( ; *s1 || *s2; s1++, s2++ ) if( tolower( *s1 ) != tolower( *s2 ) ) return 1; return 0; } /* THIS MAY BE REMOVED PENDING USAGE OF STRING CLASS */ char *str_lower( char *str ) { while( *str ) { *str = tolower(*str); str++; } return str; } int count_lines( const char *str ) { if( !str || !*str ) return 0; int lines = 1; while( *str ) { // just check '\n', there should always be \n\r or vice versa if( *str++ == '\n' ) lines++; } return lines; } const char *String::getArg() { // _argptr and _argnext are undefined until startArgs is called. _argptr = _argnext; // Naughty - you forgot to call String::startArgs() first. // This will only happen the first time but it helps debugging. // To optimize you can remove these two lines if you are coding correctly. if( !_argptr ) abort(); while( *_argptr && isspace( *_argptr ) ) _argptr++; // Now we are either at beginning of arg or NULL Char if( !*_argptr ) { _argnext = _argptr; return _argptr; } _argnext = _argptr; while( *_argnext && !isspace( *_argnext ) ) _argnext++; if( *_argnext ) { *(_argnext) = '\0'; _argnext++; } return _argptr; } /* char *get_arg( const char *str, int number, char *argbuf ) { char *next; if( !str || !*str ) { *argbuf = '\0'; return (char *)str; } while( *str && --number > 0 ) { // skip space while( *str && isspace( *str ) ) str++; // skip argument while( *str && !isspace( *str ) ) str++; } // Was there a number'th argument ? if( number > 0 ) return (char *)str; // shut the compiler up about const char // ok passed arguments now go to the one we want while( *str && isspace( *str ) ) str++; // Now we are either at beginning of arg or NULL Char if( !*str ) { if( argbuf ) *argbuf = '\0'; return (char *)str; } next = (char *)str; while( *next && !isspace( *next ) ) next++; strncpy( argbuf, str, next - str ); *(argbuf + (next - str) ) = '\0'; if( *next ) { while( *next && isspace( *next ) ) next++; } return next; } */