mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. 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;
}