/***************************************************************************
                          cstring.cpp  -  description
                             -------------------
    begin                : Fri Sep 21 2001
    copyright            : (C) 2001-2003 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cstring.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

/* for testing both code paths */
/* ifdef HAVE_STRCASESTR
 * undef HAVE_STRCASESTR
 * endif
 */

CString::CString()
{
	Init();
}

CString::CString( const char * sz )
{
	Init();

	set(sz);
}

CString::CString( const char ch )
{
	Init();

	set(&ch,1);
}

CString::CString( const CString & stringSrc )
{
	Init();

	set(stringSrc.m_szBuffer,stringSrc.m_nStringLength);
}

CString::CString( char * buffer, long length, long bufsize )
{
	m_szBuffer = buffer;
	m_nStringLength = length;
	m_nBufferSize = bufsize;
}

CString::~CString()
{
	if (m_szBuffer)
	{
		free(m_szBuffer);
		m_szBuffer = 0;
	}
}

/** */
void CString::Init()
{
	m_szBuffer      = 0;
	m_nStringLength = 0;
	m_nBufferSize   = 0;
}

/** */
void CString::Empty()
{
	if (m_szBuffer)
	{
		free(m_szBuffer);
	}

	Init();
}

/** */
unsigned char CString::GetHash( long value ) const
{
	if ( (m_nStringLength > 0) && (value < m_nStringLength) )
		return ((unsigned char)(m_szBuffer[value]&0xFF));
	else
		return 0;
}

/** */
long CString::Find( const char ch, long nStart ) const
{
	if ( m_nStringLength == 0 )
	{
		return -1;
	}

	if ( nStart > m_nStringLength )
		return -1;

	for ( long i = nStart; i < m_nStringLength; ++i )
	{
		if ( m_szBuffer[i] == ch )
			return i;
	}

	return -1;
}

/** */
long CString::Find( const char * sz, long nStart, bool cs ) const
{
	if ( !cs )
	{
		return FindCase(sz,nStart);
	}

	if ( m_nStringLength == 0 )
	{
		if ( sz != 0 )
			return -1;
		else
			return 0;
	}

	if ( !sz )
		return -1;

	const long len = strlen(sz);

	if ( (nStart+len) > m_nStringLength )
		return -1;

	const char * location = strstr( m_szBuffer + nStart, sz );
	
	if ( location == NULL )
	{
		return -1;
	}
	else
	{
		return location - m_szBuffer;
	}
}

/** */
long CString::Find( const CString & string, long nStart, bool cs ) const
{
	if ( !cs )
	{
		return FindCase(string,nStart);
	}

	if ( m_nStringLength == 0 )
	{
		if ( string.Length() == 0 )
			return 0;
		else
			return -1;
	}

	if ( string.Data() == 0 )
		return -1;

	if ( (nStart+string.Length()) > m_nStringLength )
		return -1;

	const char * location = strstr( m_szBuffer + nStart, string.Data() );
	
	if ( location == NULL )
	{
		return -1;
	}
	else
	{
		return location - m_szBuffer;
	}
}

/** */
long CString::FindCase( const char * sz, long nStart ) const
{
	if ( m_nStringLength == 0 )
	{
		if ( sz != 0 )
			return -1;
		else
			return 0;
	}

	if ( !sz )
		return -1;

	const long len = strlen(sz);

	if ( (nStart+len) > m_nStringLength )
		return -1;

#ifdef HAVE_STRCASESTR

	const char * location = strcasestr( m_szBuffer + nStart, sz );
	
	if ( location == NULL )
	{
		return -1;
	}
	else
	{
		return location - m_szBuffer;
	}

#else // HAVE_STRCASESTR

	for ( long i = nStart; i <= (m_nStringLength-len); ++i )
	{
#ifdef WIN32
		if ( _strnicmp(sz,m_szBuffer+i,len) == 0 )
#else
		if ( strncasecmp(sz,m_szBuffer+i,len) == 0 )
#endif
		{
			return i;
		}
	}
	
	return -1;
#endif // HAVE_STRCASESTR
}

/** */
long CString::FindCase( const CString & string, long nStart ) const
{
	const char * sz = string.Data();
	const long len  = string.Length();

	if ( m_nStringLength == 0 )
	{
		if ( sz != 0 )
			return -1;
		else
			return 0;
	}

	if ( !sz )
		return -1;

	if ( (nStart+len) > m_nStringLength )
		return -1;
#ifdef HAVE_STRCASESTR

	const char * location = strcasestr( m_szBuffer + nStart, sz );
	
	if ( location == NULL )
	{
		return -1;
	}
	else
	{
		return location - m_szBuffer;
	}

#else // HAVE_STRCASESTR

	for ( long i = nStart; i <= (m_nStringLength-len); ++i )
	{
#ifdef WIN32
		if ( _strnicmp(sz,m_szBuffer+i,len) == 0 )
#else
		if ( strncasecmp(sz,m_szBuffer+i,len) == 0 )
#endif
		{
			return i;
		}
	}
	
	return -1;

#endif // HAVE_STRCASESTR
}

