// GSString.cpp
//
// Copyright 1998 Diehl Graphsoft, Inc.
// All Rights Reserved.
//
// TXString class implementation.
//
// Started by David Bowman on 7/21/98.
//
// SourceSafe information for this version:
//    $Revision: 7 $
//    $Date: 12/22/00 10:34 AM $
//    $Author: Darick $


/* Includes */
/* -------- */
#include "StdHeaders.h"

#include <ctype.h>  // Do not change to <cctype> to avoid platform conflict on
                    //  whether toupper and tolower are in the "std" namespace
#include <stdarg.h>

#if _MINICAD_
	#include "ResHandler.X.h"
	#if GS_MAC
		#include "../../Include/CoreAPI.X.h"
	#elif GS_WIN
		#include "..\..\Include\CoreAPI.X.h"
	#endif
#else
	#include "MiniCadCallBacks.h"
	// This the sdk only section. DWD 3/20/00
	#include <string.h>
	#define ASSERTTHISCLASS(toWhom)
	#define ASSERTCLASS(kEveryone, charSet)
	#define NEW_NOTHROW new
	#define Mem_Copy memcpy
	#define Mem_Move memmove
	#define Mem_Zero(dest, count) memset( dest, 0, count)
#endif

#if GS_MAC
	#include "GSString.h"  // For stricmp and strnicmp
#else
	#include <tchar.h>
#endif
#include "GSString.X.h"

#include "EncodingSupport.X.h"

// This must follow all header files.
#undef THIS_FILE
DECLARE_THIS_FILE


/*============================================================================*/
/*============================================================================*/
/*********************/
/* TXString Routines */
/*********************/
#if GS_MAC
TXString NNA_CreateTXStringFromCFString(const CFStringRef& inString, bool bUseUTF8Encoding)
{
	CFStringEncoding	encoding	= kCFStringEncodingUTF8;
	if ( ! bUseUTF8Encoding ) {
		encoding	= ::CFStringGetSystemEncoding();
	}

	TXString result;
	const char* tempString = CFStringGetCStringPtr(inString, encoding);
	if (tempString) {
		result = tempString;		
	}
	else {
		CFIndex length = CFStringGetLength(inString);
		size_t maxChars = UnicodeToStringMaxChars(length);
		char *buffer = NEW_NOTHROW char[maxChars];
		CFStringGetCString(inString, buffer, maxChars, encoding);
		result = buffer;
		delete [] buffer;		
	}
	return result;
}
#endif


///////////////////////////////////////////////////////////////////////////////
// TXString public methods

//-----------------------------------------------------------------------------
TXString::~TXString()
{
	this->FreeBuffer();

	fData = NULL;
	fCStringData = NULL;

	fBufSize = 0;
	fLength = 0;
}

//-----------------------------------------------------------------------------
bool TXString::SetLength(size_t len)
{
	ASSERTTHISCLASS(kEveryone);
	if (len == 0) {
		Empty();
		return true;
	}
	else if (len < fLength) {
		UpdateLength(len);
		return true;
	}
	else
		return EnsureBufferSize(len);
}

//-----------------------------------------------------------------------------
bool TXString::SetLength(size_t len, char fill)
{
	ASSERTTHISCLASS(kEveryone);
	bool result = SetLength(len);
	for (size_t i=fLength; i<len; ++i) {  // This relies on 
		fData[i+1] = fill;
	}
	UpdateLength(len);
	return result;
}

//-----------------------------------------------------------------------------
void TXString::Truncate(size_t len)
{
	ASSERTTHISCLASS(kEveryone);
	if (len < fLength) {
		if (len <= 1)
			SetLength(len);
		else if (IsSJISLeadCharacter((const unsigned char*)&fData[1], len-1))
			SetLength(len-1);
		else
			SetLength(len);
	}

}
//-----------------------------------------------------------------------------
void TXString::Empty(bool bQuick)
{
	fLength = 0;
	if ( fData ) {
		fData[0] = fData[1] = 0;
		if (!bQuick)
			FreeExtra();
	}
}

//-----------------------------------------------------------------------------
void TXString::CopyInto(char *sz) const
{
	ASSERTTHISCLASS(kEveryone);
	if (VERIFYPOINTER(kEveryone, sz, fLength+1))
		lstrcpy(sz, &fData[1]);
}

//-----------------------------------------------------------------------------
void TXString::CopyInto(unsigned char *ps) const
{
	ASSERTTHISCLASS(kEveryone);
	int  len = (int) ((unsigned char) fData[0]) + 1;	// Number of bytes to copy
	if (VERIFYPOINTER(kEveryone, ps, len))
		memcpy(ps, &fData[0], len);
}

size_t TXString::GetSizeForUniCharRepresentation() const
{
#if GS_WIN
	return MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, fData + 1, -1, NULL, 0);
#else
	return 0;
#endif
}

void TXString::CopyInto(UCStrPtr utf16Array) const
{
#if GS_WIN
	MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, fData + 1, -1, utf16Array, GetSizeForUniCharRepresentation());
#else

#endif
}

//-----------------------------------------------------------------------------
unsigned char* TXString::GetUnsafeUnsignedCharPtr(size_t len)
{
	ASSERTTHISCLASS(kEveryone);
	SetLength(len);
	DSTOPIF(fLength > 255, (kEveryone, "You are trying to convert to a Pascal string > 255 characters (%d to be exact).", fLength));
	return (unsigned char*)&fData[0];
}


//-----------------------------------------------------------------------------
TXString::operator const unsigned char*() const
{
	ASSERTTHISCLASS(kEveryone);
	DSTOPIF(fLength > 255, (kEveryone, "You are trying to convert to a Pascal string > 255 characters (%d to be exact).", fLength));
	return (unsigned char*)&fData[0];
}

//-----------------------------------------------------------------------------
void TXString::SetAt(size_t nIndex, char ch)
{
	ASSERTTHISCLASS(kEveryone);
	if (nIndex < fLength)
		this->GetCharRef(nIndex, false) = ch;
	else if (nIndex == fLength)
		*this << ch;
	else
		DSTOP((kEveryone, "TXString: Trying to set character beyond end of string."));
}

//-----------------------------------------------------------------------------
TXString& TXString::operator<<(const char *sz)
{
	ASSERTTHISCLASS(kEveryone);
	if (sz) {
		size_t len = lstrlen(sz);
		EnsureBufferSize(fLength + len);
		memmove(&fData[fLength+1], sz, len+1);
		UpdateLength(fLength + len);
	}
	return *this;
}

//-----------------------------------------------------------------------------
TXString& TXString::operator<<(char ch)
{
	ASSERTTHISCLASS(kEveryone);
	const char sz[] = { ch, 0 };
	*this << sz;
	return *this;
}

//-----------------------------------------------------------------------------
TXString& TXString::operator<<(const unsigned char *ps)
{
	ASSERTTHISCLASS(kEveryone);
	if (VERIFYPSTRING(kEveryone, ps, 0, 0)) {
		size_t len = ps[0];
		EnsureBufferSize(fLength + len);
		memmove(&fData[fLength+1], &ps[1], len);
		fData[fLength + len + 1] = 0;
		UpdateLength(fLength + len);
	}
	return *this;
}

//-----------------------------------------------------------------------------
TXString& TXString::operator<<(long n)
{
	ASSERTTHISCLASS(kEveryone);

	char sz[12];
	#if _WINDOWS
		_itoa(n, sz, 10);
	#else
		sprintf(sz, "%ld", n);
	#endif
	return *this << sz;
}

//-----------------------------------------------------------------------------
int TXString::Compare(const char *sz) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, sz, 0, 0);
	return strcmp(*this, sz);
}

//-----------------------------------------------------------------------------
int TXString::Compare(const unsigned char *ps) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, ps, 0, 0);
	int ret = strncmp(*this, (const char*)&ps[1], ps[0]);
	if (ret == 0 && ps[0] != GetLength())
		ret = 1;
	return ret;
}

//-----------------------------------------------------------------------------
int TXString::CompareNoCase(const char *sz) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, sz, 0, 0);
	return _stricmp(*this, sz);
}

//-----------------------------------------------------------------------------
int TXString::CompareNoCase(const unsigned char *ps) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, ps, 0, 0);
	int ret = _strnicmp(*this, (const char*)&ps[1], ps[0]);
	if (ret == 0 && ps[0] != GetLength())
		ret = 1;
	return ret;
}

//-----------------------------------------------------------------------------
TXString TXString::Mid(size_t nFirst, size_t nCount) const
{
	ASSERTTHISCLASS(kEveryone);

	TXString ret;
	if ( nFirst < fLength ) {
		if ( nFirst + nCount > fLength )
			nCount = fLength - nFirst;

		size_t nLast = nFirst + nCount - 1;
		char savedChar = fData[1 + nLast + 1];
		fData[1 + nLast + 1] = 0;
		ret = &fData[1 + nFirst];
		fData[1 + nLast + 1] = savedChar;
	}
	return ret;
}

//-----------------------------------------------------------------------------
void TXString::Delete(size_t pos, size_t len)
{
	ASSERTTHISCLASS(kEveryone);
	if (!VERIFYN(kEveryone, pos < fLength)) return;

	// Don't delete past end of string.
	if (pos+len > fLength) len = fLength - pos;

	memmove(&fData[pos+1], &fData[pos+len+1], fLength-pos-len+1);
	UpdateLength(fLength - len);
}

//-----------------------------------------------------------------------------
void TXString::Insert(size_t pos, const char *sz)
{
	ASSERTTHISCLASS(kEveryone);

	size_t len = (size_t) lstrlen(sz);
	if ( len > 0 ) {
		ASSERTSTRING(kEveryone, sz, 1, 0);
		EnsureBufferSize(fLength + len);
		memmove(&fData[pos+len+1], &fData[pos+1], fLength-pos+1);
		memcpy(&fData[pos+1], sz, len);
		UpdateLength(fLength + len);
	}
}

//-----------------------------------------------------------------------------
void TXString::Insert(size_t pos, const unsigned char *ps)
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, ps, 1, 0);
	char sz[256];
	memmove(sz, &ps[1], ps[0]);
	sz[ps[0]] = 0;
	Insert(pos, sz);
}

//-----------------------------------------------------------------------------
void TXString::Insert(size_t pos, char ch, size_t cnt)
{
	ASSERTTHISCLASS(kEveryone);
	if (VERIFYN(kEveryone, cnt > 0)) {
		EnsureBufferSize(fLength + cnt);
		memmove(&fData[pos+cnt+1], &fData[pos+1], fLength-pos+1);
		memset(&fData[pos+1], ch, cnt);
		UpdateLength(fLength + cnt);
	}

}

//-----------------------------------------------------------------------------
void TXString::Replace(const char *oldStr, const char *newStr, bool bIgnoreCase)
{
	size_t oldLen = lstrlen(oldStr);
	if ( oldLen > 0 ) {
		int pos; // ::Find() may return -1 and when assigned to an unsigned int (size_t) may cause a lot of grief
		size_t nextPos = 0;
		while ((nextPos < fLength) && (pos = this->Find(nextPos, oldStr, bIgnoreCase)) >= 0) {
			this->Delete(pos, oldLen);
			this->Insert(pos, newStr);

			nextPos = pos + strlen (newStr);
		}
	}
}

//-----------------------------------------------------------------------------
void TXString::Replace(const char *oldStr, char newChar, bool bIgnoreCase)
{
	const char newStr[2] = { newChar, 0 };
	Replace(oldStr, newStr, bIgnoreCase);
}

//-----------------------------------------------------------------------------
void TXString::MakeUpper()
{
	ASSERTTHISCLASS(kEveryone);

	ECharacterType* pCharByteType = NULL;

	if (fLength > 0) {
		pCharByteType = NEW_NOTHROW ECharacterType[fLength];
	} 		
	if (pCharByteType) {
		GetSJISCharacterTypeArray(*this, pCharByteType, fLength);
	}

	for (size_t i=1; i <= fLength; ++i) {
		ECharacterType currCharacterType = eSingle;

		//check character to make sure we don't have part of a double-byte character - JLN 10/22/01
		if (pCharByteType)
			currCharacterType = pCharByteType[i-1];
		
		if (currCharacterType == eSingle)
			fData[i] = toupper(fData[i]);
	}

	delete[] pCharByteType;
	pCharByteType	= NULL;
}

//-----------------------------------------------------------------------------
void TXString::MakeLower()
{
	ASSERTTHISCLASS(kEveryone);

	ECharacterType* pCharByteType = NULL;

	if (fLength > 0) {
		pCharByteType = NEW_NOTHROW ECharacterType[fLength];
	} 		
	if (pCharByteType) {
		GetSJISCharacterTypeArray(*this, pCharByteType, fLength);
	}

	for (size_t i=1; i <= fLength; ++i) {
		ECharacterType currCharacterType = eSingle;

		//check character to make sure we don't have part of a double-byte character - JLN 10/22/01
		if (pCharByteType)
			currCharacterType = pCharByteType[i-1];
		
		if (currCharacterType == eSingle)
			fData[i] = tolower(fData[i]);
	}

	delete[] pCharByteType;
	pCharByteType	= NULL;
}

//-----------------------------------------------------------------------------
void TXString::MakeReverse()
{
	ASSERTTHISCLASS(kEveryone);
	for (size_t i=1, j=fLength; i <= fLength/2; ++i,--j) {
		char ch = fData[i];
		fData[i] = fData[j];
		fData[j] = ch;
	}
}

//-----------------------------------------------------------------------------
void TXString::TrimRight()
{
	ASSERTTHISCLASS(kEveryone);
	int pos = ReverseFindNotOneOf(" \t");
	if (pos >= 0) {
		++pos;
		int len = fLength - pos;
		if (len > 0)
			Delete(pos, len);
	}
}

//-----------------------------------------------------------------------------
void TXString::TrimLeft()
{
	ASSERTTHISCLASS(kEveryone);
	int pos = FindNotOneOf(" \t");
	if (pos >= 0)
		Delete(0, pos);
	else if (this->GetLength() > 0)
		// Everything in the string is either a space or a tab
		//	so remove everything
		Delete (0, this->GetLength());
}

//-----------------------------------------------------------------------------
int TXString::Find(char ch, int nStart) const
{ 
	ASSERTTHISCLASS(kEveryone);

	int pos = -1;

	long strLength = this->GetLength();
	ECharacterType* pCharByteType = NULL;

	if (strLength > 0) {
		pCharByteType = NEW_NOTHROW ECharacterType[strLength];
	} 		
	if (pCharByteType) {
		GetSJISCharacterTypeArray(*this, pCharByteType, strLength);
	}

	for (TConstForwardIterator i(*this, nStart); i; ++i) {

		//check character to make sure we don't have part of a double-byte character - JLN 10/22/01
		ECharacterType currCharacterType = eSingle;
		if (pCharByteType)
			currCharacterType = pCharByteType[i.GetPosition()];
	
		if (i.GetChar() == ch && currCharacterType != eLead && currCharacterType != eTrail) {
			pos = i.GetPosition();
			break;
		}
	}

	delete[] pCharByteType;
	return pos;
}

//-----------------------------------------------------------------------------
int TXString::FindNoSJISCheck(char ch, int nStart) const
{ 
	ASSERTTHISCLASS(kEveryone);

	int pos = -1;

	for (TConstForwardIterator i(*this, nStart); i; ++i) {
	
		if (i.GetChar() == ch) {
			pos = i.GetPosition();
			break;
		}
	}

	return pos;
}

//-----------------------------------------------------------------------------
int TXString::FindOneOf(const TXString &charSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTCLASS(kEveryone, &charSet);

	for (TConstForwardIterator i(*this); i; ++i) {
		int pos = charSet.Find( i.GetChar() );
		if (pos >= 0)
			return i.GetPosition();;
	}
	return -1;
}

//-----------------------------------------------------------------------------
int TXString::FindOneOf(const char *szCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, szCharSet, 1, 0);
	TXString str(szCharSet);
	return FindOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::FindOneOf(const unsigned char *psCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, psCharSet, 1, 0);
	TXString str(psCharSet);
	return FindOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::FindNotOneOf(const TXString &charSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTCLASS(kEveryone, &charSet);

	for (TConstForwardIterator i(*this); i; ++i) {
		int pos = charSet.Find( i.GetChar() );
		if (pos < 0)
			return i.GetPosition();
	}
	return -1;
}

