/*===========================================================================
 *
 * File:	SString.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Tuesday, May 08, 2001
 *
 * Contains the implementation for the simple string class CSString.
 *
 *=========================================================================*/

	/* Include Files */
#include "sstring.h"
#include <ctype.h>
#include <limits.h>


/*===========================================================================
 *
 * Begin Local Variable Definitions
 *
 *=========================================================================*/
  DEFINE_FILE();
/*===========================================================================
 *		End of Local Variable Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Constructor - CSString (pSrcString);
 *
 * Uses the given character buffer to initialize the string.  Handles the
 * NULL input string case.
 *
 *=========================================================================*/
CSString::CSString (const char* pSrcString) {
  //DEFINE_FUNCTION("CSString::CSString(char*)");

	/* Initialize the member variables */
  m_Length = 0;
  m_AllocLength = 0;
  m_pString = NULL;

  	/* Check for the NULL case */
  if (pSrcString == NULL) {
    AllocString(0);
    *m_pString = NULL_CHAR;
   }
	/* Create a normally sized string */
  else {
    AllocString(strlen(pSrcString));
    strcpy(m_pString, pSrcString);
   }

 }
/*===========================================================================
 *		End of Class CSString Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Constructor - CSString (pSrcString, SrcLength);
 *
 * Uses the given character buffer to initialize the string, up to the
 * given length.  Handles the NULL input string case.
 *
 *=========================================================================*/
CSString::CSString (const char* pSrcString, const size_t SrcLength) {
  //DEFINE_FUNCTION("CSString::CSString(char*, size_t)");

	/* Initialize the member variables */
  m_Length = 0;
  m_AllocLength = 0;
  m_pString = NULL;

  	/* Check for the NULL case */
  if (pSrcString == NULL) {
    AllocString(SrcLength);
    *m_pString = NULL_CHAR;
    m_Length = 0;
   }
	/* Create a normally sized string */
  else {
    size_t InputLength = strlen(pSrcString);

    AllocString(SrcLength);
    strnncpy(m_pString, pSrcString, SrcLength);
    if (InputLength < SrcLength) m_Length = InputLength;
   }

 }
/*===========================================================================
 *		End of Class CSString Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Constructor - CSString (SrcString);
 *
 * Uses the given CSString object to initialize the string.
 *
 *=========================================================================*/
CSString::CSString (const CSString& SrcString) {
  //DEFINE_FUNCTION("CSString::CSString(CSString&)");

	/* Initialize the member variables */
  m_Length = 0;
  m_AllocLength = 0;
  m_pString = NULL;

	/* Copy the source string */
  AllocString(SrcString.GetLength());
  strcpy(m_pString, (char*) SrcString);
 }
/*===========================================================================
 *		End of Class CSString Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Constructor - CSString (StringSize);
 *
 * Creates an empty string with the given number of bytes.  String is 
 * initially composed of all space characters.
 *
 *=========================================================================*/
CSString::CSString (const size_t StringSize) {
  //DEFINE_FUNCTION("CSString::CSString(size_t)");

	  /* Initialize the member variables */
  m_Length = 0;
  m_AllocLength = 0;
  m_pString = NULL;

	/* Copy the source string */
  AllocString(StringSize);
  memset(m_pString, ' ', StringSize);
  m_pString[StringSize] = NULL_CHAR;
 }
/*===========================================================================
 *		End of Class CSString Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Constructor - CSString (Char, Count);
 *
 * Creates a multiple character string.  ASSERTs on invalid input.
 *
 *=========================================================================*/
CSString::CSString (const char Char, const size_t Count) {
  //DEFINE_FUNCTION("CSString::CSString(char, size_t)");

	/* Initialize the member variables */
  m_Length = 0;
  m_AllocLength = 0;
  m_pString = NULL;

	/* Allocate and create the string */
  AllocString((size_t)Count);
  memset(m_pString, (int)Char, Count);
  m_pString[m_Length] = NULL_CHAR;
 }
/*===========================================================================
 *		End of Class Method CSString& CSString::operator=()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - void AllocString (StringSize);
 *
 * Protected class method which allocates a new string buffer for the 
 * given size, if the string doesn't fit in the currently allocated
 * buffer.  String is not initialized.
 *
 *=========================================================================*/
