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