//-----------------------------------------------------------------------------
int TXString::FindNotOneOf(const char *szCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, szCharSet, 1, 0);
	TXString str(szCharSet);
	return FindNotOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::FindNotOneOf(const unsigned char *psCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, psCharSet, 1, 0);
	TXString str(psCharSet);
	return FindNotOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::Find(size_t nFirst, const char *szSub, bool bIgnoreCase) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, szSub, 1, 0);
	int loc;
	if ( ! fLength || nFirst >= fLength )
		loc = -1;
	else if (bIgnoreCase) {
		TXString str(*this), sub(szSub);
		str.MakeUpper();
		sub.MakeUpper();
		loc = str.Find(nFirst, sub, false);
	}
	else {
		TXString str;
		if (nFirst > 0)
			str = this->Mid(nFirst);
		else
			str = *this;
			
		const char *strBuf = (const char *) str;

#if GS_WIN
		// _tcsstr is the Windows generic function that ensures the proper type of character is
		// taken into consideration during the substring find. This corrects issues with Japanese
		// characters. strstr worked appropriately for Mac but not for Windows.
		const char *pos = _tcsstr(str, szSub);
#else
		const char *pos = strstr(str, szSub);
#endif

		if (pos == NULL)
			loc = -1;
		else
			loc = pos - strBuf + nFirst;
	}
	return loc;
}

//-----------------------------------------------------------------------------
int TXString::Find(size_t nFirst, const unsigned char *psSub, bool bIgnoreCase) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, psSub, 1, 0);
	int loc;
	if ( ! fLength || nFirst >= fLength )
		loc = -1;
	else if (bIgnoreCase) {
		TXString str(*this), sub(psSub);
		str.MakeUpper();
		sub.MakeUpper();
		loc = str.Find(nFirst, sub, false);
	}
	else {
		char sz[256];
		memmove(sz, &psSub[1], psSub[0]);
		sz[psSub[0]] = 0;
		loc = this->Find(sz);
	}
	return loc;
}

//-----------------------------------------------------------------------------
int TXString::ReverseFind(char ch, bool checkForJISChar) const
{
	ASSERTTHISCLASS(kEveryone);
	int pos = -1;

	long strLength = this->GetLength();
	ECharacterType* pCharByteType = NULL;

	if (checkForJISChar) {
		if (strLength > 0) {
			pCharByteType = NEW_NOTHROW ECharacterType[strLength];
		} 		
		if (pCharByteType) {
			GetSJISCharacterTypeArray(*this, pCharByteType, strLength);
		}
	}
	
	for (TConstReverseIterator i(*this); i; --i) {

		ECharacterType currCharacterType = eSingle;
		
		if (checkForJISChar) {
			//check character to make sure we don't have part of a double-byte character - JLN 10/22/01
			currCharacterType = pCharByteType[i.GetPosition()];
		}
		
		if (i.GetChar() == ch && currCharacterType != eLead && currCharacterType != eTrail) {
			pos = i.GetPosition();
			break;
		}
	}

	delete[] pCharByteType;

	return pos;
}

//-----------------------------------------------------------------------------
int TXString::ReverseFindOneOf(const TXString &charSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTCLASS(kEveryone, &charSet);

	for (TConstReverseIterator i(*this); i; --i) {
		int pos = charSet.Find( i.GetChar() );
		if (pos >= 0)
			return pos;
	}
	return -1;
}

//-----------------------------------------------------------------------------
int TXString::ReverseFindOneOf(const char *szCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, szCharSet, 1, 0);
	TXString str(szCharSet);
	return ReverseFindOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::ReverseFindOneOf(const unsigned char *psCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, psCharSet, 1, 0);
	TXString str(psCharSet);
	return ReverseFindOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::ReverseFindNotOneOf(const TXString &charSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTCLASS(kEveryone, &charSet);

	for (TConstReverseIterator i(*this); i; --i) {
		int pos = charSet.Find( i.GetChar() );
		if (pos < 0)
			return i.GetPosition();
	}
	return -1;
}

//-----------------------------------------------------------------------------
int TXString::ReverseFindNotOneOf(const char *szCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, szCharSet, 1, 0);
	TXString str(szCharSet);
	return ReverseFindNotOneOf(str);
}

//-----------------------------------------------------------------------------
int TXString::ReverseFindNotOneOf(const unsigned char *psCharSet) const
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, psCharSet, 1, 0);
	TXString str(psCharSet);
	return ReverseFindNotOneOf(str);
}

#if _MINICAD_

//-----------------------------------------------------------------------------
bool TXString::Load(short strListID, short index, EEmptyHandling allowEmpty)
{
	ASSERTTHISCLASS(kEveryone);
	GSStr255 s;
	RsrcMgr::GSGetIndString(s, strListID, index, allowEmpty);
	SetEqual(s);

	return !IsEmpty();
}

//-----------------------------------------------------------------------------
bool TXString::Load(short strID, EEmptyHandling allowEmpty)
{
	ASSERTTHISCLASS(kEveryone);
	StringHandle sh = RsrcMgr::GSGetString(strID, allowEmpty);
	if (sh) {
		size_t stringSize = GetHandleSize((Handle) sh);

		SetEqual((char*)*sh, stringSize);
		RsrcMgr::GSReleaseResource((Handle) sh);
		return true;
	}
	else {
		return false;
	}
}

#if _WINDOWS

//DECLARE_HANDLE(HINSTANCE);

typedef const char *			LPCSTR;

#define LoadString				LoadStringA
#define GetModuleHandle			GetModuleHandleA

extern "C" __declspec(dllimport) int __stdcall		LoadStringA(HINSTANCE, UINT, LPSTR, int);
extern "C" __declspec(dllimport) HINSTANCE __stdcall	GetModuleHandleA(LPCSTR);

//-----------------------------------------------------------------------------
bool TXString::WLoad(int index, EEmptyHandling allowEmpty)
{
	bool ok = true;

#if GS_INTERFACE_FACTORING
	while (ok && (size_t)::LoadString(GetModuleHandle(NULL), index, &fData[1], fBufSize) >= fBufSize-1)
#else
	while (ok && (size_t)::GS_LoadString(index, &fData[1], fBufSize) >= fBufSize-1)
#endif

		ok = EnsureBufferSize(fBufSize + 64);
	if (ok)
		UpdateLength();
	else
		Empty();

	#if TEST && NNA_WARN_ABOUT_EMPTY_STRING_RESOURCES
	if (IsEmpty() && allowEmpty == eDontAllowEmptyResult) {
		DSTOP((kEveryone, "TXString::WLoad was unable to load string index %d!", index));
	}
	#else
	allowEmpty;
	#endif

	return ok;
}
#endif // _WINDOWS

#else // _MINICAD_

// Implement thise for the plugins outside the core
bool TXString::Load(short strListID, short index, EEmptyHandling allowEmpty)
{
	ASSERTTHISCLASS(kEveryone);
	Str255		theString;
	::GS_GetResourceString( gCBP, strListID, index, theString, true, EEmptyStringResourceHandling(allowEmpty) );
	this->SetEqual( theString );

	#if TEST && NNA_WARN_ABOUT_EMPTY_STRING_RESOURCES
	if (IsEmpty() && allowEmpty == eDontAllowEmptyResult) {
		DSTOP((kEveryone, "TXString::Load was unable to load STR# %d index %d!", strListID, index));
	}
	#else
	allowEmpty;
	#endif

	return !IsEmpty();
}


#endif _MINICAD_

//-----------------------------------------------------------------------------
void TXString::Format(const char *szFormat, ...)
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTSTRING(kEveryone, szFormat, 1, 0);
	va_list marker;
	va_start(marker, szFormat);
	VFormat(szFormat, marker);
	va_end(marker);
}

//-----------------------------------------------------------------------------
void TXString::Format(const unsigned char *psFormat, ...)
{
	ASSERTTHISCLASS(kEveryone);
	ASSERTPSTRING(kEveryone, psFormat, 1, 0);
	TXString str(psFormat);
	va_list marker;
	va_start(marker, psFormat);
	VFormat((const char *)str, marker);
	va_end(marker);
}

//-----------------------------------------------------------------------------
void TXString::Format(short strList, short index, ...)
{
	ASSERTTHISCLASS(kEveryone);
	#if _MINICAD_
		unsigned char fmt[257];
		RsrcMgr::GSGetIndString(fmt, strList, index);
		if (fmt[0] > 0) {
			fmt[fmt[0]+1] = 0;
			va_list marker;
			va_start(marker, index);
			VFormat((const char*)&fmt[1], marker);
			va_end(marker);
		}
		else {
			Empty();
		}
	#else // _MINICAD_
		TXString	format( strList, index );
		const char*	szFormat	= format;
		va_list		marker;
		va_start( marker, index );
		this->VFormat( szFormat, marker );
		va_end( marker );
	#endif
}

//-----------------------------------------------------------------------------
void TXString::itoa(int value)
{
	ASSERTTHISCLASS(kEveryone);
	char sz[12];
	#if _WINDOWS
		::_itoa(value, sz, 10);
	#else
		sprintf(sz, "%d", value);
	#endif
	*this = sz;
}

//-----------------------------------------------------------------------------
int TXString::atoi() const
{
	ASSERTTHISCLASS(kEveryone);
	return ::atoi( *this );
}

//-----------------------------------------------------------------------------
Real64 TXString::atof() const
{
	ASSERTTHISCLASS(kEveryone);
	return ::atof( *this );
}

//-----------------------------------------------------------------------------
void TXString::FreeExtra()
{
	ASSERTTHISCLASS(kEveryone);
	VERIFYN(kDave, ReallocBuffer(fLength + 2));
}


///////////////////////////////////////////////////////////////////////////////
// TXString protected methods

//-----------------------------------------------------------------------------
void TXString::SetEqual(const char* sz, size_t len)
{
	ASSERTTHISCLASS(kEveryone);
	if (EnsureBufferSize(len)) {
		strncpy(&fData[1], sz, len);
		UpdateLength(len);
	}
}

//-----------------------------------------------------------------------------
void TXString::SetEqual(char ch, size_t cnt)
{
	ASSERTTHISCLASS(kEveryone);
	if (EnsureBufferSize(cnt)) {
		memset(&fData[1], ch, cnt);
		UpdateLength(cnt);
	}
}

//-----------------------------------------------------------------------------
bool TXString::EnsureBufferSize(size_t size)
// This function ensures that the buffer is at least large enough to hold 'size'
// characters of string data. 'size' should NOT include the pascal length byte
// or the C-string NULL character.
//
// Return true if buffer already large enough or if it was enlarged.
{
	const size_t kGrowChunkSize = 16; // Grow the buffer in chunks of 16 bytes.

	size += 2; // Make room for Pascal string length byte and C-string NULL character.

	// See if the buffer is getting larger, if not, don't do anything.
	if (size > fBufSize) {

		// Grow the buffer in chunks of 16 bytes.
		if (size % kGrowChunkSize) {
			size = (size / kGrowChunkSize + 1) * kGrowChunkSize;
		}

		// Resize the buffer.
		return ReallocBuffer(size);
	}
	return true;
}