/** */
long CString::FindRev( char ch, long nStart ) const
{
	if ( m_nStringLength == 0 )
		return -1;

	if ( nStart < 0 )
		nStart = m_nStringLength-1;
	else if ( nStart > (m_nStringLength-1) )
		return -1;

	for ( long i = nStart; i >= 0; --i )
	{
		if (m_szBuffer[i]==ch)
			return i;
	}

	return -1;
}

/** */
long CString::FindRev( const CString & string ) const
{
	const long len = string.Length();

	if ( m_nStringLength == 0 )
	{
		if ( len == 0 )
			return 0;
		else
			return -1;
	}

	if ( len == 0 )
		return -1;

	for ( long i = (m_nStringLength-len); i >= 0; --i )
	{
		if ( strncmp( m_szBuffer+i, string.Data(), len ) == 0 )
		{
			return i;
		}
	}

	return -1;
}

/** */
CString CString::Mid( long nFirst, long nCount ) const
{
	CString ret;

	// added_by_frg for nFirst < 0 case:
	// which is needed in all occasions where we use str.Right(n) where n > str.Length()
	if (nFirst < 0)
	{
		nCount += nFirst;
		nFirst = 0;
	}
	// addition_end

	if ( m_nStringLength == 0 )
		return ret;
	if ( nCount == -1 )
		nCount = m_nStringLength-nFirst;
	if ( nCount <= 0 )
		return ret;
	if( nFirst > m_nStringLength )
		return ret;

	if( (nFirst+nCount) > m_nStringLength )
		return ret;

	ret.set(m_szBuffer+nFirst,nCount);

	return ret;
}

/** */
CString CString::Section( const char sep, int start, int end ) const
{
	int i,si,ei;

	if ( m_nStringLength == 0 )
		return CString();

	for(i=0,si=0;i<start;i++,si++)
	{
		si = Find(sep,si);
		if ( si == -1 )
			break;
	}

	if ( si == -1 )
		return CString();

	for(ei=si;i<=end;i++,ei++)
	{
		ei = Find(sep,ei);
		if ( ei == -1 )
			break;
	}

	if ( ei == -1 )
		ei = m_nStringLength + 1;

	/* if ( (si==-1) || (ei==-1) )
		return CString(); */

	return Mid(si,ei-si-1);
}

/** */
bool CString::IsEmpty() const
{
	return ( (m_nStringLength==0) || (m_szBuffer==0) );
}

/** */
CString CString::number( const int n )
{
	char c[50];

#ifdef WIN32
	_snprintf(c,50,"%d",n);
#else
	snprintf(c,50,"%d",n);
#endif

	return CString(c);
}

/** */
CString CString::number( const unsigned int n )
{
	char c[50];

#ifdef WIN32
	_snprintf(c,50,"%u",n);
#else
	snprintf(c,50,"%u",n);
#endif

	return CString(c);
}

/** */
CString CString::number( const long n )
{
	char c[50];

#ifdef WIN32
	_snprintf(c,50,"%ld",n);
#else
	snprintf(c,50,"%ld",n);
#endif

	return CString(c);
}

/** */
CString CString::number( const unsigned long n )
{
	char c[50];

#ifdef WIN32
	_snprintf(c,50,"%lu",n);
#else
	snprintf(c,50,"%lu",n);
#endif

	return CString(c);
}

/** */
CString CString::number( const long long n )
{
	char c[50];

#ifdef WIN32
	_i64toa( n, c, 10 );
#else
	snprintf(c,50,"%lld",n);
#endif

	return CString(c);
}

/** */
CString CString::number( const ulonglong n )
{
	char c[50];

#ifdef WIN32
	_ui64toa( n, c, 10 );
#else
	snprintf(c,50,"%llu",n);
#endif

	return CString(c);
}

/** */
CString CString::number( const double n, const int p )
{
	char c[50];

#ifdef WIN32
	_snprintf(c,50,"%.*f",p,n);
#else
	snprintf(c,50,"%.*f",p,n);
#endif
	return CString(c);
}

