/* ....[@@@..[@@@..............[@.................. 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 ------------------------------------------------------------------------------ string.cc */ // string.cc // Written by Fusion <msmith@hom.net> // 25 Apr 1995 Casual hacking by Furey <mec@duracef.shout.net> #include "config.h" #include "string.h" #include <stdio.h> #include <stdarg.h> #include <unistd.h> #if defined(WIN32) #include "win32/crypt.h" #endif // Copy-on-write shared-rep string class // 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. // -- String class starts here -- // 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 xlen = strlen( x.str ); if( this != &x ) { delete [] str; str = new char[ xlen + 1 ]; strcpy( str, x.str ); count = 1; sz = xlen + 1; } return *this; } // 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 xlen = strlen( x ); if( xlen < rep->sz ) { strcpy( rep->str, x ); return *this; } else { char * buf; buf = new char[ xlen + 1 ]; delete [] rep->str; strcpy( buf, x ); rep->str = buf; rep->sz = xlen + 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 ) { char * t; if( i < rep->sz ) { if( rep->count == 1 ) return *(rep->str + i); rep->count--; rep = new StringRep( *rep ); return *(rep->str + i); } else { if( rep->count == 1 ) { t = rep->str; rep->sz <<= 1; rep->str = (char *)realloc( rep->str, rep->sz ); return *(rep->str + i); } t = rep->str; rep->count--; rep = new StringRep( ( rep->sz << 1 ) ); memcpy( rep->str, t, i ); return *(rep->str + i); } } 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 ) { int ylen = strlen( rep->str ); if( xlen == 0 ) return *this; int tlen = xlen + ylen; 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 + ylen; 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 ) { int ylen = strlen( rep->str ); if( xlen == 0 ) return *this; int tlen = xlen + ylen; 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 ylen = strlen( rep->str ); if( rep->count == 1 ) { if( ylen + 1 < rep->sz ) { *(rep->str + ylen) = x; ylen++; *(rep->str + ylen) = '\0'; } else { char *buf = new char [ ylen + 2 ]; strcpy( buf, rep->str ); *(buf + ylen) = x; ylen++; *(buf + ylen) = '\0'; delete [] rep->str; rep->str = buf; rep->sz = ylen + 1; } } else { int tlen = ylen + 1; char *buf = new char [ tlen + 1 ]; strcpy( buf, rep->str ); *(buf + ylen) = x; *(buf + tlen) = '\0'; rep->count--; rep = new StringRep; rep->str = buf; rep->sz = tlen + 1; } return *this; } String & String::append( const timeval & x ) { char buf[ 256 ]; int days = 0; int hours = 0; int minutes = x.tv_sec / 60; if( minutes >= 60 ) { hours = minutes / 60; minutes = ( minutes % 60 ); if( hours >= 24 ) { days = hours / 24; hours = ( hours % 24 ); } } strcpy( buf, "Up " ); if( days ) { strcat( buf, itoa( days ) ); if( days < 2 ) strcat( buf, " day, " ); else strcat( buf, " days, " ); } if( hours ) { strcat( buf, itoa( hours ) ); if( hours > 1 ) strcat( buf, " hours and " ); else strcat( buf, " hour and " ); } strcat( buf, itoa( minutes ) ); if( minutes == 1 ) strcat( buf, " minute" ); else strcat( buf, " minutes" ); return append( buf ); } 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; } 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 WHITESPACE/LF/CR from string. String & String::rtrim() { 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; } String & String::strip( const char & ch ) { char * dest; char * src; if( *rep->str ) { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } dest = rep->str; src = rep->str; while( *src ) { if( *src != ch ) *dest++ = *src; src++; } *dest = '\0'; } return *this; } // Remove leading WHITESPACE/LF/CR from string. String & String::ltrim() { char * dest; char * src; if( *rep->str ) { if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } dest = rep->str; src = rep->str; while( isspace( *src ) ) src++; for( ; *dest; src++, dest++ ) *dest = *src; } return *this; } #if 0 #ifdef WIN32 bool String::operator ! ( void ) { char *s1 = rep->str; if ( !*s1 ) // if the first character is '\0' then the String is empty return true; return false; } // not necessary? // bool String::operator ! ( const char * ) #endif /* WIN32 */ #endif /* 0 */ 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; } // Return true only if x is abbreviation of String not vice-versa bool String::isAbbrev( const char * x ) const { char *s1 = rep->str; for( ; ; ) { if( !*s1 ) return true; else if( !*x ) return false; else if( tolower( *s1 ) != tolower( *x ) ) return false; s1++; x++; } return true; } // Return true only if String is abbreviation of x not vice-versa bool String::hasAbbrev( const char * x ) const { char *s1 = rep->str; for( ; ; ) { if( !*x ) return true; else if( !*s1 ) return false; else if( tolower( *s1 ) != tolower( *x ) ) return false; s1++; x++; } 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] = '\n'; ret[i+1] = '\0'; } return ret; } // 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 int String::sprintf( const char *fmt, ... ) { char buf[ 8192 ]; // Yeah so dont sprintf anything huge yet :P int xlen; va_list args; va_start( args, fmt ); xlen = vsprintf( buf, fmt, args ); clr(); append( buf, xlen ); va_end( args ); return xlen; } int String::sprintfAdd( const char *fmt, ... ) { char buf[ 8192 ]; int xlen; va_list args; va_start( args, fmt ); xlen = vsprintf( buf, fmt, args ); append( buf, xlen ); va_end( args ); return xlen; } void String::shiftLeft() { char * dest; char * src; if( rep->count > 1 ) { rep->count--; rep = new StringRep( rep->str ); } if( *rep->str ) { dest = rep->str; src = rep->str + 1; for( ; *dest; src++, dest++ ) *dest = *src; } } 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; if( !*x ) return false; if ( *x == '-' ) x++; for( ; *x; x++ ) if( !isdigit( *x ) ) return false; return true; } int str_cmp( const char *s1, const char *s2 ) { if( !s1 || !s2 ) return 1; // failure...bad pointer for( ; *s1 || *s2; s1++, s2++ ) if( tolower( *s1 ) != tolower( *s2 ) ) return 1; // failure...character mismatch if( !*s1 && !*s2 ) return 0; // success...string chars and lengths same else return 1; // failure...one string is subset of another } // If one string is an abbrev of the other // Empty string doesn't coun't int str_abbrev( const char *s1, const char *s2 ) { if( !s1 || !s2 || !*s1 || !*s2 ) return 1; for( ; *s1 && *s2; s1++, s2++ ) if( tolower( *s1 ) != tolower( *s2 ) ) return 1; return 0; } 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; } // Warning this code looks nasty. This is all static variable work. // The getArg() member function must be a const member so everything has to be // overridden with const_cast<>. The actual String object state is not changed. String String::getArg() const { // _argptr and _argnext are undefined until startArgs is called. ((String *)this)->_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 ) ) ((String *)this)->_argptr++; if( *_argptr == '"' ) { ((String *)this)->_argptr++; ((String *)this)->_argnext = _argptr; while( *_argnext && *_argnext != '"' ) ((String *)this)->_argnext++; if( *_argnext ) { *(((String *)this)->_argnext) = '\0'; ((String *)this)->_argnext++; } return String( _argptr ); } ((String *)this)->_argnext = _argptr; // Now we are either at beginning of arg or NULL Char if( !*_argptr ) return String( _argptr ); while( *_argnext && !isspace( *_argnext ) ) ((String *)this)->_argnext++; if( *_argnext ) { *(((String *)this)->_argnext) = '\0'; ((String *)this)->_argnext++; } return String( _argptr ); } // check if string is legal and safe filename // allowed chars are letters, digits and dot bool String::legalFilename() const { int i; int length = len(); for ( i =0; i< length; i++ ) { if ( !isalpha(rep->str[i]) && (rep->str[i] != '.' ) && !isdigit(rep->str[i]) ) return false; } return true; }