//-----------------------------------------------------------------------------
bool TXString::ReallocBuffer(size_t newSize)
// Return true if memory was allocated and string was copied.
{
	DSTOPIF(newSize < 2, (kDave, "TXString buffer must be large enough to hold Pascal length byte and C terminator (newSize = %lu)", newSize));

	if (newSize == fBufSize) // If no size change, just return.
		return true;

	/////////////////////////////////////////////////////////////////////////
	// XXX Vlado - Better memory for Core <-> plug-in communication
	char*	newBuf			= NULL;
	char	memoryCode		= 0;
	size_t	staticBufSize	= sizeof(fStaticStringBuffer) / sizeof(char);

	// allow static buffer use only if we create new string
	// or come from static buffer based string.
	// This is to support compatibility with plug-ins compiled with SDK TXString without static buffer
	bool	bAllowStaticBuffer	= true;
	if ( fData ) {
		char*	pTheData		= fData - 1;
		char	memoryCode		= *pTheData;
		bAllowStaticBuffer		= memoryCode == 2;
	}

	if ( bAllowStaticBuffer && newSize+2 < staticBufSize ) {
		memoryCode	= 2;
		newBuf		= fStaticStringBuffer;
	}
	else {
#if _MINICAD_
		memoryCode	= 1;
		newBuf		= (char*) Mem_NewPtr( newSize + 1 );
#else
		memoryCode	= gCBP ? 1 : 0;
		if ( gCBP )	newBuf	= (char*) ::GS_NewPtr( gCBP, newSize + 1 );
		else		newBuf	= (char*) malloc( newSize + 1 );
#endif
	}

	if ( newBuf ) {
		*newBuf		= memoryCode;
		newBuf		+= 1;			// move to the real data
	}
	/////////////////////////////////////////////////////////////////////////

	if (newBuf) {
		const size_t minSize = ( newSize < fBufSize ? newSize : fBufSize );
		if (fData) {
			::memcpy(newBuf, fData, minSize);

			this->FreeBuffer();
		}
		fData = newBuf;
		fCStringData = fData + 1;

		fBufSize = newSize;
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------------
void  TXString::FreeBuffer()
{
	/////////////////////////////////////////////////////////////////////////
	// XXX Vlado - Better memory for Core <-> plug-in communication
	if ( fData ) {
		char*	pTheData		= fData - 1;
		char	memoryCode		= *pTheData;
		if ( memoryCode == 2 || pTheData == fStaticStringBuffer ) {
			ASSERTN( kVStanev, memoryCode == 2 );
			ASSERTN( kVStanev, pTheData == fStaticStringBuffer );
		}
		else {
#if _MINICAD_
			Mem_DisposePtr( (Ptr) pTheData );
#else
			if ( memoryCode == 1 ) {
				// XXX Vlado
				// this may produce leaks when it was marked to use CBP
				// but now the CBP is NULL
				ASSERTN( kVStanev, gCBP );
				if ( gCBP ) ::GS_DisposePtr( gCBP, pTheData );
			}
			else {
				free( pTheData );
			}
#endif
		}
	}
	/////////////////////////////////////////////////////////////////////////
}

//-----------------------------------------------------------------------------
void TXString::UpdateLength()
{
	size_t len = (size_t)lstrlen(&fData[1]);
	UpdateLength(len);
}

//-----------------------------------------------------------------------------
void TXString::UpdateLength(size_t len)
{
	fLength = len;
	fData[len+1] = 0;
	ASSERTN(kDave, len == (size_t)lstrlen(&fData[1]));
	*(unsigned char*)fData = (unsigned char)(len > 255 ? 255 : len);
}

//-----------------------------------------------------------------------------
char &TXString::GetCharRef(size_t nIndex, bool bAllowNull) const
{
	static char deadchar = 0;
	if ( bAllowNull ? (nIndex <= fLength) : (nIndex < fLength) ) {
		++nIndex;	// Skip over Pascal string length.
		return fData[nIndex];
	}
	else return deadchar;
}

//-----------------------------------------------------------------------------
TXString TXString::Span(bool include, const char *sz) const
{
	int end = (include ? FindNotOneOf(sz) : FindOneOf(sz));

	TXString part;
	if (end > 0)
		part = this->Left(end);
	return part;
}

//-----------------------------------------------------------------------------
TXString TXString::Span(bool include, const unsigned char *ps) const
{
	TXString c(ps);
	return Span(include, (const char*)c );
}

//-----------------------------------------------------------------------------
void TXString::VFormat(const char *szFormat, va_list argPtr)
{
	const int kBufferSize = 10240;
	EnsureBufferSize(kBufferSize);
	int len = vsprintf(&fData[1], szFormat, argPtr);
	DSTOPIF(len >= kBufferSize, (kDave, "TXString::Format() - exceeded maximum buffer length of %d by %d bytes.", kBufferSize, len - kBufferSize + 1));
	UpdateLength(len);
	FreeExtra();
}


//---------------------------------------------------------------------------------------
short TXString::AnsiToMac()
{
	// Commented out DSTOP below.  I think the design was that this can be called from both platforms,
	// so we don't have to check what platform we're on everywhere this is called. [BAF 6/23/04]
	#if 0
		DSTOP((kAyo, "Should this function be used on the Macintosh platform?\n"));
	#endif	//#if GS_MAC


	long stringSize = 0;



#if GS_WIN
	if (!this->IsEmpty()) {

		const unsigned char *src = (ConstGSStringPtr)fData+1;
		char *dst;
		long dst_size = fLength * 3 + 1;

		dst = NEW_NOTHROW char[dst_size];  //When converted may expand

		if (dst) {
			char* dstPos = dst;
			long nChars = fLength;
			short copySize = 0;

			#ifdef _DEBUG
			const short kBlockSize = 32; // give it a workout
			#else
			const short kBlockSize = 16384;
			#endif

			ASSERTN(kPaul, src != (unsigned char*)dst);
			memset(dst, 0, dst_size);
			if (src && VERIFYN(kPaul, nChars <= dst_size - 1)) {
				long blockCount = kBlockSize;

				for (blockCount = kBlockSize; blockCount < nChars; blockCount += kBlockSize, src += kBlockSize, dstPos += copySize) {
					#if _MINICAD_
						copySize = WinAnsiToMacBuf((const unsigned char *) src, (unsigned char *) dstPos, kBlockSize);
					#else
						TAnsiMacBufDataStructAlturaRemoval data;
						data.feFunctionEnumeration	= eAnsiToMacBuf;		
						data.fpInAnsiString			= src;						
						data.fpOutMacString			= (unsigned char*)dstPos;
						data.fStringLength			= kBlockSize;				
						
						VERIFYN(kChao, GS_Kludge(gCBP, kKludgeAlturaBufFunctionAccess, &data, &copySize));
					#endif
				}
				blockCount -= kBlockSize;

				#if _MINICAD_
					copySize = WinAnsiToMacBuf((const unsigned char *) src, (unsigned char *) dstPos, (short)(nChars - blockCount));
				#else
					TAnsiMacBufDataStructAlturaRemoval data;
					data.feFunctionEnumeration	= eAnsiToMacBuf;
					data.fpInAnsiString			= src;			
					data.fpOutMacString			= (unsigned char*)dstPos;	
					data.fStringLength			= (short)(nChars - blockCount);	
					
					VERIFYN(kChao, GS_Kludge(gCBP, kKludgeAlturaBufFunctionAccess, &data, &copySize));
				#endif

				#ifdef TEST
					const long overwrite = ((dstPos + copySize) - (char *) dst) - dst_size;
					DSTOPIF(overwrite > 0, (kJimG, "TXString::AnsiToMac overwrote buffer by %ld bytes!", overwrite));
				#endif // TEST

			}

			stringSize = (dstPos + copySize) - (char *) dst;
			
			SetEqual(dst);

			delete [] dst;

		}
	}
#endif	//#if GS_WIN


	// using DemoteTo here causes a bunch of library code to be pulled into the plug-ins
	// and causes some build problems with CreateElementList - PCP

	return (short) stringSize; //DemoteTo<short, long>(kPaul, stringSize);
	
}

void TXString::UTF8ToMac()
{
#if GS_MAC

	CFIndex bufSize = GetLength()*2;  // the size of the Mac-encoded string should typically be smaller than the UTF-8 encoding...
	char macBuf[bufSize];
	
	CFStringRef cfstr = CFStringCreateWithCString(NULL, fData + 1, kCFStringEncodingUTF8);
	if (CFStringGetCString(cfstr, macBuf, bufSize, GetApplicationTextEncoding()))
		SetEqual(macBuf);
		
	CFRelease(cfstr);
	
#endif
}

//---------------------------------------------------------------------------------------
void TXString::MacToWinNewLine ()
{

	// Replace all characters with value ascii 10 (line feed) with characters with 
	//	two characters ascii 13 followed by ascii 10 (carriage return, line feed).
	//	If at least one windows-style endline is found then don't do anything.  If there
	//	is a mixed set of line endings then there's something special about the string
	//	and we should not touch it.

	char pszWindowsEndLine [3];
	pszWindowsEndLine[0] = 13;
	pszWindowsEndLine[1] = 10;
	pszWindowsEndLine[2] = 0;

	if (this->Find (0, pszWindowsEndLine) == -1)
	{
		char pszMacEndLine [2];
		pszMacEndLine[0] = 13;
		pszMacEndLine[1] = 0;

		// There are no windows end lines in this string therefore
		//	we can safely replace any line feeds we find with carriage return, line feeds.
		this->Replace (pszMacEndLine, pszWindowsEndLine);
	}

}

//---------------------------------------------------------------------------------------
void TXString::WinToMacNewLine ()
{
	char pszWindowsEndLine [3];
	pszWindowsEndLine[0] = 13;
	pszWindowsEndLine[1] = 10;
	pszWindowsEndLine[2] = 0;

	char pszMacEndLine [2];
	pszMacEndLine[0] = 10;
	pszMacEndLine[1] = 0;

	// Repalace all carriage return, line feed pair with just a line feed.
	this->Replace (pszWindowsEndLine, pszMacEndLine);
}



// the whole point of this function is to allow larger strings than 32000 bytes to be processed - it should return a long
// this system can be dramatically simplified for VW12 - PCP


//---------------------------------------------------------------------------------------
short TXString::MacToAnsi()
{
	// Removed the following DSTOP.  I think the idea is that this gets called in many places
	// and where it's called, we don't need to put the call inside #if GS_WIN, but it will noop
	// for GS_MAC.  [BAF 6/9/04]
	//
	//#if GS_MAC
	//	DSTOP((kAyo, "Should this function be used on the Macintosh platform?\n"));
	//#endif	//#if GS_MAC

	long stringSize = 0;


#if GS_WIN
	if (!this->IsEmpty()) {
		const unsigned char *src = (ConstGSStringPtr)fData+1;

		// dst_size needs to be one larger than the largest possible size the destination can grow to
		// because it is treated as a c string at the end when it is assigned


		const long dst_size = fLength * 4 + 1;
		char *dst = NEW_NOTHROW char[dst_size];  //When converted may expand

		if (dst) {
			char* dstPos = dst;
			long nChars = fLength;
			short copySize = 0;

			#ifdef _DEBUG
			const short kBlockSize = 32; // give it a workout
			#else
			const short kBlockSize = 16384;
			#endif

			ASSERTN(kPaul, (char*)src != dst);
			memset(dst, 0, dst_size);
			if (src && VERIFYN(kPaul, nChars <= dst_size - 1)) {
				long blockCount;

				for(blockCount = kBlockSize; blockCount < nChars; blockCount += kBlockSize, src += kBlockSize, dstPos += copySize) {
					#if _MINICAD_
						copySize = WinMacToAnsiBuf((const unsigned char *)src, (unsigned char *)dstPos, kBlockSize);
					#else
						TAnsiMacBufDataStructAlturaRemoval data;
						data.feFunctionEnumeration	= eMacToAnsiBuf;
						data.fpInMacString			= src;
						data.fpOutAnsiString		= (unsigned char *)dstPos;
						data.fStringLength			= kBlockSize;
						data.fConversionFlag		= 0;
						
						VERIFYN(kChao, GS_Kludge(gCBP, kKludgeAlturaBufFunctionAccess, &data, &copySize));
					#endif
				}
				blockCount -= kBlockSize;
				
				#if _MINICAD_
					copySize = WinMacToAnsiBuf((const unsigned char *)src, (unsigned char *)dstPos, (short)(nChars - blockCount));
				#else
					TAnsiMacBufDataStructAlturaRemoval data;
					data.feFunctionEnumeration	= eMacToAnsiBuf;
					data.fpInMacString			= src;
					data.fpOutAnsiString		= (unsigned char *)dstPos;
					data.fStringLength			= (short)(nChars - blockCount);
					data.fConversionFlag		= 0;
					
					VERIFYN(kChao, GS_Kludge(gCBP, kKludgeAlturaBufFunctionAccess, &data, &copySize));
				#endif

				#ifdef TEST
					const long overwrite = ((dstPos + copySize) - dst) - dst_size;
					DSTOPIF(overwrite > 0, (kJimG, "VCMacToANSIString overwrote buffer by %ld bytes!", overwrite));
				#endif // TEST

			}

			stringSize = (dstPos + copySize) - (char *) dst;

			// this copies dst as a c string into the current TXString
			// in VW12 we should use the version of SetEqual which takes size as a parameter - PCP

			SetEqual(dst);

			delete [] dst;

		}
	}
#endif	//#if GS_WIN

	return (short) stringSize; //DemoteTo<short, long>(kPaul, stringSize);
}

#if _MINICAD_ //&& GS_WIN //We now need this functionality on Mac

/////////////////////////////////////////////////////////////////////////////////
// XXX_JDW_ALTURA [2003-02-18] - probably temporary placement


unsigned char AnsiToMacTab[] = 
/* 80 - 87 */	   {0xDB, 0xB0, 0xE2, 0xC4, 0xE3, 0xC9, 0xA0, 0xE0,
/* 88 - 8F */		0xF6, 0xE4, 0xDE, 0xDC, 0xCE, 0xB2, 0xB3, 0xB6,
/* 90 - 97 */		0xB1, 0xD4, 0xD5, 0xD2, 0xD3, 0xA5, 0xD0, 0xD1,
/* 98 - 9F */		0xF7, 0xAA, 0xDF, 0xDD, 0xCF, 0xBA, 0xFD, 0xD9,
/* A0 - A7 */		0xCA, 0xC1, 0xA2, 0xA3, 0xDB, 0xB4, 0xBD, 0xA4,
/* A8 - AF */		0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0xF0, 0xA8, 0xF8,

// Don't remap ± to Æ
/* B0 - B7 */		0xA1, 0xB1, 0xC3, 0xC5, 0xAB, 0xB5, 0xA6, 0xE1,

/* B8 - BF */		0xFC, 0xFF, 0xBC, 0xC8, 0xF9, 0xDA, 0xD7, 0xC0,
/* C0 - C7 */		0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82,
/* C8 - CF */		0xE9, 0x83, 0xE6, 0xE8, 0xED, 0xEA, 0xEB, 0xEC,
/* D0 - D7 */		0xF5, 0x84, 0xF1, 0xEE, 0xEF, 0xCD, 0x85, 0xFB,

// remap Y with acute accent for Iceland
/* D8 - DF */		0xAF, 0xF4, 0xF2, 0xF3, 0x86, 0xA0, 0xB8, 0xA7,

/* E0 - E7 */		0x88, 0x87, 0x89, 0x8B, 0x8A, 0x8C, 0xBE, 0x8D,
/* E8 - EF */		0x8F, 0x8E, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95,
/* F0 - F7 */		0xFE, 0x96, 0x98, 0x97, 0x99, 0x9B, 0x9A, 0xD6,

// remap y with acute accent for Iceland
/* F8 - FF */		0xBF, 0x9D, 0x9C, 0x9E, 0x9F, 0xE0, 0xB9, 0xD8};


unsigned char MacToAnsiTab[] = 
/* 80 - 87 */	   {0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
/* 88 - 8F */		0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
/* 90 - 97 */		0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
/* 98 - 9F */		0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,

// remap Y with acute accent for Iceland
/* A0 - A7 */		0xDD, 0xB0, 0xA2, 0xA3, 0xA7, 0x95, 0xB6, 0xDF,

/* A8 - AF */		0xAE, 0xA9, 0x99, 0xB4, 0xA8, 0x80, 0xC6, 0xD8,
/* B0 - B7 */		0x81, 0xB1, 0x8D, 0x8E, 0xA5, 0xB5, 0x8F, 0x90,
/* B8 - BF */		0xDE, 0xFE, 0x9D, 0xAA, 0xBA, 0xA6, 0xE6, 0xF8,
/* C0 - C7 */		0xBF, 0xA1, 0xAC, 0xB2, 0x83, 0xB3, 0xB1, 0xAB,
/* C8 - CF */		0xBB, 0x85, 0xA0, 0xC0, 0xC3, 0xD5, 0x8C, 0x9C,
/* D0 - D7 */		0x96, 0x97, 0x93, 0x94, 0x91, 0x92, 0xF7, 0xBE,
/* D8 - DF */		0xFF, 0x9F, 0xBD, 0x80, 0x8B, 0x9B, 0x8A, 0x9A,

// remap y with acute accent for Iceland
/* E0 - E7 */		0xFD, 0xB7, 0x82, 0x84, 0x89, 0xC2, 0xCA, 0xC1,

/* E8 - EF */		0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
/* F0 - F7 */		0xAD, 0xD2, 0xDA, 0xDB, 0xD9, 0xD0, 0x88, 0x98,
/* F8 - FF */		0xAF, 0xBC, 0xDD, 0xD7, 0xB8, 0x9E, 0xF0, 0xB9};

//
//New Greek Tables
//The Mac uses Greek Table #10006
//Windows uses Greek Table #1253
//See "Developing International Software" by Nadine Kano
unsigned char AnsiGreekToMacGreekTab[] = 
/* 80 - 87 */	       {0xFF, 0xFF, 0x2C, 0x66, 0xD3, 0xC9, 0xA0, 0xFF,
/* 88 - 8F */		0xFF, 0x98, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* 90 - 97 */		0xFF, 0xD4, 0xD5, 0xD2, 0xD3, 0x96, 0xD0, 0xD1,
/* 98 - 9F */		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
/* A0 - A7 */		0xCA, 0x87, 0xCD, 0x92, 0xFF, 0xB4, 0x9B, 0xAC,
/* A8 - AF */		0x8C, 0xA9, 0xFF, 0xC7, 0xC2, 0xFF, 0xA8, 0xD1,
/* B0 - B7 */		0xAE, 0xB1, 0x82, 0x84, 0x8B, 0xFF, 0xFF, 0xAF,
/* B8 - BF */		0xCE, 0xD7, 0xD8, 0xC8, 0xD9, 0x97, 0xDA, 0xDF,
/* C0 - C7 */		0xDD, 0xB0, 0xB5, 0xA1, 0xA2, 0xB6, 0xB7, 0xB8,
/* C8 - CF */		0xA3, 0xB9, 0xBA, 0xA4, 0xBB, 0xC1, 0xA5, 0xC3,
/* D0 - D7 */		0xA6, 0xC4, 0xFF, 0xAA, 0xC6, 0xCB, 0xBC, 0xCC,
/* D8 - DF */		0xBE, 0xBF, 0xAB, 0xBD, 0xC0, 0xDB, 0xDC, 0xDD,
/* E0 - E7 */		0xFE, 0xE1, 0xE2, 0xE7, 0xE4, 0xE5, 0xFA, 0xE8,
/* E8 - EF */		0xF5, 0xE9, 0xEB, 0xEC, 0xED, 0xEE, 0xEA, 0xEF,
/* F0 - F7 */		0xF0, 0xF2, 0xF7, 0xF3, 0xF4, 0xF9, 0xE6, 0xF8,
/* F8 - FF */		0xE3, 0xF6, 0xFB, 0xFC, 0xDE, 0xE0, 0xF1, 0xFF};

unsigned char MacGreekToAnsiGreekTab[] = 
/* 80 - 87 */	       {0xC1, 0x31, 0xB2, 0xC5, 0xB3, 0xCF, 0x55, 0xA1,
/* 88 - 8F */		0x61, 0x61, 0x61, 0xB4, 0xA8, 0x63, 0x65, 0x65,
/* 90 - 97 */		0x65, 0x65, 0xA3, 0x99, 0x69, 0x69, 0x95, 0xBD,
/* 98 - 9F */		0x89, 0x6F, 0x6F, 0xA6, 0x3F, 0x75, 0x75, 0x75,
/* A0 - A7 */		0x86, 0xC3, 0xC4, 0xC8, 0xCB, 0xCE, 0xD0, 0xE2,
/* A8 - AF */		0xAE, 0xA9, 0xD3, 0xDA, 0xA7, 0xA0, 0xB0, 0xB7,
/* B0 - B7 */		0xC1, 0xB1, 0x80, 0x80, 0xA5, 0xC2, 0xC5, 0xC6,
/* B8 - BF */		0xC7, 0xC9, 0xCA, 0xCC, 0xD6, 0xDB, 0xD8, 0xD9,
/* C0 - C7 */		0xDC, 0xCD, 0xAC, 0xCF, 0xD1, 0x80, 0xD4, 0xAB,
/* C8 - CF */		0xBB, 0x85, 0xA0, 0xD5, 0xD7, 0xA2, 0xB8, 0x80,
/* D0 - D7 */		0x96, 0x97, 0x93, 0x94, 0x91, 0x92, 0x80, 0xB9,
/* D8 - DF */		0xBA, 0xBC, 0xBE, 0xDD, 0xDE, 0xDF, 0xFC, 0xBF,
/* E0 - E7 */		0xFD, 0xE1, 0xE2, 0xF8, 0xE4, 0xE5, 0xF6, 0xE3,
/* E8 - EF */		0xE7, 0xE9, 0xEE, 0xEA, 0xEB, 0xEC, 0xED, 0xEF,
/* F0 - F7 */		0xF0, 0xFE, 0xF1, 0xF3, 0xF4, 0xE8, 0xF9, 0xF2,
/* F8 - FF */		0xF7, 0xF5, 0xE6, 0xFA, 0xFB, 0xC0, 0xE0, 0x80};



void GS_STRING_API SetMacToAnsiTablesToGreek()
{
	memcpy (AnsiToMacTab, AnsiGreekToMacGreekTab, 128);
	memcpy (MacToAnsiTab, MacGreekToAnsiGreekTab, 128);
}

#if GS_MAC
	typedef unsigned char BYTE;
#endif

short GS_STRING_API WinAnsiToMacBuf(unsigned char const *AnsiStr, unsigned char *MacStr, short ansiLen)
{
	register BYTE 	character;
	unsigned char	*origMacStr;
	origMacStr = MacStr;

	bool farEastVersion = CoreAPI::IsSystemFarEast();
	ECharacterType* pCharByteType = nil;
#if GS_WIN
	if (farEastVersion && CoreAPI::IsSystemJapanese()) {
		pCharByteType = NEW_NOTHROW ECharacterType[ansiLen];
		if (pCharByteType) {
			GetSJISCharacterTypeArray((char*)AnsiStr, pCharByteType, ansiLen);
		}
	}
#endif

	for (int i = 0; i < ansiLen; i++)
	{
		character = *AnsiStr++;

		if (farEastVersion) {
			/* There are a few double byte characters that are not standard shift-JIS characters and that are */
			/* defined both in MacJapanese and in the Win shift-JIS with different codes.  So we need to check */
			/* for one of these special characters and if found, translate it. */
			Boolean isSpecialChar = false;
#if GS_WIN
			if (pCharByteType) {

				ECharacterType currCharacterType = pCharByteType[i];
	
				if (currCharacterType == eLead && character == 0x87) {
					BYTE nextChar = *AnsiStr;
					if (nextChar >= 0x40 && nextChar <= 0x53) {
						isSpecialChar = true;
						*MacStr++ = 0x85;
					}
					else if (nextChar == 0x6F || nextChar == 0x70 || nextChar == 0x71 || nextChar == 0x75) {
						isSpecialChar = true;
						*MacStr++ = 0x86;
						switch (nextChar) {
							case 0x6F:
								*MacStr++ = 0x40;
								break;
							case 0x70:
								*MacStr++ = 0x42;
								break;
							case 0x71:
								*MacStr++ = 0x48;
								break;
							case 0x75:
								*MacStr++ = 0x46;
								break;
						}
						AnsiStr++;
						i++;
					}
				}
			}
#endif
			if (! isSpecialChar)
				*MacStr++ = character;
		}
		else
			*MacStr++ = (character & 0x80) ? AnsiToMacTab[character & 0x7F] : character;
	}

	if (pCharByteType)
		delete [] pCharByteType;

	return( (short) (MacStr - origMacStr) );
}

short GS_STRING_API WinMacToAnsiBuf(unsigned char const *MacStr, unsigned char *AnsiStr, short macLen, EStringExpansion expansion)
{

	register BYTE	character;
	unsigned char	*origAnsiStr;
	BYTE			tempStr[8];
	int				numCharsOut;

	origAnsiStr = AnsiStr;

	bool farEastVersion = CoreAPI::IsSystemFarEast();
	ECharacterType* pCharByteType = nil;
#if GS_WIN
	if (farEastVersion && CoreAPI::IsSystemJapanese()) {
		pCharByteType = NEW_NOTHROW ECharacterType[macLen];	
		if (pCharByteType) {
			GetSJISCharacterTypeArray((char*)MacStr, pCharByteType, macLen);
		}
	}
#endif

	for (int i = 0; i < macLen; i++) {
		if ( farEastVersion ) {
			/* There are a few double byte characters that are not standard shift-JIS characters and that are */
			/* defined both in MacJapanese and in the Win shift-JIS with different codes.  So we need to check */
			/* for one of these special characters and if found, translate it. */
			character = *MacStr++;
			Boolean isSpecialChar = false;
#if GS_WIN
			if (pCharByteType) {

				ECharacterType currCharacterType = pCharByteType[i];
				if (currCharacterType == eLead && (character == 0x85 || character == 0x86)) {
					BYTE nextChar = *MacStr;
					if (character == 0x85 && nextChar >= 0x40 && nextChar <= 0x53) {
						isSpecialChar = true;
						*AnsiStr++ = 0x87;
					}
					else if (character == 0x86 && 
						(nextChar == 0x40 || nextChar == 0x42 || nextChar == 0x46 || nextChar == 0x48)) 
					{
						isSpecialChar = true;
						*AnsiStr++ = 0x87;
						switch (nextChar) {
							case 0x40:
								*AnsiStr++ = 0x6F;
								break;
							case 0x42:
								*AnsiStr++ = 0x70;
								break;
							case 0x46:
								*AnsiStr++ = 0x75;
								break;
							case 0x48:
								*AnsiStr++ = 0x71;
								break;
						}
						MacStr++;
						i++;
					}
				}
			}
#endif
			if (! isSpecialChar)
				*AnsiStr++ = character;
		}
		else {
			character = *MacStr++;


	  					/* build up string to output	*/
						/*   by assuming it is a normal	*/
						/*   single char. case		*/
			tempStr[ 0 ] = character;
    						/* set output cnt to 1 since	*/
						/*   this is the case unless	*/
						/*   we have a special char.	*/
			numCharsOut = 1;
						/* skip the following check, all*/
						/*   4 are single byte HANKATA	*/
						/*   characters in ShiftJis code*/

		  
						/* check for multiple character	*/
						/*   output expansion		*/
			if ( (expansion == eAllowStringExpansion) && (character & 0x80) ) {
				if ( character == 0xC9 )
				{
				tempStr[ 0 ] = '.';
				tempStr[ 1 ] = '.';
				tempStr[ 2 ] = '.';
				numCharsOut = 3;
				}
				else if ( (character == 0xD2) ||
					  (character == 0xD3) )
				{
					tempStr[ 0 ] = '"';
				}
				else if ( character ==  0xAA )
				{
					tempStr[ 0 ] = '(';
					tempStr[ 1 ] = 't';
					tempStr[ 2 ] = 'm';
					tempStr[ 3 ] = ')';
					numCharsOut = 4;
				}
			}
	  

			/* output the mac characters	*/
			/*   we have accumulated	*/
			for(int j=0; j<numCharsOut; j++ ) {

				character = tempStr[j];


				// if it's an extended character, use the code page above
				if (character & 0x80) {
						*AnsiStr++ = MacToAnsiTab[character & 0x7F];
				}
				else {
					*AnsiStr++ = character;
				}

			}
		}
	}
	if (pCharByteType)
		delete [] pCharByteType;

	return( (short) (AnsiStr - origAnsiStr) );

}



#endif // _MINICAD_


#if _WINDOWS

//---------------------------------------------------------------------------------------
void TXString::AppendBackslash()
{
	if (Right(1) != "\\")
		*this << "\\";
}

#endif // _WINDOWS


///////////////////////////////////////////////////////////////////////////////
// TXString private methods

//-----------------------------------------------------------------------------
void TXString::Init(size_t nInitialSize)
{
	fBufSize = 0;
	fLength = 0;
	fData = NULL;
	fCStringData = NULL;

	EnsureBufferSize(nInitialSize);
	fData[0] = fData[1] = 0;
}

///////////////////////////////////////////////////////////////////////////////
// TXString debugging

IMPLEMENT_ASSERTVALID(TXString, TDebugObject,
	// Check basics.
	DSTOPIF(fBufSize < 1, (toWhom, "TXString::fBufSize too small (%d)", fBufSize));
	DSTOPIF(fLength > fBufSize-2, (toWhom, "TXString::fLength too big (%d)", fLength));
	ASSERTPOINTER(toWhom, fData, fBufSize);

	// Check C-string length.
	size_t len = (size_t)lstrlen(&fData[1]);
	DSTOPIF(fLength != len, (toWhom, "TXString::fLength (%d) not equal to C-string length (%d).", fLength, len));

	// Check Pascal-string length.
	len = (fData != NULL ? ((unsigned char*)fData)[0] : 0);
	if (fLength < 256) DSTOPIF(fLength != len, (toWhom, "TSTring::fLength (%d) not equal to Pascal-string length (%d).", fLength, len));
	if (fLength >= 256) DSTOPIF(len != 255, (toWhom, "TXString:: Pascal-string length (%d) not set to 255.", len)); 
)

/*IMPLEMENT_DUMP(TXString, TDebugObject,
	DUMP_INT(fBufSize);
	DUMP_INT(fLength);
	DUMP_PTR(fData);
)
*/


/////////////////////////////////////////////////////////////////////////////////////////
// TXString helper functions

//---------------------------------------------------------------------------------------
TXString operator+(const TXString& string1, const TXString& string2)
{
	TXString dest(string1);
	dest << string2;
	return dest;
}

//---------------------------------------------------------------------------------------
TXString operator+(const TXString& string, char ch)
{
	TXString dest(string);
	dest << ch;
	return dest;
}

//---------------------------------------------------------------------------------------
TXString operator+(const TXString& string, const char *sz)
{
	TXString dest(string);
	dest << sz;
	return dest;
}

//---------------------------------------------------------------------------------------
TXString operator+(const TXString& string, const unsigned char *str)
{
	TXString dest(string);
	dest << str;
	return dest;
}

//---------------------------------------------------------------------------------------
TXString operator+(char ch, const TXString& string)
{
	TXString dest(ch, 1);
	dest << string;
	return dest;
}

//---------------------------------------------------------------------------------------
TXString operator+(const char *sz, const TXString& string)
{
	TXString dest(sz);
	dest << string;
	return dest;
}

//---------------------------------------------------------------------------------------
TXString operator+(const unsigned char *str, const TXString& string)
{
	TXString dest(str);
	dest << string;
	return dest;
}


/*============================================================================*/
/*============================================================================*/
/**************************/
/* TXStringArray Routines */
/**************************/


//---------------------------------------------------------------------------------------
TXStringArray::TXStringArray(int nInitialSize, int nGrowBy)
{
	Init(nInitialSize, nGrowBy);
}

//---------------------------------------------------------------------------------------
TXStringArray::TXStringArray(const TXStringArray &src)
{
	Init(src.GetSize(), src.fGrowBy);
	Append(src);
}

//---------------------------------------------------------------------------------------
TXStringArray::~TXStringArray()
{
	RemoveAll();
}

//---------------------------------------------------------------------------------------
TXStringArray& TXStringArray::operator=(const TXStringArray &src)
{
	Init(src.GetSize(), src.fGrowBy);
	Append(src);
	return *this;
}

//---------------------------------------------------------------------------------------
bool TXStringArray::SetSize(Uint32 nSize)
{
	if (nSize < fNumStringsStored) {
		RemoveAt(nSize, fNumStringsStored - nSize);
	}
	else if (nSize > fNumStringsStored) {
		if (EnsureArraySize(nSize)) {
			fNumStringsStored = nSize;
		}
		else
			return false;
	}
	return true;
}

//---------------------------------------------------------------------------------------
void TXStringArray::SetAt(Uint32 nIndex, const TXString &string)
{
	if (EnsureArraySize(nIndex+1)) {
		*fStringsPtr[nIndex] = string;
		if (nIndex+1 > fNumStringsStored)
			fNumStringsStored = nIndex+1;
	}
}

//---------------------------------------------------------------------------------------
void TXStringArray::SetAt(Uint32 nIndex, ConstGSCStrPtr string)
{
	if (EnsureArraySize(nIndex+1)) {
		*fStringsPtr[nIndex] = string;
		if (nIndex+1 > fNumStringsStored)
			fNumStringsStored = nIndex+1;
	}
}

//---------------------------------------------------------------------------------------
void TXStringArray::SetAt(Uint32 nIndex, ConstGSStringPtr string)
{
	if (EnsureArraySize(nIndex+1)) {
		*fStringsPtr[nIndex] = string;
		if (nIndex+1 > fNumStringsStored)
			fNumStringsStored = nIndex+1;
	}
}


#if _MINICAD_

//---------------------------------------------------------------------------------------
bool TXStringArray::AppendStringResourceList( short stringID )
{
	RsrcMgr::GSResHandle	resourceData	= RsrcMgr::GSGetResource( 'STR#', stringID );
	bool					success			= false;
	
	if ( resourceData && *resourceData ) {
	
		int			currentSize		= this->GetSize();
		Uint16		nStrings		= **(Uint16**)resourceData;
		
		this->SetSize( currentSize + nStrings );
		
		if ( nStrings > 0 ) {
			unsigned char *currentString = (unsigned char*)(*resourceData + sizeof( nStrings ));
			
			for ( long x = 0; x < nStrings; x++ ) {
				(*this)[currentSize + x] << currentString; 
				
				currentString += (currentString[0] + 1);
			}
		}
		
		RsrcMgr::GSReleaseResource( resourceData );
		
		success = true;
	}
	
	return success;
}

#endif // _MINICAD_



//---------------------------------------------------------------------------------------
bool TXStringArray::Append(const TXString &string)
{
	if (EnsureArraySize(fNumStringsStored+1)) {
		*fStringsPtr[fNumStringsStored] = string;
		++fNumStringsStored;
		return true;
	}
	return false;
}

//---------------------------------------------------------------------------------------
bool TXStringArray::Append(ConstGSCStrPtr string)
{
	if (EnsureArraySize(fNumStringsStored+1)) {
		*fStringsPtr[fNumStringsStored] = string;
		++fNumStringsStored;
		return true;
	}
	return false;
}

//---------------------------------------------------------------------------------------
bool TXStringArray::Append(ConstGSStringPtr string)
{
	if (EnsureArraySize(fNumStringsStored+1)) {
		*fStringsPtr[fNumStringsStored] = string;
		++fNumStringsStored;
		return true;
	}
	return false;
}

//---------------------------------------------------------------------------------------
bool TXStringArray::Append(const TXStringArray &src)
{
	const Uint32 nNumToAppend = src.GetSize();

	if (EnsureArraySize(fNumStringsStored + nNumToAppend)) {

		for (Uint32 i=0; i<nNumToAppend; ++i) {
			Append(src[i]);
		}
		return true;
	}
	return false;
}

//---------------------------------------------------------------------------------------


void TXStringArray::RemoveAt(Uint32 nIndex, Uint32 nCount)
{
	// The method used here may seem weird, but it doesn't require
	// any new memory to be allocated, so it can't fail.

	// Check and correct parameters.
	if (nIndex < fNumStringsStored && nCount > 0) {
	
		// for the starting index specified, determine the maximum legal count
		Uint32 maxCount = fNumStringsStored - nIndex;
	
		// pin count to the max legal range
		if (nCount > maxCount)
			nCount = maxCount;


		Uint32 i;
		
		// slide the remaining strings down one by one
		Uint32 nextRemaining = nIndex + nCount;
		
		
		if (nextRemaining < fNumStringsStored) {
			for (i=0; i<nCount; i++) {
				TXString* pRemovedString = fStringsPtr[nIndex+i];
				fStringsPtr[nIndex+i] = fStringsPtr[nextRemaining+i];
				fStringsPtr[nextRemaining+i] = pRemovedString;
			}
		}

		// now eliminate the contents of the newly empty strings at the end of the array
		Uint32 firstEmpty = fNumStringsStored - nCount;
		for (i=0; i<nCount; i++) {
			fStringsPtr[firstEmpty+i]->Empty(false);
		}
		
		fNumStringsStored -= nCount;		
	}
}


// this is the source of a gargantuan memory leak. TXStringArray is defined to grow in chunks of 10 or more strings.
// so the maximum size is usually greater than the occupied size. When we were disposing of these arrays (which are used
// all over the place) we were only disposing of the strings being used and leaking the unused strings at the end of the array. - PCP

//---------------------------------------------------------------------------------------
void TXStringArray::RemoveAll()
{
	// Delete each string in the array.
	for (Uint32 i=0; i<fMaxStringsStored; ++i) {
		delete fStringsPtr[i];
	}

	// Delete the string pointer array.
	delete [] fStringsPtr;
	fStringsPtr = NULL;

	fNumStringsStored = fMaxStringsStored = 0;
}


//---------------------------------------------------------------------------------------
void TXStringArray::Init(int nInitialSize, int nGrowBy)
{
	fGrowBy = nGrowBy;
	fNumStringsStored = fMaxStringsStored = 0;
	fStringsPtr = NULL;
	
	EnsureArraySize(nInitialSize);
}

//---------------------------------------------------------------------------------------
bool TXStringArray::EnsureArraySize(Uint32 nSize)
{
	if (fMaxStringsStored < nSize) {

		Uint32 nRoundedSize = (nSize / fGrowBy) * fGrowBy;
		if (nSize % fGrowBy) nRoundedSize += fGrowBy;

		// Allocate new, larger buffer for all of the string pointers.
		TXString **pNewArray = NEW_NOTHROW TXString*[ sizeof(TXString*) * nRoundedSize ];
		if (pNewArray) {
			Mem_Zero(pNewArray, sizeof(TXString*) * nRoundedSize);

			// Copy the strings currently stored in the array.
			if (fNumStringsStored > 0)
				Mem_Copy(pNewArray, fStringsPtr, sizeof(TXString*) * fNumStringsStored);

			// Delete old string pointer array and start using the new one.
			delete [] fStringsPtr;
			fStringsPtr = pNewArray;

			// Create the TXStrings at the end of the new buffer.
			for (Uint32 i=fMaxStringsStored; i<nRoundedSize; ++i) {
				fStringsPtr[i] = NEW_NOTHROW TXString;
				if (fStringsPtr[i] == NULL) {
					fMaxStringsStored = i;
					return false;
				}
			}
			fMaxStringsStored = nRoundedSize;

			return true;
		}
		return false;
	}
	return true;
}

//---------------------------------------------------------------------------------------
Uint32 TXStringArray::FindString (const char* str, bool& bFound)
{
	for (Uint32 i = 0; i < fNumStringsStored; i++)
	{
		if (GetAt (i).CompareNoCase (str) == 0)
		{
			bFound = true;
			return i;
		}
	}

	bFound = false;
	return 0;
}


/*============================================================================*/
/*============================================================================*/
/*****************************/
/* TXStringResource Routines */
/*****************************/

#if COMPILE_TXStringResource


///////////////////////////////////////////////////////////////////////////////
// TXStringResource public methods

void TXStringResource::LoadIt()
{
	if (!fLoaded) {
		if (fStrList == -14)
			fStr.Load(fIndex);
		else
			fStr.Load(fStrList, fIndex);
		fLoaded = true;
	}
}


///////////////////////////////////////////////////////////////////////////////
// TXStringResource debugging

IMPLEMENT_ASSERTVALID(TXStringResource, TDebugObject,
	ASSERTCLASS(toWhom, &fStr);
);


#endif  // COMPILE_TXStringResource


/*============================================================================*/
/*============================================================================*/
/***************/
/* TXUniString */
/***************/


// TXUniString buffer is allocated in fixed-size chunks
//  to avoid reallocating it too frequently
static const UCCharCnt	kTXUSGrowChunk = 16;


/*============================================================================*/
/****************************/
/* TXUniString Constructors */
/****************************/


TXUniString::TXUniString ()
{
	bool	success;
	
	success = this->Init(kTXUSGrowChunk-1);  // Chunk size less terminating NULL
} // TXUniString::TXUniString


TXUniString::TXUniString (UCCharCnt numChars)
{
	bool	success;
	
	success = this->Init(numChars);
} // TXUniString::TXUniString


TXUniString::TXUniString (const TXUniString& srcStr)
{
	bool	success;
	
	ASSERTCLASS(kEveryone, &srcStr);
	
	success = this->Init(srcStr.GetLength());
	if (success) this->SetValue(srcStr);
} // TXUniString::TXUniString


TXUniString::TXUniString (ConstUCStrPtr pCharsTerm)
{
	bool	success;
	
	success = this->Init(StringLength(pCharsTerm));
	if (success) this->SetValue(pCharsTerm);
} // TXUniString::TXUniString


TXUniString::TXUniString (ConstUCCharPtr pChars, UCCharCnt length)
{
	bool	success;
	
	success = this->Init(length);
	if (success) this->SetValue(pChars, length);
} // TXUniString::TXUniString


TXUniString::TXUniString (UCChar uch, UCCharCnt repeatCount)
{
	bool	success;
	
	success = this->Init(repeatCount);
	if (success) this->SetValue(uch, repeatCount);
} // TXUniString::TXUniString


TXUniString::TXUniString (GSTextEncoding srcEncoding, const char* srcStr)
{
	bool		success;
	UCCharCnt	newBufSize;
	
	/* Initially Allocate the Maximum-Needed Buffer for a Unicode,  */
	/*  Conversion, then Reallocate It to the Used Number of Chunks */
	success = this->Init(StringToUnicodeMaxChars(strlen(srcStr))-1);
	if (success) {
		success = StringToUnicode(srcEncoding, srcStr, &fData[0], fBufSize,
		                           &fLength);
		newBufSize = (((fLength + 1) + (kTXUSGrowChunk - 1)) / kTXUSGrowChunk) *
		              kTXUSGrowChunk;  // Include terminating NULL word
		success = this->ReallocBuffer(newBufSize);
	}
} // TXUniString::TXUniString


TXUniString::TXUniString (GSTextEncoding srcEncoding, ConstGSStr255Param srcStr)
{
	bool		success;
	UCCharCnt	newBufSize;
	
	/* Initially Allocate the Maximum-Needed Buffer for a Unicode,  */
	/*  Conversion, then Reallocate It to the Used Number of Chunks */
	success = this->Init(StringToUnicodeMaxChars(::GetLength(srcStr))-1);
	if (success) {
		success = StringToUnicode(srcEncoding, srcStr, &fData[0], fBufSize,
		                           &fLength);
		newBufSize = (((fLength + 1) + (kTXUSGrowChunk - 1)) / kTXUSGrowChunk) *
		              kTXUSGrowChunk;  // Include terminating NULL word
		success = this->ReallocBuffer(newBufSize);
	}
} // TXUniString::TXUniString


/*============================================================================*/
/**************************/
/* TXUniString Destructor */
/**************************/


TXUniString::~TXUniString ()
{
	delete[] fData;
	fData = nil;
	fBufSize = 0;
	fLength = 0;
} // TXUniString::~TXUniString


/*============================================================================*/
/*************************/
/* TXUniString Operators */
/*************************/


TXUniString& TXUniString::operator<< (long n)
{
	// Base-10 digits plus thousands separators plus sign is 14 for 32 bits
	const UCCharCnt		kMaxNumChars = 14 * sizeof(UCChar);
	
	CStr15		cStr;
	UCChar		uniNumArray[kMaxNumChars];
	UCCharCnt	numLen;
	bool		success;
	
	ASSERTTHISCLASS(kEveryone);
	
	cStr << n;  // XXX: Might use _itow on Windows and CF stuff on Mac
	success = StringToUnicode(GetSystemEncoding(), (char*)cStr, uniNumArray,
	                           kMaxNumChars, &numLen);
	success = this->Append(uniNumArray, numLen);
	return(*this);
} // TXUniString::operator<<


/*============================================================================*/
/*****************************************************/
/* TXUniString String Value Assignment and Retrieval */
/*****************************************************/


//*****************************************************************************
//
// Function Name:  TXUniString::GetValue (terminated string version)
//
// Description:
//	Copies the characters from the TXUniString into the provided buffer,
//	including the terminating NULL word.
//
// Parameters:
//	pCharsTerm - Pointer to an array of 16-bit words for the TXUniString
//	              characters to be copied into (NULL-word terminated type)
//	maxChars   - Maximum number of 16-bit words that the pChars array can hold,
//	              including the terminating NULL word
//	pLen       - Returns the number of 16-bit words the copied string occupies
//	              in pChars (does not include the terminating NULL)
//
// Return Value:
//	None
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

void TXUniString::GetValue (UCStrPtr pCharsTerm, UCCharCnt maxChars) const
{
	UCCharCnt	length;
	
	this->GetValue(pCharsTerm, maxChars, &length);
} // TXUniString::GetValue


//*****************************************************************************
//
// Function Name:  TXUniString::GetValue (character array version)
//
// Description:
//	Copies the characters from the TXUniString into the provided buffer,
//	including the terminating NULL word.  The number of Unicode characters
//	copied is also returned (as always, the number of 16-bit words, being
//	insensitive to whether any of the Unicode is surrogate pairs).
//
// Parameters:
//	pChars   - Pointer to an array of 16-bit words for the TXUniString
//	            characters to be copied into
//	maxChars - Maximum number of 16-bit words that the pChars array can hold,
//	            including the terminating NULL word
//	pLen     - Returns the number of 16-bit words the copied string occupies
//	            in pChars (does not include the terminating NULL)
//
// Return Value:
//	None
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

void TXUniString::GetValue (UCCharPtr pChars, UCCharCnt maxChars,
                             UCCharCnt* pLen) const
{
	UCCharCnt	charsToCopy;
	bool		success;
	
	ASSERTTHISCLASS(kEveryone);
	
	/* Determine How Many Characters from the String Will Fit, */
	/*  Making Sure to Truncate the Copy on a Cluster Boundary */
	if (maxChars > fLength)
		charsToCopy = fLength;
	else {
		charsToCopy = maxChars - 1;
		success = FindCharClusterBreak(&fData[0], fLength, charsToCopy,
		                                kFindRev, &charsToCopy);
	}
	
	/* Copy the String Characters to the Destination Array */
	/*  and Return the Number of Characters Copied         */
	if (!VERIFYPOINTER(kEveryone, pChars, ((charsToCopy+1)*sizeof(UCChar))))
		*pLen = 0;
	else {
		memcpy(pChars, &fData[0], (charsToCopy*sizeof(UCChar)));
		pChars[charsToCopy] = NULL;
		*pLen = charsToCopy;
	}
} // TXUniString::GetValue


/*============================================================================*/
/*****************************************************************/
/* TXUniString Length Getters, Length Setters, and Buffer Sizers */
/*****************************************************************/


bool TXUniString::GrowLength (UCCharCnt len, UCChar fill)
{
	bool	success;
	
	ASSERTTHISCLASS(kEveryone);
	success = false;
	
	if (len <= fLength) {
		/* String Already as Big as Requested */
		success = true;
		}
	else {
		/* Attempt to Grow Buffer, then Fill Area Beyond   */
		/*  Current String Length with Specified Character */
		success = this->Append(fill, (len-fLength));
	}
	return(success);
} // TXUniString::GrowLength


void TXUniString::Truncate (UCCharCnt maxLen)
{
	UCCharCnt	breakPos;
	bool		success;
	
	ASSERTTHISCLASS(kEveryone);
	
	/* Must Truncate on Character Cluster Boundary  */
	/*  (But Will Use Position Passed If Not Found) */
	if (maxLen < fLength) {
		success = FindCharClusterBreak(&fData[0], fLength, maxLen, kFindRev,
		                                &breakPos);
		fLength = breakPos;
		fData[fLength] = NULL;
	}
} // TXUniString::Truncate


void TXUniString::Empty (bool keepBuffer)
{
	ASSERTTHISCLASS(kEveryone);
	
	fLength = 0;
	if (!keepBuffer) this->FreeExtraBuffer();
	if (fBufSize > 0) fData[0] = NULL;
} // TXUniString::Empty


bool TXUniString::GrowBuffer (UCCharCnt minNumChars)
{
	bool		success;
	UCCharCnt	newBufSize;

	if (minNumChars < fBufSize) {
		/* Buffer Already as Large as Requested */
		success = true;
		}
	else {
		/* Grow the Buffer in Fixed-Size Increments to   */
		/*  Avoid Piddly Reallocations When String Grows */
		/*  (Could Retry Failure with Original Value of  */
		/*  numChars, But Unlikely to Make a Difference) */
		newBufSize = (((minNumChars + 1) + (kTXUSGrowChunk - 1)) / kTXUSGrowChunk) *
		              kTXUSGrowChunk;  // Include terminating NULL word
		success = this->ReallocBuffer(newBufSize);
	}
	return(success);
} // TXUniString::GrowBuffer


void TXUniString::FreeExtraBuffer ()
{
	bool	success;
	
	success = this->ReallocBuffer(fLength+1);  // Include terminating NULL word
} // TXUniString::FreeExtraBuffer


/*============================================================================*/
/*************************************************************/
/* TXUniString Appending, Inserting, Deleting, and Replacing */
/*************************************************************/


bool TXUniString::Append (const TXUniString& string)
{
	ASSERTCLASS(kEveryone, &string);
	
	return(this->Append((const UCCharPtr)string, string.GetLength()));
} // TXUniString::Append


bool TXUniString::Append (ConstUCStrPtr pCharsTerm)
{
	return(this->Append(pCharsTerm, StringLength(pCharsTerm)));
} // TXUniString::Append


bool TXUniString::Append (ConstUCCharPtr pChars, UCCharCnt len)
{
	bool		success;
	UCCharCnt	charsToCopy;
	
	ASSERTTHISCLASS(kEveryone);
	if (len <  0) return(false);
	if (len == 0) return(true);
	
	success = this->GrowBuffer(fLength+len);
	if (success)
		charsToCopy = len;
	else
		charsToCopy = fBufSize - fLength - 1;
	
	memcpy(&fData[fLength], pChars, (charsToCopy*sizeof(UCChar)));
	fLength += charsToCopy;
	fData[fLength] = NULL;
	return(success);
} // TXUniString::Append


bool TXUniString::Append (UCChar uch, UCCharCnt repeatCount)
{
	bool		success;
	UCCharCnt	charsToSet;
	
	ASSERTTHISCLASS(kEveryone);
	if (repeatCount  < 0) return(false);
	if (repeatCount == 0) return(true);
	
	success = this->GrowBuffer(fLength+repeatCount);
	if (success)
		charsToSet = repeatCount;
	else
		charsToSet = fBufSize - fLength - 1;
	
	for (UCCharCnt pos = fLength; pos < (fLength+charsToSet); pos++) {
		fData[pos] = uch;
	}
	fLength += charsToSet;
	fData[fLength] = NULL;
	return(success);
} // TXUniString::Append


bool TXUniString::Insert (UCCharCnt pos, const TXUniString& string)
{
	ASSERTCLASS(kEveryone, &string);
	
	return(this->Insert(pos, (const UCCharPtr)string, string.GetLength()));
} // TXUniString::Insert


bool TXUniString::Insert (UCCharCnt pos, ConstUCStrPtr pCharsTerm)
{
	return(this->Insert(pos, pCharsTerm, StringLength(pCharsTerm)));
} // TXUniString::Insert


bool TXUniString::Insert (UCCharCnt pos, ConstUCCharPtr pChars, UCCharCnt len)
{
	bool		success, success2;
	UCCharCnt	charsToCopy, insPos;
	
	/* Restrict Starting Position to Within String */
	/*  and Check that Insertion String Length > 0 */
	ASSERTTHISCLASS(kEveryone);
	if (pos < 0)
		pos = 0;
	else if (pos > fLength)
		pos = fLength;
	if (len  < 0) return(false);
	if (len == 0) return(true);
	
	/* Must Insert Only What Fits at a Cluster Boundary */
	success = this->GrowBuffer(fLength+len);
	if (success)
		charsToCopy = len;
	else
		charsToCopy = fBufSize - fLength - 1;
	success2 = FindCharClusterBreak(&fData[0], fLength, pos, kFindFwd, &insPos);
	
	memmove(&fData[insPos+charsToCopy], &fData[insPos],
	         ((fLength-insPos+1)*sizeof(UCChar)));
	memcpy(&fData[insPos], pChars, (charsToCopy*sizeof(UCChar)));
	fLength += charsToCopy;
	return(success);
} // TXUniString::Insert


bool TXUniString::Insert (UCCharCnt pos, UCChar uch, UCCharCnt repeatCount)
{
	bool		success, success2;
	UCCharCnt	charsToSet, insPos;
	
	/* Restrict Starting Position to Within String   */
	/*  and Check that Insertion Character Count > 0 */
	ASSERTTHISCLASS(kEveryone);
	if (pos < 0)
		pos = 0;
	else if (pos > fLength)
		pos = fLength;
	if (repeatCount  < 0) return(false);
	if (repeatCount == 0) return(true);
	
	/* Must Insert Only What Fits at a Cluster Boundary */
	success = this->GrowBuffer(fLength+repeatCount);
	if (success)
		charsToSet = repeatCount;
	else
		charsToSet = fBufSize - fLength;
	success2 = FindCharClusterBreak(&fData[0], fLength, pos, kFindFwd, &insPos);
	
	memmove(&fData[insPos+charsToSet], &fData[insPos],
	         ((fLength-insPos+1)*sizeof(UCChar)));
	for (UCCharCnt pos = insPos; pos < (insPos+charsToSet); pos++) {
		fData[pos] = uch;
	}
	fLength += charsToSet;
	return(success);
} // TXUniString::Insert


void TXUniString::Delete (UCCharCnt pos, UCCharCnt len)
{
	UCCharCnt	begPos, endPos;
	bool		success;
	
	/* Check that Starting Position Is Within String   */
	/*  and Restrict Deletion Length to That of String */
	ASSERTTHISCLASS(kEveryone);
	if ((pos < 0) || (fLength <= pos)) return;
	if (len <= 0) return;
	if ((pos + len) > fLength) len = fLength - pos;
	
	/* Must Delete at Cluster Boundaries--Move Forward if Necessary */
	success = FindCharClusterBreak(&fData[0], fLength, pos, kFindFwd, &begPos);
	success = FindCharClusterBreak(&fData[0], fLength, (pos+len), kFindFwd, &endPos);
	
	/* Copy End of String Forward Over Characters To Be Deleted */
	memmove(&fData[begPos], &fData[endPos], ((fLength-endPos+1)*sizeof(UCChar)));
	fLength -= (endPos - begPos);
} // TXUniString::Delete


bool TXUniString::Replace (ConstUCStrPtr pOldStrTerm,
                            ConstUCStrPtr pNewStrTerm, bool ignoreCase)
{
	// This is not the most efficient way to do this, since the old string
	//  is copied to be made NULL-word terminated, but I think it's safest
	return(this->Replace(pOldStrTerm, StringLength(pOldStrTerm),
	                      pNewStrTerm, StringLength(pNewStrTerm), ignoreCase));
} // TXUniString::Replace


bool TXUniString::Replace (ConstUCCharPtr pOldStr, UCCharCnt oldLen,
		           ConstUCCharPtr pNewStr, UCCharCnt newLen, bool ignoreCase)
{
	bool			success, insertSuccess;
	TXUniString		*oldStrTerm;
	UCCharCnt		startPos, oldBegPos, oldEndPos;
	
	/* Check Object and that Find/Replace String Lengths Are Valid */
	ASSERTTHISCLASS(kEveryone);
	if (oldLen <= 0) return(false);
	if (newLen <  0) return(false);
	
	/* Since Find Requires a NULL-Word Terminated String, */
	/*  We Need to Copy the Find String to Another Buffer */
	oldStrTerm = NEW_NOTHROW TXUniString(pOldStr, oldLen);
	success = (oldStrTerm != nil);
	
	/* For Every Successive Occurrence of the Find String, */
	/*  Delete It and then Insert the Replacement String   */
	/*  (Not the Most Efficient Char Movement, But Clean)  */
	if (success) {
		startPos = 0;
		do {
			success = this->Find((const UCStrPtr)*oldStrTerm, startPos,
    		                      ignoreCase, &oldBegPos, &oldEndPos);
    		if (success) {
				this->Delete(oldBegPos, (oldEndPos-oldBegPos));
				insertSuccess = this->Insert(oldBegPos, pNewStr, newLen);
				if (!insertSuccess) success = false;
				startPos = oldBegPos + newLen;
			}
		} while (success);
	}
	if (oldStrTerm != nil) delete oldStrTerm;
	
	return(success);
} // TXUniString::Replace


/*============================================================================*/
/***********************************/
/* TXUniString Whitespace Trimming */
/***********************************/


//*****************************************************************************
//
// Function Name:  TXUniString::TrimLeft
//
// Description:
//	Removes all contiguous "whitespace" characters from the beginning of the
//	TXUniString.  This includes all the Unicode space characters (basic,
//	special-width, no-break, etc.) plus the tab character.  Line and paragraph
//	break characters are not included.
//
// Parameters:
//	None
//
// Return Value:
//	None
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

void TXUniString::TrimLeft ()
{
	UCCharCnt	nonWhitePos;
	
	/* Check the Object and Initialize Variables */
	ASSERTTHISCLASS(kEveryone);
	if (fLength == 0) return;
	nonWhitePos = 0;
	
#if GS_WIN
	const UCCharCnt		kGroupSize = 20;
	
	Uint16		charProps[kGroupSize];
	UCCharCnt	groupBeg, groupCnt, pos;
	bool		nonWhiteFound, success;
	
	/* Check the String Characters in Groups (So Don't Wastefully Check All) */
	groupBeg = 0;
	nonWhiteFound = false;
	while ((groupBeg < fLength) && !nonWhiteFound) {
		/* Determine Whether Full or Partial Group Remains */
		if ((groupBeg+kGroupSize) > fLength)
			groupCnt = fLength - groupBeg;
		else
			groupCnt = kGroupSize;
		
		/* Get the Properties of Each Character in the Group */
		success = GetStringTypeW(CT_CTYPE1, (LPCWSTR) &fData[groupBeg], groupCnt,
		                          &charProps[0]);
		if (!success) break;
		
		/* Check for Non-Whitespace Characters from the Group Beginning */
		for (pos = 0; pos < groupCnt; pos++) {
			nonWhiteFound = !(((charProps[pos] && C1_SPACE) != 0) ||
			 (((charProps[pos] && C1_CNTRL) != 0) && (fData[groupBeg+pos] == '\t')));
			if (nonWhiteFound)
				break;
			else
				nonWhitePos += 1;
		}
		groupBeg += kGroupSize;
	}
	
#else  /* Macintosh */
	UCCharPropertyValue		charProp;
	UCCharCnt				pos;
	bool					nonWhiteFound;
	OSStatus				err;
	
	pos = 0;
	nonWhiteFound = false;
	while ((pos < fLength) && !nonWhiteFound) {
		err = UCGetCharProperty(&fData[pos], 1, kUCCharPropTypeGenlCategory,
		                         &charProp);
		if (err != noErr) break;
		nonWhiteFound = !((charProp == kUCGenlCatSeparatorSpace) ||
		 ((charProp == kUCGenlCatOtherControl) && (fData[pos] == '\t')));
		if (!nonWhiteFound) nonWhitePos += 1;
		pos += 1;
	}
#endif  // GS_WIN
	
	/* Delete the Characters from the Beginning of the  */
	/*  String Until the First Non-Whitespace Character */
	if (nonWhitePos > 0) this->Delete(0, nonWhitePos);
} // TXUniString::TrimLeft


//*****************************************************************************
//
// Function Name:  TXUniString::TrimRight
//
// Description:
//	Removes all contiguous "whitespace" characters from the end of the
//	TXUniString.  This includes all the Unicode space characters (basic,
//	special-width, no-break, etc.) plus the tab character.  Line and paragraph
//	break characters are not included.
//
// Parameters:
//	None
//
// Return Value:
//	None
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

void TXUniString::TrimRight ()
{
	UCCharCnt	begWhitePos;
	
	/* Check the Object and Initialize Variables */
	ASSERTTHISCLASS(kEveryone);
	if (fLength == 0) return;
	begWhitePos = fLength;
	
#if GS_WIN
	const UCCharCnt		kGroupSize = 20;
	
	Uint16		charProps[kGroupSize];
	UCCharCnt	groupEnd, groupCnt, pos;
	bool		nonWhiteFound, success;
	
	/* Check the String Characters in Groups (So Don't Wastefully Check All) */
	groupEnd = fLength - 1;
	nonWhiteFound = false;
	while ((groupEnd >= 0) && !nonWhiteFound) {
		/* Determine Whether Full or Partial Group Remains */
		if (groupEnd < (kGroupSize-1))
			groupCnt = groupEnd + 1;
		else
			groupCnt = kGroupSize;
		
		/* Get the Properties of Each Character in the Group */
		success = GetStringTypeW(CT_CTYPE1, (LPCWSTR) &fData[(groupEnd+1)-groupCnt],
		                          groupCnt, &charProps[0]);
		if (!success) break;
		
		/* Check for Non-Whitespace Characters from the Group End */
		for (pos = (groupCnt-1); pos >= 0; pos--) {
			nonWhiteFound = !(((charProps[pos] && C1_SPACE) != 0) ||
			 (((charProps[pos] && C1_CNTRL) != 0) &&
			 (fData[(groupEnd+1)-groupCnt+pos] == '\t')));
			if (nonWhiteFound)
				break;
			else
				begWhitePos -= 1;
		}
		if (groupEnd >= kGroupSize)  // groupEnd is unsigned
			groupEnd -= kGroupSize;
		else
			break;
	}
	
#else  /* Macintosh */
	UCCharPropertyValue		charProp;
	UCCharCnt				pos;
	bool					nonWhiteFound;
	OSStatus				err;
	
	pos = fLength - 1;
	nonWhiteFound = false;
	while ((pos >= 0) && !nonWhiteFound) {
		err = UCGetCharProperty(&fData[pos], 1, kUCCharPropTypeGenlCategory,
		                         &charProp);
		if (err != noErr) break;
		nonWhiteFound = !((charProp == kUCGenlCatSeparatorSpace) ||
		 ((charProp == kUCGenlCatOtherControl) && (fData[pos] == '\t')));
		if (!nonWhiteFound) begWhitePos -= 1;
		pos -= 1;
	}
#endif  // GS_WIN
	
	/* Delete the Characters After the Last Non-Whitespace Character */
	if (begWhitePos < fLength)
	  this->Delete(begWhitePos, (fLength-begWhitePos));
} // TXUniString::TrimRight


/*============================================================================*/
/*******************************/
/* TXUniString Case Conversion */
/*******************************/


void TXUniString::MakeUpperCase ()
{
	ASSERTTHISCLASS(kEveryone);
	if (fLength == 0) return;
	
#if GS_WIN
	Sint32	numProcessed;
	
	/* Call the Unicode Uppercase String Function */
	numProcessed = CharUpperBuffW((LPWSTR) &fData[0], fLength);
#else
	CFMutableStringRef	cfUniStr;
	
	/* Wrap a Mutable CFString around the Characters and Call the CF Function */
	cfUniStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
	            &fData[0], fLength, (fBufSize-1), kCFAllocatorNull);
	if (cfUniStr != nil) {
		CFStringUppercase(cfUniStr, nil);  // nil should use CFLocaleCopyCurrent
		CFRelease(cfUniStr);
	}
#endif  // GS_WIN
} // TXUniString::MakeUpperCase


void TXUniString::MakeLowerCase ()
{
	ASSERTTHISCLASS(kEveryone);
	if (fLength == 0) return;
	
#if GS_WIN
	Sint32	numProcessed;
	
	/* Call the Unicode Lowercase String Function */
	numProcessed = CharLowerBuffW((LPWSTR) &fData[0], fLength);
#else
	CFMutableStringRef	cfUniStr;
	
	/* Wrap a Mutable CFString around the Characters and Call the CF Function */
	cfUniStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
	            &fData[0], fLength, (fBufSize-1), kCFAllocatorNull);
	if (cfUniStr != nil) {
		CFStringLowercase(cfUniStr, nil);  // nil should use CFLocaleCopyCurrent
		CFRelease(cfUniStr);
	}
#endif  // GS_WIN
} // TXUniString::MakeLowerCase