/** */
ulonglong CString::asULL( int base ) const
{
	if ( m_nStringLength == 0 )
	{
		return 0;
	}

#if defined(HAVE_STRTOULL)
	return strtoull(Data(),NULL,base);
#elif defined(HAVE__STRTOUI64)
	return _strtoui64(Data(),NULL,base);
#elif defined(HAVE_STRTOUQ)
	return strtouq(Data(),NULL,base);
#elif defined(__arch64__)
 	return strtoul(Data(),NULL,base);
#else
	#error "No strtoull _strtoui64 strtouq function available"
#endif
}

/** */
unsigned int CString::asUINT( int base ) const
{
	return (unsigned int)asULONG(base);
}

/** */
int CString::asINT( int base ) const
{
	return (int)asLONG(base);
}

/** */
long CString::asLONG( int base ) const
{
	if ( m_nStringLength == 0 )
	{
		return 0;
	}

	return strtol(Data(),NULL,base);
}

unsigned long CString::asULONG( int base ) const
{
	if ( m_nStringLength == 0 )
	{
		return 0;
	}
	
	return strtoul( Data(), NULL, base );
}

long long CString::asLONGLONG( int base ) const
{
	if ( m_nStringLength == 0 )
	{
		return 0;
	}

#if defined(HAVE_STRTOLL)
	return strtoll(Data(),NULL,base);
#elif defined(HAVE__STRTOI64)
	return _strtoi64(Data(),NULL,base);
#elif defined(HAVE_STRTOQ)
	return strtoq(Data(),NULL,base);
#elif defined(__arch64__)
	return strtol(Data(),NULL,base);
#else
	#error "No strtoll _strtoi64 strtoq function available"
#endif
}

/** */
double CString::asDOUBLE() const
{
	if ( m_nStringLength == 0 )
	{
		return 0;
	}

	return strtod(Data(),NULL);
}

/** */
CString CString::ToUpper() const
{
	if ( m_nStringLength == 0 )
		return CString();

	char * resbuf = (char*) malloc(m_nStringLength+1);
	
	if ( !resbuf )
	{
		perror("CString::ToUpper: malloc");
		return CString();
	}

	for ( long i = 0; i < m_nStringLength; ++i )
	{
		resbuf[i] = (const char)(toupper(m_szBuffer[i])&0xFF);
	}

	resbuf[m_nStringLength] = 0;

	return CString( resbuf, m_nStringLength, m_nStringLength+1 );
}

/** */
CString CString::ToLower() const
{
	if ( m_nStringLength == 0 )
		return CString();

	char * resbuf = (char*) malloc(m_nStringLength+1);
	
	if ( !resbuf )
	{
		perror("CString::ToLower: malloc");
		return CString();
	}

	for ( long i = 0; i < m_nStringLength; ++i )
	{
		resbuf[i] = (const char)(tolower(m_szBuffer[i])&0xFF);
	}

	resbuf[m_nStringLength] = 0;

	return CString( resbuf, m_nStringLength, m_nStringLength+1 );
}

/** */
CString CString::Replace( CString src, CString string ) const
{
	long i = 0, t = 0;
	CString ret;

	while ( (i=Find(src,i)) != -1 )
	{
		ret += Mid(t,i-t);
		ret += string;
		i+=src.Length();
		t = i;
	}

	ret += Mid(t,m_nStringLength-t);

	return ret;
}

/** */
void CString::Swap( const char before, const char after )
{
	for ( long i = 0; i < m_nStringLength; i++ )
	{
		if ( m_szBuffer[i] == before )
		{
			m_szBuffer[i] = after;
		}
	}
}

/** */
void CString::set( const char * sz, long nLength )
{
	long l;
	char * temp;

	if ( !sz )
	{
		Empty();
		return;
	}

	if( nLength == -1 )
		l = strlen(sz);
	else
		l = nLength;

	if ( l <= 0 )
	{
		Empty();
		return;
	}

	if ( (m_szBuffer) && (sz >= m_szBuffer) && ( sz <= m_szBuffer+m_nBufferSize ) )
	{
		temp = (char*) malloc( l );
		if ( temp == 0 )
		{
			perror("CString::set malloc ");
			return;
		}
		memcpy(temp,sz,l);
	}
	else
	{
		temp = (char*) sz;
	}

	Empty();

	m_szBuffer = (char*) malloc(l+1); //new char[l+1];

	if ( m_szBuffer == 0 )
	{
		printf("CString::set malloc [%ld]: %s\n",l+1,strerror(errno));
		
		if ( temp != sz )
		{
			free(temp);
		}
		
		return;
	}

	memcpy(m_szBuffer,temp,l);
	m_szBuffer[l]   = 0;
	m_nStringLength = l;
	m_nBufferSize   = l+1;
	
	if ( temp != sz )
	{
		free(temp);
	}
}