void CSString::AllocString (const size_t StringSize) {
  DEFINE_FUNCTION("CSString::AllocString()");

	/* Check for integer wrap arounds */
  ASSERT(StringSize < UINT_MAX - SSTRING_ALLOC_EXTRA - 1);

	/* Do we need to allocate a new buffer */
  if (StringSize > m_AllocLength || m_pString == NULL) {
    DestroyArrayPointer(m_pString);
    m_AllocLength = StringSize + SSTRING_ALLOC_EXTRA;
    m_Length = StringSize;
    m_pString = (char *) AllocateMemory(m_AllocLength + 1);   
   }
  else {
    m_Length = StringSize;
   }

	/* Ensure valid output */
  ASSERT(m_pString != NULL);
  ASSERT(m_Length <= m_AllocLength);
 }
/*===========================================================================
 *		End of Class Method CSString::AllocString()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - void AllocCopy (StringSize);
 *
 * Protected class method which allocates a copy of the the current 
 * string buffer with the  given size, if the new size doesn't fit in the
 * currently allocated buffer.  Thus, all that possibly changes is
 * the allocated string length.
 *
 *=========================================================================*/
void CSString::AllocCopy (const size_t StringSize) {
  DEFINE_FUNCTION("CSString::AllocCopy()");

	/* Check for integer wrap arounds */
  ASSERT(StringSize < (size_t)(UINT_MAX - SSTRING_ALLOC_EXTRA - 1));

	/* Do we need to allocate a new buffer */
  if (StringSize > m_AllocLength) {
    char* pNewString;

		/* Allocate the new string buffer */
    m_AllocLength = StringSize + SSTRING_ALLOC_EXTRA;
    pNewString = (char *) AllocateMemory(m_AllocLength + 1);   

		/* Copy the old buffer to the new then destroy the old */
    strcpy (pNewString, m_pString);
    DestroyPointer(m_pString);
    m_pString = pNewString;
   }

	/* Ensure valid output */
  ASSERT(m_pString != NULL);
  ASSERT(m_Length <= m_AllocLength);
 }
/*===========================================================================
 *		End of Class Method CSString::AllocCopy()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - void ConcatStrings (String1Size, pString1, 
 *					       String2Size, pString2);
 *
 * Protected class method used by the friend operator+() functions.
 * Creates a concatenation of pString1+pString2.  Handles NULL and
 * zero length strings.
 *
 *=========================================================================*/
void CSString::ConcatStrings (const size_t String1Size, const char* pString1,
			      const size_t String2Size, const char* pString2) {
  DEFINE_FUNCTION("CSString::ConcatStrings()");
  size_t NewSize;

	/* Ensure the strings can combine to produce a valid size */
  ASSERT(String1Size < (size_t)(UINT_MAX - String2Size));
  NewSize = String1Size + String2Size;

	/* Create a buffer larger enough to hold the new string */
  AllocString(NewSize);

	/* Create the new string */
  if (pString1 != NULL && String1Size != 0) memmove(m_pString, pString1, String1Size);
  if (pString2 != NULL && String2Size != 0) memmove(m_pString + String1Size, pString2, String2Size);
  m_pString[NewSize] = NULL_CHAR;
 }
/*===========================================================================
 *		End of Class Method CSString::ConcatStrings()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - void Empty (void);
 *
 * Deletes the current string contents, reallocating an empty string.
 *
 *=========================================================================*/
void CSString::Empty (void) {
  DEFINE_FUNCTION("CSString::Empty()");

  if (m_Length != 0) {
    DestroyPointer(m_pString);
    AllocString(0);
    *m_pString = NULL_CHAR;
   }

	/* Ensure success */
  ASSERT(m_Length == 0);
  ASSERT(*m_pString == NULL_CHAR);
 }
/*===========================================================================
 *		End of Class Method CSString::Empty()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - void FreeExtra (void);
 *
 * Ensures that the allocated and string lengths are equal, removing any
 * extra allocated buffer from the end of the string.
 *
 *=========================================================================*/