/*============================================================================*/
/**************************/
/* TXUniString Comparison */
/**************************/


bool TXUniString::IsEqual (const TXUniString& string, bool ignoreCase) const
{
	ASSERTCLASS(kEveryone, &string);
	
	return(this->IsEqual((const UCCharPtr)string, string.GetLength(), ignoreCase));
} // TXUniString::IsEqual


bool TXUniString::IsEqual (ConstUCStrPtr pCharsTerm, bool ignoreCase) const
{
	return(this->IsEqual(pCharsTerm, StringLength(pCharsTerm), ignoreCase));
} // TXUniString::IsEqual


bool TXUniString::IsEqual (ConstUCCharPtr pChars, UCCharCnt len,
                            bool ignoreCase) const
{
	ASSERTTHISCLASS(kEveryone);
	
	return(StringEqual((const UCCharPtr)*this, this->GetLength(),
	                    pChars, len, ignoreCase));
} // TXUniString::IsEqual


Sint32 TXUniString::Compare (const TXUniString& string, bool ignoreCase) const
{
	ASSERTCLASS(kEveryone, &string);
	
	return(this->Compare((const UCCharPtr)string, string.GetLength(), ignoreCase));
} // TXUniString::Compare


Sint32 TXUniString::Compare (ConstUCStrPtr pCharsTerm, bool ignoreCase) const
{
	return(this->Compare(pCharsTerm, StringLength(pCharsTerm), ignoreCase));
} // TXUniString::Compare