/** */
void CString::add( const char * sz, long nLength )
{
	long l,len,i;
	char *p;
	char * temp;

	if (!sz)
		return;

	if( nLength == -1 )
		l = strlen(sz);
	else
		l = nLength;

	if (l<=0)
		return;

	if(!m_szBuffer)
	{
		set(sz,l);
	}
	else
	{
		// if pointers overlap, copy input before realloc
		// which also means memcpy is safe (do not need memmove)
		if ( (sz >= m_szBuffer) && (sz <= m_szBuffer+m_nBufferSize) )
		{
			temp = (char*) malloc( l );
			if ( temp == 0 )
			{
				perror("CString::add malloc ");
				return;
			}
			memcpy(temp,sz,l);
		}
		else
		{
			temp = (char*) sz;
		}

		len = m_nStringLength;

		if((l+len+1)>m_nBufferSize)
		{
			i = 1000+1+l;
			p = (char*) realloc(m_szBuffer,m_nBufferSize+i);

			if ( p == 0 )
			{
				perror("CString::add realloc ");
				
				if ( temp != sz )
				{
					free(temp);
				}
				
				return;
			}

			m_szBuffer = p;
			m_nBufferSize += i;
		}

		memset(m_szBuffer+len+l,0,1);
		memcpy(m_szBuffer+len,temp,l);
		m_nStringLength+=l;
		
		if ( temp != sz )
		{
			free(temp);
		}
	}
}

/** */
void CString::Append( const char ch )
{
	if ( m_nStringLength + 2 > m_nBufferSize )
	{
		char * newbuf = (char*) realloc( m_szBuffer, m_nBufferSize + 200 );
		
		if ( newbuf == 0 )
		{
			perror("CString::addchar realloc ");
			return;
		}
		
		m_szBuffer = newbuf;
		m_nBufferSize += 200;
	}
	
	m_szBuffer[m_nStringLength] = ch;
	m_szBuffer[m_nStringLength+1] = 0;
	++m_nStringLength;
}

/** */
bool operator == ( const CString & string1, const CString & string2 )
{
	if ( string1.IsEmpty() )
	{
		return string2.IsEmpty();
	}

	if ( string1.Length() != string2.Length() )
		return false;

	if ( memcmp( string1.Data(), string2.Data(), string1.Length() ) != 0 )
		return false;

	return true;
}

/** */
CString CString::RightJustify( long nNewLength, char fill , bool /*truncate*/ )
{
	CString s;

	while( (s.m_nStringLength+m_nStringLength) < nNewLength )
		s+=fill;

	return(s+*this);
}

/** */
bool operator == ( const char * sz, const CString & string )
{
	if ( sz == 0 )
	{
		return string.IsEmpty();
	}
	else
	{
		if ( strlen(sz) == (size_t) string.Length() )
		{
			return ( memcmp( sz, string.Data(), string.Length() ) == 0 );
		}
		else
		{
			return false;
		}
	}
}

/** */
bool operator == ( const CString & string, const char * sz )
{
	return ( sz == string );
}

/** */
bool operator != ( const CString & string1, const CString & string2 )
{
	return (!(string1 == string2));
}

/** */
bool operator != ( const char * sz, const CString & string )
{
	return (!(sz == string));
}

/** */
bool operator != ( const CString & string, const char * sz )
{
	return (!(sz == string));
}

/** */
int CString::Compare( const CString & other ) const
{
	if ( m_nStringLength == 0 )
	{
		if ( other.IsEmpty() )
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	else if ( other.IsEmpty() )
	{
		return -1;
	}
	else
	{
		return strcoll( Data(), other.Data() );
	}
}

/** */
int CString::Compare( const char * other ) const
{
	if ( m_nStringLength == 0 )
	{
		if ( other == NULL )
		{
			return 0;
		}
		else
		{
			return 1;
		}
	}
	else if ( other == NULL )
	{
		return -1;
	}
	else
	{
		return strcoll( Data(), other );
	}
}

/** */
bool CString::StartsWith( const CString & other ) const
{
	if ( other.Length() > m_nStringLength )
	{
		return false;
	}
	else if ( other.Length() == 0 )
	{
		return true;
	}
	else
	{
		return strncmp( m_szBuffer, other.Data(), other.Length() ) == 0;
	}
}

/** */
bool CString::StartsWith( const char * other, long len ) const
{
	if ( len > m_nStringLength )
	{
		return false;
	}
	else if ( len == 0 )
	{
		return true;
	}
	else
	{
		return strncmp( m_szBuffer, other, len ) == 0;
	}
}
