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