Sint32 TXUniString::Compare (ConstUCCharPtr pChars, UCCharCnt len,
                              bool ignoreCase) const
{
	Sint32	orderResult;
	
	ASSERTTHISCLASS(kEveryone);
	if (len < 0) return(+1);
	if (len == 0) {
		if (IsEmpty())
			return(0);
		else
			return(+1);
	}
	
#if GS_WIN
	Uint32	compareFlags;
	
	compareFlags = NORM_IGNOREWIDTH | SORT_STRINGSORT;
	if (ignoreCase) compareFlags |= NORM_IGNORECASE;
	orderResult = CompareStringW(LOCALE_USER_DEFAULT, compareFlags,
	               (LPCWSTR) ((const UCCharPtr)*this), this->GetLength(), (LPCWSTR) pChars, len);
	if (orderResult == CSTR_LESS_THAN)
		orderResult = -1;
	else if (orderResult == CSTR_EQUAL)
		orderResult =  0;
	else if (orderResult == CSTR_GREATER_THAN)
		orderResult = +1;
	else
		orderResult =  0;  // We have no idea, so take the middle
	
#else  /* Macintosh */
	UCCollateOptions	collateOpts;
	OSStatus			err;
	
	collateOpts = kUCCollateStandardOptions | kUCCollatePunctuationSignificantMask;
	if (ignoreCase) collateOpts |= kUCCollateCaseInsensitiveMask;
	err = UCCompareTextDefault(collateOpts, (const UCCharPtr)*this, this->GetLength(),
	                            pChars, len, nil, &orderResult);
	if (err != noErr) orderResult = 0; // We have no idea, so take the middle
#endif  // GS_WIN
	
	return(orderResult);
} // TXUniString::Compare


