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