void CSString::FreeExtra (void) {
  DEFINE_FUNCTION("CSString::FreeExtra()");
  char* pNewString;

	/* Ensure valid input */
  ASSERT(m_pString != NULL);
  ASSERT(m_Length  <= m_AllocLength);

	/* Create an allocated string the exact size of the current buffer */
  if (m_Length != m_AllocLength) {
    pNewString = CreateString(m_Length);
    strcpy(pNewString, m_pString);
    DestroyPointer(m_pString)
    m_AllocLength = m_Length;
    m_pString = pNewString;
   }
 }
/*===========================================================================
 *		End of Class Method CSString::FreeExtra()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - CSString& TrimLeft (void);
 *
 * Trims whitespace from the left side of the string (according to the
 * isspace() function).
 *
 *=========================================================================*/
CSString& CSString::TrimLeft (void) {
  DEFINE_FUNCTION("CSString::TrimLeft()");
  size_t StringIndex = 0;

	/* Find the first non-whitespace character from left side of string */
  while (m_pString[StringIndex] != NULL_CHAR) {
    if (!isspace(m_pString[StringIndex])) break;
    StringIndex++;
   }

	/* Shift the string, if required, to remove whitespace */
  if (StringIndex != 0) {
    memmove(m_pString, m_pString + StringIndex, m_Length - StringIndex + 1);
    m_Length -= StringIndex;
   }

  ASSERT(m_pString[m_Length] == NULL_CHAR);
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString::TrimLeft()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - CSString& TrimRight (void);
 *
 * Trims whitespace from the right side of the string (according to the
 * isspace() function).
 *
 *=========================================================================*/
CSString& CSString::TrimRight (void) {
  DEFINE_FUNCTION("CSString::TrimRight()");
  size_t StringIndex = m_Length;

	/* Find the first non-whitespace character from right side of string */
  while (StringIndex != 0) {
    StringIndex--;
    if (!isspace(m_pString[StringIndex])) break;
   }

	/* Terminate the string at the last non-whitespace char */
  if (StringIndex != m_Length) {
    m_pString[StringIndex + 1] = NULL_CHAR;
    m_Length = StringIndex;
   }

  ASSERT(m_pString[m_Length] == NULL_CHAR);
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString::TrimRight()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString& CSString Method - operator= (SrcString);
 *
 * Copies the given source string to the current object.
 *
 *=========================================================================*/
const CSString& CSString::operator= (const CSString& SrcString) {
  //DEFINE_FUNCTION("CSString::operator=(CSString&)");

  AllocString(SrcString.GetLength());
  strcpy(m_pString, (char*)SrcString);
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString& CSString::operator=()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString& CSString Method - operator= (Char);
 *
 * Creates a one character string.
 *
 *=========================================================================*/
const CSString& CSString::operator= (const char Char) {
  //DEFINE_FUNCTION("CSString::operator=(char)");

  AllocString(1);
  m_pString[0] = Char;
  m_pString[1] = NULL_CHAR;
  
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString& CSString::operator=()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString& CSString Method - operator= (pSrcString);
 *
 * Copies the given string buffer to the current object.  Handles the
 * NULL string case.
 *
 *=========================================================================*/
const CSString& CSString::operator= (const char* pSrcString) {
  //DEFINE_FUNCTION("CSString::operator=(char*)");

	/* Check for the NULL string case */
  if (pSrcString == NULL) {
    AllocString(0);
    *m_pString = NULL_CHAR;
   }
	/* Create a normally sized string */
  else {
    AllocString(strlen(pSrcString));
    strcpy(m_pString, pSrcString);
   }

  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString& CSString::operator=()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - CSString& operator+= (pString);
 *
 * Concatenates the given string to the end of the string buffer.
 * Accepts NULL string input.
 *
 *=========================================================================*/
const CSString& CSString::operator+= (const char* pString) {
  DEFINE_FUNCTION("CSString::operator+=(char*)");
  size_t NewLength;

	/* Ignore NULL inputs */
  if (pString == NULL) return (*this);

	/* Ensure an allocated buffer of the correct length */
  NewLength = strlen(pString);
  AllocCopy(NewLength + m_Length);

	/* Add the source string to the end of the buffer */
  strcpy(m_pString + m_Length, pString);
  m_Length += NewLength;

	/* Ensure valid output */
  ASSERT(m_pString[m_Length] == NULL_CHAR);
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString::operator+=()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - CSString& operator+= (SourceString);
 *
 * Concatenates the given string to the end of the string buffer.
 * Accepts NULL string input.
 *
 *=========================================================================*/
const CSString& CSString::operator+= (const CSString& SourceString) {
  DEFINE_FUNCTION("CSString::operator+=(CSString)");
	
	/* Ensure an allocated buffer of the correct length */
  AllocCopy(SourceString.GetLength() + m_Length);

	/* Add the source string to the end of the buffer */
  strcpy(m_pString + m_Length, (const char*)SourceString);
  m_Length += SourceString.GetLength();

	/* Ensure valid output */
  ASSERT(m_pString[m_Length] == NULL_CHAR);
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString::operator+=()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CSString Method - CSString& operator+= (SourceChar);
 *
 * Concatenates the given character to the end of the string buffer.
 *
 *=========================================================================*/
const CSString& CSString::operator+= (const char SourceChar) {
  DEFINE_FUNCTION("CSString::operator+=(char)");
	
	/* Ensure an allocated buffer of the correct length */
  AllocCopy(m_Length + 1);

	/* Add the source character to the end of the buffer */
  m_pString[m_Length] = SourceChar;
  m_Length++;
  m_pString[m_Length] = NULL_CHAR;  
  
	/* Ensure valid output */
  ASSERT(m_pString[m_Length] == NULL_CHAR);
  return (*this);
 }
/*===========================================================================
 *		End of Class Method CSString::operator+=()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - CSString operator+ (String1, String2);
 *
 * Friend function to the CSString class to add two CSString objects.
 *
 *=========================================================================*/
CSString operator+ (const CSString& String1, const CSString& String2) {
  DEFINE_FUNCTION("operator+(CSString, CSString)");
  CSString NewString;
 	
  NewString.ConcatStrings(String1.GetLength(), (const char*)String1,
		          String2.GetLength(), (const char*)String2);

  return (NewString);
 }
/*===========================================================================
 *		End of Function operator+()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - CSString operator+ (pString1, String2);
 *
 * Friend function to the CSString class to add a string pointer with 
 * a CSString object.
 *
 *=========================================================================*/
CSString operator+ (const char* pString1, const CSString& String2) {
  DEFINE_FUNCTION("operator+(char*, CSString)");
  CSString NewString;
  	
  NewString.ConcatStrings(SafeStrLen(pString1), pString1,
		          String2.GetLength(),  (const char*)String2);

  return (NewString);
 }
/*===========================================================================
 *		End of Function operator+()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - CSString operator+ (String1, pString2);
 *
 * Friend function to the CSString class to add a string pointer with 
 * a CSString object.
 *
 *=========================================================================*/
CSString operator+ (const CSString& String1, const char* pString2) {
  DEFINE_FUNCTION("operator+(CSString, char*)");
  CSString NewString;
  	
  NewString.ConcatStrings(String1.GetLength(),  (const char*)String1,
		          SafeStrLen(pString2), pString2);

  return (NewString);
 }
/*===========================================================================
 *		End of Function operator+()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - CSString operator+ (String1, Char);
 *
 * Friend function to the CSString class to add a character pointer with 
 * a CSString object.
 *
 *=========================================================================*/
CSString operator+ (const CSString& String1, const char Char2) {
  DEFINE_FUNCTION("operator+(CSString, char)");
  CSString NewString;
  	
  NewString.ConcatStrings(String1.GetLength(),  (const char*)String1,
		         1,		        &Char2);

  return (NewString);
 }
/*===========================================================================
 *		End of Function operator+()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - CSString operator+ (Char, String2);
 *
 * Friend function to the CSString class to add a character pointer with 
 * a CSString object.
 *
 *=========================================================================*/
CSString operator+ (const char Char1, const CSString& String2) {
  DEFINE_FUNCTION("operator+(char, CSString)");
  CSString NewString;
  	
  NewString.ConcatStrings(1,		     &Char1,
		          String2.GetLength(),  (const char*)String2);

  return (NewString);
 }
/*===========================================================================
 *		End of Function operator+()
 *=========================================================================*/