/*============================================================================*/
/*************************************/
/* TXUniString Sub-String Extraction */
/*************************************/


TXUniString TXUniString::Mid (UCCharCnt pos, UCCharCnt len) const
{
	TXUniString		subStr(len);  // Initially the empty string
	UCCharCnt		begPos, endPos;
	bool			success;
	
	/* Check that Starting Position Is Within String  */
	/*  and Restrict Copying Length to That of String */
	ASSERTTHISCLASS(kEveryone);
	if ((pos < 0) || (fLength <= pos)) return(subStr);
	if (len <= 0) return(subStr);
	if ((pos + len) > fLength) len = fLength - pos;
	
	/* Must Copy at Cluster Boundaries--Move Forward if Necessary */
	success = FindCharClusterBreak(&fData[0], fLength, pos, kFindFwd, &begPos);
	success = FindCharClusterBreak(&fData[0], fLength, (pos+len), kFindFwd, &endPos);
	
	/* Append the Substring to the Allocated Empty String */
	subStr.GrowBuffer(endPos-begPos);  // Substring may have become longer
	subStr.Append(&fData[begPos], (endPos-begPos));
	return(subStr);
} // TXUniString::Mid


/*============================================================================*/
/*************************************/
/* TXUniString Searches for a String */
/*************************************/


//*****************************************************************************
//
// Function Name:  TXUniString::Find (TXUniString version)
//
// Description:
//	Finds the first occurence of the specified string in the string object
//	that comes at or after the starting position specified.  Case-sensitivity
//	can be ignored or required.  Note that "loose" Unicode matching, where
//	different forms of characters are considered the same (precomposed versus
//	decomposed, ligature or not), is used on the Macintosh.  This is desirable
//	on Windows, but it does not appear that its routines support this (there
//	are definitely no options for performing it or not).  Because of this, the
//	length of the matching sub-string in the object may not be the same as
//	that of the search string; thus beginning and end positions are returned.
//	Also note that no cluster-break checking is done, so that searches for
//	arbitrary characters, including non-spacing combining ones, can be made.
//	
//	NOTE: Windows currently has no find/search routines that support reverse
//	searching.  If these are ever added, a "forward" parameter should be
//	inserted after the "findStr" parameter.
//
// Parameters:
//	findStr    - TXUniString object with the string to be found in the
//	              object this method is called for
//	startPos   - Position in the called object's string to start searching at
//	ignoreCase - Whether to ignore case or not in determining a match
//	pFndBegPos - Returns the position in the object's string at which the
//	              search string begins; meaningless if function returns false
//	pFndEndPos - Returns the position in the object's string FOLLOWING the end
//	              of the search string (think of it as the caret sitting
//	              between the last matched character and the next one; you
//	              can then use *pFndEndPos for startPos in successive Finds
//
// Return Value:
//	Whether the substring was found in the string object; a return value of
//	false could mean that the string was not there or that an error occurred
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

bool TXUniString::Find (const TXUniString& findStr, UCCharCnt startPos,
		   bool ignoreCase, UCCharCnt* pFndBegPos, UCCharCnt* pFndEndPos) const
{
	ASSERTCLASS(kEveryone, &findStr);
	
	return(this->Find((ConstUCStrPtr)findStr, startPos, ignoreCase,
			           pFndBegPos, pFndEndPos));
} // TXUniString::Find


//*****************************************************************************
//
// Function Name:  TXUniString::Find (terminated string version)
//
// Description:
//	Finds the first occurence of the specified string in the string object
//	that comes at or after the starting position specified.  Case-sensitivity
//	can be ignored or required.  Note that "loose" Unicode matching, where
//	different forms of characters are considered the same (precomposed versus
//	decomposed, ligature or not), is used on the Macintosh.  This is desirable
//	on Windows, but it does not appear that its routines support this (there
//	are definitely no options for performing it or not).  Because of this, the
//	length of the matching sub-string in the object may not be the same as
//	that of the search string; thus beginning and end positions are returned.
//	Also note that no cluster-break checking is done, so that searches for
//	arbitrary characters, including non-spacing combining ones, can be made.
//	
//	NOTE: Windows currently has no find/search routines that support reverse
//	searching.  If these are ever added, a "forward" parameter should be
//	inserted after "pFindCharsT".
//
// Parameters:
//	pFindCharsT - Pointer to an array of 16-bit words with the string to be
//	               found in the object--MUST be terminated with a NULL word
//	startPos    - Position in the object's string to start searching at
//	ignoreCase  - Whether to ignore case or not in determining a match
//	pFndBegPos  - Returns the position in the object's string at which the
//	               search string begins; meaningless if function returns false
//	pFndEndPos  - Returns the position in the object's string FOLLOWING the end
//	               of the search string (think of it as the caret sitting
//	               between the last matched character and the next one; you
//	               can then use *pFndEndPos for startPos in successive Finds
//
// Return Value:
//	Whether the substring was found in the string object; a return value of
//	false could mean that the string was not there or that an error occurred
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

bool TXUniString::Find (ConstUCStrPtr pFindCharsT, UCCharCnt startPos,
           bool ignoreCase, UCCharCnt* pFndBegPos, UCCharCnt* pFndEndPos) const
{
	bool	success;
	
	/* Check the Object and the Starting Position, */
	/*  and Initialize the Found Position Returns  */
	ASSERTTHISCLASS(kEveryone);
	if ((startPos < 0) || (fLength <= startPos)) return(false);
	*pFndBegPos = fLength;
	*pFndEndPos = 0;
	
#if GS_WIN
	UCCharPtr		matchPtr;
	TXUniString		*upperString, *upperFind;
	
	/* On Windows, Do a Search, and If Found Assume that the   */
	/*  Length of the Found String Matches the Search String's */
	if (!ignoreCase) {
		/* If Case Sensitive, Things Are Straightforward */
		matchPtr = (UCCharPtr) wcsstr((const wchar_t *) &fData[0], (const wchar_t *)pFindCharsT);
		if (matchPtr == nil)
			success = false;
		else {
			*pFndBegPos = matchPtr - fData;
			*pFndEndPos = *pFndBegPos + wcslen((const wchar_t*) pFindCharsT);
			success = true;
		}
		}
	else {
		/* If Case Insensitive, Both Strings Must Be Copied */
		/*  and Upper-Cased Before the Search Can Be Done   */
		upperString = NEW_NOTHROW TXUniString(&fData[startPos], (fLength-startPos));
		upperFind   = NEW_NOTHROW TXUniString(pFindCharsT, wcslen((const wchar_t *) pFindCharsT));
		if ((upperString == nil) || (upperFind == nil))
			success = false;
		else {
			upperString->MakeUpperCase();
			upperFind->MakeUpperCase();
			matchPtr = (UCCharPtr) wcsstr((const wchar_t *) ((const UCStrPtr)*upperString), (const wchar_t *)((const UCStrPtr)*upperFind));
			if (matchPtr == nil)
				success = false;
			else {
				*pFndBegPos = matchPtr - (const UCCharPtr)*upperString + startPos;
				*pFndEndPos = *pFndBegPos + upperFind->GetLength();
				success = true;
			}
		}
		if (upperString != nil) delete upperString;
		if (upperFind   != nil) delete upperFind;
	}
	
#else  /* Macintosh */
	CFMutableStringRef	cfObjStr;
	CFStringRef			cfFindStr;
	CFRange				searchRange, foundRange;
	CFOptionFlags		findOptions;
	UCCharCnt			findStrLen;
	
	/* Wrap a CFString around the Characters and Call the CF Find Function */
	//  There were two options here for wrapping the find string.
	//   (1) Using CFStringCreateWithCharactersNoCopy and possibly having a
	//        memory leak if CFString decides it needs its own buffer (because
	//        we must pass kCFAllocatorNull to ensure that the original buffer
	//        is never deallocated by CFString).
	//   (2) Using CFStringCreateMutableWithExternalCharactersNoCopy, which
	//        ensures the original buffer is used, but prevents us from
	//        specifying the find string parameter as const (which could make
	//        the function more difficult to use in some situations).
	//  I opted for option (1), but have no idea how often it is leaking.  MFH
	cfObjStr  = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
	                     &fData[0], fLength, (fBufSize-1), kCFAllocatorNull);
	findStrLen = StringLength(pFindCharsT);
	cfFindStr = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
	                     pFindCharsT, findStrLen, kCFAllocatorNull);
	if ((cfObjStr == nil) || (cfFindStr == nil))
		success = false;
	else {
		searchRange.location = startPos;
		searchRange.length   = fLength - startPos;
		findOptions = kCFCompareNonliteral;
		if (ignoreCase) findOptions |= kCFCompareCaseInsensitive;
		success = CFStringFindWithOptions(cfObjStr, cfFindStr, searchRange,
		                                   findOptions, &foundRange);
		if (success) {
			*pFndBegPos = foundRange.location;
			*pFndEndPos = *pFndBegPos + foundRange.length;
		}
   	}
   	if (cfObjStr  != nil) CFRelease(cfObjStr);
   	if (cfFindStr != nil) CFRelease(cfFindStr);
#endif  // GS_WIN
	
	return(success);
} // TXUniString::Find


//*****************************************************************************
//
// Function Name:  TXUniString::Find (character array version)
//
// Description:
//	Finds the first occurence of the specified string in the string object
//	that comes at or after the starting position specified.  Case-sensitivity
//	can be ignored or required.  Note that "loose" Unicode matching, where
//	different forms of characters are considered the same (precomposed versus
//	decomposed, ligature or not), is used on the Macintosh.  This is desirable
//	on Windows, but it does not appear that its routines support this (there
//	are definitely no options for performing it or not).  Because of this, the
//	length of the matching sub-string in the object may not be the same as
//	that of the search string; thus beginning and end positions are returned.
//	Also note that no cluster-break checking is done, so that searches for
//	arbitrary characters, including non-spacing combining ones, can be made.
//	
//	NOTE: Windows currently has no find/search routines that support reverse
//	searching.  If these are ever added, a "forward" parameter should be
//	inserted after "len".
//
// Parameters:
//	pFindChars - Pointer to an array of 16-bit words with the string to be
//	              found in the object
//	len        - Number of 16-bit words in pFindChars containing characters
//	              to be found
//	startPos   - Position in the object's string to start searching at
//	ignoreCase - Whether to ignore case or not in determining a match
//	pFndBegPos - Returns the position in the object's string at which the
//	              search string begins; meaningless if function returns false
//	pFndEndPos - Returns the position in the object's string FOLLOWING the end
//	              of the search string (think of it as the caret sitting
//	              between the last matched character and the next one; you
//	              can then use *pFndEndPos for startPos in successive Finds
//
// Return Value:
//	Whether the substring was found in the string object; a return value of
//	false could mean that the string was not there or that an error occurred
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

bool TXUniString::Find (ConstUCCharPtr pFindChars, UCCharCnt len,
                         UCCharCnt startPos, bool ignoreCase,
                         UCCharCnt* pFndBegPos, UCCharCnt* pFndEndPos) const
{
	bool			success;
	TXUniString		*findStrTerm;
	
	/* Since Windows Find Requires a NULL-Word Terminated String, */
	/*  We Need to Copy the Find String to Another Buffer First   */
	findStrTerm = NEW_NOTHROW TXUniString(pFindChars, len);
	success = (findStrTerm != nil);
	
	/* Call the Find Method for a NULL-Word Terminated String */
	if (success) {
		success = this->Find((const UCStrPtr)findStrTerm, startPos, ignoreCase,
		                      pFndBegPos, pFndEndPos);
	}
	
	return(success);
} // TXUniString::Find


//*****************************************************************************
//
// Function Name:  TXUniString::Find (character version)
//
// Description:
//	Finds the first occurence of the specified character in the string object
//	that comes at or "after: the starting position specified.  Case-sensitivity
//	can be ignored or required.  Note that "loose" Unicode matching, where
//	different forms of characters are considered the same (precomposed versus
//	decomposed, ligature or not), is used on the Macintosh.  This is desirable
//	on Windows, but it does not appear that its routines support this (there
//	are definitely no options for performing it or not).  Because of this, the
//	length of the matching sub-string in the object may not be a single
//	character; thus beginning and end positions are returned.
//	Also note that no cluster-break checking is done, so that searches for
//	arbitrary characters, including non-spacing combining ones, can be made.
//
// Parameters:
//	findUch    - Unicode character to be found in the string object
//	forward    - Whether to search forward or backwards from startPos
//	startPos   - Position in the object's string to start searching at;
//	              this can be regarded as a caret between characters, with a
//	              forward find including the character at that index to the
//	              end, and a reverse find starting at the character to the
//	              left of the specified one.  So range depends on direction:
//	              Forward:  0 <= startPos <  fLength
//	              Reverse:  0 <  startPos <= fLength
//	ignoreCase - Whether to ignore case or not in determining a match
//	pFndBegPos - Returns the position in the object's string at which the
//	              specified character begins; meaningless if false returned
//	pFndEndPos - Returns the position in the object's string FOLLOWING the end
//	              of the search character (think of it as the caret sitting
//	              between the last matched character and the next one; you
//	              can then use *pFndEndPos for startPos in successive Finds
//
// Return Value:
//	Whether the character was found in the string object; a return value of
//	false could mean that the character wasn't there or that an error occurred
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

bool TXUniString::Find (UCChar findUch, bool forward, UCCharCnt startPos,
            bool ignoreCase, UCCharCnt* pFndBegPos, UCCharCnt* pFndEndPos) const
{
	bool	success;
	UCChar	findStr[2];
	
	/* Check the Object and the Starting Position, */
	/*  and Initialize the Found Position Returns  */
	ASSERTTHISCLASS(kEveryone);
	if (forward)
		if ((startPos <  0) || (fLength <= startPos)) return(false);
	else
		if ((startPos <= 0) || (fLength <  startPos)) return(false);
	*pFndBegPos = fLength;
	*pFndEndPos = 0;
	
	if (forward) {
		/* For Forward Matching, Convert the Specified Character to  */
		/*  a NULL-Terminated String and Call the String Find Method */
		/* --------------------------------------------------------- */
		findStr[0] = findUch;
		findStr[1] = NULL;
		success = this->Find(&findStr[0], startPos, ignoreCase,
		                      pFndBegPos, pFndEndPos);
		}
	else {
		/* For Reverse Matching, Take a Platform-Dependent Approach */
		/* -------------------------------------------------------- */
#if GS_WIN
		TXUniString		*objSubStr;
		UCCharPtr		matchPtr;
		Sint32			numProcessed;
		
		/* Copy the To-Be-Searched Portion of the Object String and Do a   */
		/*  Reverse Character-Search, Upper-Casing All If Case Insensitive */
		/* Windows has no reverse string-search routine nor   */
		/*  a partial-string reverse character-search routine */
		objSubStr = NEW_NOTHROW TXUniString(&fData[0], startPos);
		if (objSubStr != nil) {
			findStr[0] = findUch;
			findStr[1] = NULL;
			if (ignoreCase) {
				objSubStr->MakeUpperCase();
				numProcessed = CharUpperBuffW((LPWSTR) &findStr[0], 1);
			}
			matchPtr = (UCCharPtr) wcsrchr((const wchar_t *) ((const UCStrPtr)*objSubStr), findStr[0]);
			if (matchPtr == nil)
				success = false;
			else {
				*pFndBegPos = matchPtr - (const UCCharPtr)*objSubStr;
				*pFndEndPos = *pFndBegPos + 1;
				success = true;
			}
			delete objSubStr;
		}
		
#else  /* Macintosh */
		CFMutableStringRef	cfObjStr, cfFindStr;
		CFRange				searchRange, foundRange;
		CFOptionFlags		findOptions;
		
		/* Wrap a Mutable CFString around the Characters and Call the CF Function */
		/*  (Making It Mutable Ensures that the Characters Will Never Be Copied)  */
		cfObjStr  = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
		                     &fData[0], fLength, (fBufSize-1), kCFAllocatorNull);
		cfFindStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault,
		                     &findUch, 1, 1, kCFAllocatorNull);
		if ((cfObjStr == nil) || (cfFindStr == nil))
			success = false;
		else {
			searchRange.location = 0;
			searchRange.length   = startPos;
			findOptions = kCFCompareBackwards | kCFCompareNonliteral;
			if (ignoreCase) findOptions |= kCFCompareCaseInsensitive;
			success = CFStringFindWithOptions(cfObjStr, cfFindStr, searchRange,
			                                   findOptions, &foundRange);
			if (success) {
				*pFndBegPos = foundRange.location;
				*pFndEndPos = *pFndBegPos + foundRange.length;
			}
	   	}
	   	if (cfObjStr  != nil) CFRelease(cfObjStr);
	   	if (cfFindStr != nil) CFRelease(cfFindStr);
#endif  // GS_WIN
	}
	
	return(success);
} // TXUniString::Find


/*============================================================================*/
/***************************************/
/* TXUniString Character Set Searching */
/***************************************/


/*** For FindOneOf, look at wcscspn or wcspbrk on Windows ***/


/*============================================================================*/
/********************************/
/* TXUniString Debugging Macros */
/********************************/


IMPLEMENT_ASSERTVALID (TXUniString, TDebugObject,
	ASSERTPOINTER(toWhom, fData, (fBufSize*sizeof(UCChar)));
	DSTOPIF((fBufSize < 1), (toWhom, "TXUniString::fBufSize too small (%d)", fBufSize));
	DSTOPIF((fLength >= fBufSize), (toWhom,  "TXUniString::fLength too big (%d)", fLength));
)


/*============================================================================*/
/*******************************/
/* TXUniString Private Methods */
/*******************************/


//*****************************************************************************
//
// Function Name:  TXUniString::Init
//
// Description:
//	Initializes the member variables and creates a storage buffer for the
//	TXUniString.  The buffer will hold the specified number of characters plus
//	the terminating NULL word used by TXUniString.
//
// Parameters:
//	numChars - Number of Unicode characters that the string should be able
//	            to hold (this does not include the terminating NULL word)
//
// Return Value:
//	None
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool TXUniString::Init (UCCharCnt numChars)
{
	bool	success;
	
	/* Initialize the member variables */
	fData = nil;
	fBufSize = 0;
	fLength = 0;
	
	/* Allocate the String Value Buffer and Set the Terminating NULL Word */
	if (numChars < 0) numChars = 0;  // Will always get terminating NULL space
	success = this->GrowBuffer(numChars);
	if (fBufSize > 0) fData[0] = NULL;
	return(success);
} // TXUniString::Init


//*****************************************************************************
//
// Function Name:  TXUniString::ReallocBuffer
//
// Description:
//	Resizes the storage buffer of the TXUniString to the specified number of
//	16-bit words (or does the initial buffer creation).  Note that the
//	specified size must INCLUDE the terminating NULL word of each string value
//	(unlike Init and GrowBuffer, which let their clients use the count of
//	actual characters to be stored).  This routine will not resize the buffer
//	below what will hold the current string value.
//
// Parameters:
//	numWords - Number of 16-bit words the buffer is to be resized to
//	            (again, note that this must include the terminating NULL word)
//
// Return Value:
//	Whether the buffer was reallocated to (or created with) the specified size
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool TXUniString::ReallocBuffer (UCCharCnt numWords)
{
	bool		success;
	UCCharPtr	newBuffer;
	
	/* Check for Valid Number of Characters, Don't Shrink Buffer Beyond */
	/*  Current Contents, and Ignore if Requested Size Is Current Size  */
	if (numWords <= 0) numWords = 1;
	if (numWords <= fLength) numWords = (fLength+1);
	if (numWords == fBufSize)
		return(true);
	
	/* Allocate a New Buffer, Copy the Old Contents, and then Free */
	/*  the Old Buffer (So Don't Fail Because Old One Is Boxed In) */
	success = false;
	newBuffer = NEW_NOTHROW UCChar[numWords];
	if (newBuffer != nil) {
		if (fData != nil) {
			memcpy(newBuffer, fData, ((fLength+1)*sizeof(UCChar)));
			delete[] fData;
		}
		fData = newBuffer;
		fBufSize = numWords;
		success = true;
	}
	return(success);
} // TXUniString::ReallocBuffer


/*============================================================================*/
/*============================================================================*/
/*************************************/
/* TXUniString Concatenation Helpers */
/*************************************/


TXUniString operator+ (const TXUniString& str1, const TXUniString& str2)
{
	TXUniString		destStr(str1);
	bool			success;
	
	success = destStr.Append(str2);
	return(destStr);
}


TXUniString operator+ (const TXUniString& string, ConstUCStrPtr pCharsTerm)
{
	TXUniString		destStr(string);
	bool			success;
	
	success = destStr.Append(pCharsTerm);
	return(destStr);
}


TXUniString operator+ (ConstUCStrPtr pCharsTerm, const TXUniString& string)
{
	TXUniString		destStr(pCharsTerm);
	bool			success;
	
	success = destStr.Append(string);
	return(destStr);
}


TXUniString operator+ (const TXUniString& string, UCChar uch)
{
	TXUniString		destStr(string);
	bool			success;
	
	success = destStr.Append(uch, 1);
	return(destStr);
}


TXUniString operator+ (UCChar uch, const TXUniString& string)
{
	TXUniString		destStr(uch, 1);
	bool			success;
	
	success = destStr.Append(string);
	return(destStr);
}


/*============================================================================*/
/*============================================================================*/
/********************/
/* TXUniStringArray */
/********************/


/*============================================================================*/
/*********************************/
/* TXUniStringArray Constructors */
/*********************************/


TXUniStringArray::TXUniStringArray (Uint32 initialSize/*=10*/,
                                     Uint32 growChunk/*=10*/)
{
	bool	success;
	
	success = this->Init(initialSize, growChunk);
} // TXUniStringArray::TXUniStringArray


TXUniStringArray::TXUniStringArray (const TXUniStringArray& srcArray)
{
	bool	success;
	
	success = this->Init(srcArray.GetSize(), srcArray.fGrowChunk);
	if (success) success = this->Append(srcArray);
} // TXUniStringArray::TXUniStringArray


/*============================================================================*/
/*******************************/
/* TXUniStringArray Destructor */
/*******************************/


TXUniStringArray::~TXUniStringArray ()
{
	/* Dispose of All the TXUniStrings from the Array */
	/*  Buffer, and Then Dispose of the Buffer Itself */
	for (Uint32 i = 0; i < fBufferSize; i++) {
		delete fStringPtrs[i];
	}
	delete[] fStringPtrs;
	fStringPtrs = nil;
	fBufferSize = 0;
	fArraySize  = 0;
	fGrowChunk  = 0;
} // TXUniStringArray::~TXUniStringArray


/*============================================================================*/
/******************************************************************/
/* TXUniStringArray Size Getter, Size Setter, and Buffer Shrinker */
/******************************************************************/


bool TXUniStringArray::SetSize (Uint32 newSize)
{
	bool	success;
	
	success = true;
	if (newSize < fArraySize) {
		/* Delete Array Elements Beyond the Number Specified */
		this->Delete(newSize, (fArraySize-newSize));
		}
	else if (newSize > fArraySize) {
		/* Grow the Buffer, then Make Its Empty Strings Array Elements */
		success = this->GrowBuffer(newSize);
		if (success) fArraySize = newSize;
	}
	
	return(success);
} // TXUniStringArray::SetSize


void TXUniStringArray::FreeExtraBuffer ()
{
	bool	success;
	
	success = this->ReallocBuffer(fArraySize);
} // TXUniStringArray::FreeExtraBuffer


/*============================================================================*/
/***************************************************/
/* TXUniStringArray Element Appending and Deleting */
/***************************************************/


bool TXUniStringArray::Append (const TXUniStringArray& strArray)
{
	bool	success;
	Uint32	numToAdd;
	
	numToAdd = strArray.GetSize();
	success = this->GrowBuffer(fArraySize+numToAdd);
	if (success) {
		for (Uint32 i = 0; i < numToAdd; i++) {
			this->Append(strArray[i]);
		}
	}
	return(success);
} // TXUniStringArray::Append


bool TXUniStringArray::Append (const TXUniString& string)
{
	bool	success;
	
	success = this->GrowBuffer(fArraySize+1);
	if (success) {
		success = fStringPtrs[fArraySize]->SetValue(string);
		if (success) fArraySize += 1;
	}
	return(success);
} // TXUniStringArray::Append


bool TXUniStringArray::Append (ConstUCStrPtr pCharsTerm)
{
	bool	success;
	
	success = this->GrowBuffer(fArraySize+1);
	if (success) {
		success = fStringPtrs[fArraySize]->SetValue(pCharsTerm);
		if (success) fArraySize += 1;
	}
	return(success);
} // TXUniStringArray::Append


bool TXUniStringArray::Append (ConstUCCharPtr pChars, UCCharCnt len)
{
	bool	success;
	
	success = this->GrowBuffer(fArraySize+1);
	if (success) {
		success = fStringPtrs[fArraySize]->SetValue(pChars, len);
		if (success) fArraySize += 1;
	}
	return(success);
} // TXUniStringArray::Append


bool TXUniStringArray::Append (UCChar uch, UCCharCnt repeatCount)
{
	bool	success;
	
	success = this->GrowBuffer(fArraySize+1);
	if (success) {
		success = fStringPtrs[fArraySize]->SetValue(uch, repeatCount);
		if (success) fArraySize += 1;
	}
	return(success);
} // TXUniStringArray::Append


void TXUniStringArray::Delete (Uint32 index, Uint32 count/*=1*/)
{
	TXUniString		*delStr;
	
	/* Check that Index and Count Are Within Current Array Elements */
	if (index >= fArraySize) return;
	if ((index+count) > fArraySize) count = fArraySize - index;
	
	/* For Each String To Be Deleted, Save a Pointer to It, Empty It, */
	/*  Move Following Elements Forward, Then Put Empty String At End */
	// This is slower, but avoids doing any memory allocation that could fail
	for (Uint32 i = 0; i < count; i++) {
		delStr = fStringPtrs[index+i];
		delStr->Empty(false);
		memmove(&fStringPtrs[index+i], &fStringPtrs[index+i+1],
		         ((fArraySize-index-1)*sizeof(TXUniString*)));
		fStringPtrs[fArraySize-1] = delStr;
		fArraySize -= 1;
	}
} // TXUniStringArray::Delete


void TXUniStringArray::DeleteAll (bool keepBuffer)
{
	bool	success;
	
	/* If Not Keeping the Array Buffer, Shrink */
	/*  It to a Maximum of the Grow Chunk Size */
	if (!keepBuffer) {
		if (fBufferSize > fGrowChunk)
		  success = this->ReallocBuffer(fGrowChunk);
	}
	
	/* Empty All the Element Strings and Zero the Element Count */
	for (Uint32 i = 0; i < fArraySize; i++) {
		fStringPtrs[i]->Empty(false);
	}
	fArraySize = 0;
} // TXUniStringArray::DeleteAll


/*============================================================================*/
/******************************************************/
/* TXUniStringArray Element Value Setters and Getters */
/******************************************************/


bool TXUniStringArray::SetElem (Uint32 index, const TXUniString& string)
{
	bool	success;
	
	if (index >= fArraySize)
		success = false;
	else 
		success = fStringPtrs[index]->SetValue(string);
	return(success);
} // TXUniStringArray::SetElem


bool TXUniStringArray::SetElem (Uint32 index, ConstUCStrPtr pCharsTerm)
{
	bool	success;
	
	if (index >= fArraySize)
		success = false;
	else 
		success = fStringPtrs[index]->SetValue(pCharsTerm);
	return(success);
} // TXUniStringArray::SetElem


bool TXUniStringArray::SetElem (Uint32 index, ConstUCCharPtr pChars,
                                 UCCharCnt len)
{
	bool	success;
	
	if (index >= fArraySize)
		success = false;
	else 
		success = fStringPtrs[index]->SetValue(pChars, len);
	return(success);
} // TXUniStringArray::SetElem


bool TXUniStringArray::SetElem (Uint32 index, UCChar uch, UCCharCnt repeatCount)
{
	bool	success;
	
	if (index >= fArraySize)
		success = false;
	else 
		success = fStringPtrs[index]->SetValue(uch, repeatCount);
	return(success);
} // TXUniStringArray::SetElem


/*============================================================================*/
/******************************************/
/* TXUniStringArray Searches for a String */
/******************************************/


void TXUniStringArray::FindString (const TXUniString& string, Uint32 startIndex,
                        bool ignoreCase, bool* pFound, Uint32* pIndex) const
{
	this->FindString((const UCCharPtr)string, string.GetLength(), startIndex,
	                  ignoreCase, pFound, pIndex);
} // TXUniStringArray::FindString


void TXUniStringArray::FindString (ConstUCStrPtr pCharsTerm, Uint32 startIndex,
                        bool ignoreCase, bool* pFound, Uint32* pIndex) const
{
	this->FindString(pCharsTerm, StringLength(pCharsTerm), startIndex,
	                  ignoreCase, pFound, pIndex);
} // TXUniStringArray::FindString


void TXUniStringArray::FindString (ConstUCCharPtr pChars, UCCharCnt len,
        Uint32 startIndex, bool ignoreCase, bool* pFound, Uint32* pIndex) const
{
	if (startIndex >= fArraySize) {
		*pFound = false;
		*pIndex = fArraySize;
		return;
	}
	
	*pFound = false;
	*pIndex = startIndex;
	while (!(*pFound) && (*pIndex < fArraySize)) {
		*pFound = fStringPtrs[*pIndex]->IsEqual(pChars, len, ignoreCase);
		if (!(*pFound)) *pIndex += 1;
	}
} // TXUniStringArray::FindString


/*============================================================================*/
/************************************/
/* TXUniStringArray Private Methods */
/************************************/


bool TXUniStringArray::Init (Uint32 initialSize, Uint32 growChunk)
{
	/* Initialize the member variables */
	if (growChunk == 0) growChunk = 10;
	fStringPtrs = nil;
	fBufferSize = 0;
	fArraySize  = 0;
	fGrowChunk  = growChunk;
	
	/* Allocate the Buffer of Pointers to Empty TXUniStrings */
	if (initialSize <= 0) initialSize = 1;  // Start with at least one
	return(this->GrowBuffer(initialSize));
} // TXUniStringArray::Init


bool TXUniStringArray::GrowBuffer (Uint32 minSize)
{
	bool	success;
	Uint32	newBufSize;
	
	if (minSize <= fBufferSize) {
		/* Buffer Already as Large as Requested */
		success = true;
		}
	else {
		/* Grow the Buffer in Fixed-Size Increments to  */
		/*  Avoid Piddly Reallocations When Array Grows */
		/*  (Could Retry Failure with Original Value of */
		/*  minSize, But Unlikely to Make a Difference) */
		newBufSize = ((minSize + (fGrowChunk - 1)) / fGrowChunk) * fGrowChunk;
		success = this->ReallocBuffer(newBufSize);
	}
	return(success);
} // TXUniStringArray::GrowBuffer


bool TXUniStringArray::ReallocBuffer (Uint32 newSize)
{
	bool			success;
	TXUniString		**newStringPtrs;
	Uint32			newElemsUsed;
	
	/* Check for Valid Size, and Ignore if Requested Size Is Current Size  */
	if (newSize <= 0) newSize = 1;
	if (newSize == fBufferSize)
		return(true);
	
	/* Allocate a New Buffer, Copy the Old Contents, Free */
	/*  the Old Buffer, and Make Appropriate Adjustments  */
	success = false;
	newStringPtrs = NEW_NOTHROW TXUniString*[newSize];
	if (newStringPtrs != nil) {
		/* Zero All the Buffer's Pointers for Safety */
		memset(newStringPtrs, 0, (newSize*sizeof(TXUniString*)));
		
		if (fStringPtrs == nil) {
			/* If No Old Buffer, There Are No Array Elements */
			fBufferSize = 0;
			newElemsUsed = 0;
			}
		else {
			if (newSize > fBufferSize) {
				/* If New Buffer Larger than Old One, Copy */
				/*  All of the Old String Pointers to It   */
				memcpy(newStringPtrs, fStringPtrs,
				        (fBufferSize*sizeof(TXUniString*)));
				newElemsUsed = fArraySize;
				}
			else {
				/* If New Buffer Smaller than Old One, Copy Only the String */
				/*  Pointers that Fit, then Dispose of the Excess Strings   */
				memcpy(newStringPtrs, fStringPtrs,
				        (newSize*sizeof(TXUniString*)));
				if (fArraySize > newSize)
					newElemsUsed = newSize;
				else
					newElemsUsed = fArraySize;
				
				for (Uint32 i = newSize; i < fBufferSize; i++) {
					delete fStringPtrs[i];
				}
			}
			
			/* Dispose of the Old Buffer */
			delete[] fStringPtrs;
			success = true;
		}
		
		/* If New Buffer Is Larger than Old One, Allocate Empty    */
		/*  TXUniStrings and Store Their Pointers in the New Slots */
		if (newSize > fBufferSize) {
			for (Uint32 i = fBufferSize; i < newSize; i++) {
				newStringPtrs[i] = NEW_NOTHROW TXUniString((UCCharCnt)0);
				if (newStringPtrs[i] != nil)
					newStringPtrs[i]->FreeExtraBuffer();  // Want them small
				else {
					newSize = i;  // Limit array element use
					success = false;
					break;
				}
			}
		}
		
		/* Make the New Buffer Active for the Array */
		fStringPtrs = newStringPtrs;
		fBufferSize = newSize;
		fArraySize  = newElemsUsed;
	}
	
	return(success);
} // TXUniStringArray::ReallocBuffer


/*============================================================================*/
/*============================================================================*/
