// GSString.cpp
//	Kernel source file for basic string manipulation.
//
// Copyright 1996-2004 Nemetschek North America. All Rights Reserved.


/* Includes */
/* -------- */
#include "StdHeaders.h"	// DO NOT MOVE THIS LINE FROM BEING FIRST NON-COMMENT LINE.  [DMB, 10/30/95]
#include "GSCore.h"

#if !_WINDOWS || _MINICAD_ || _GSESDK_
// Windows externals get this module's code from "MiniCad.lib". [DMB 11/25/96]

#include <stdarg.h>

#if _WINDOWS
	#include <Strings.h>
	#include <Usp10.h>
	#include "GSUtil.h" // For inrange function; not sure where why Mac is okay
#else
	#include <ctype.h>
#endif

#if _MINICAD_
	#include "DialogUtilities.h"
	#include "MemoryAllocation.h"
	#include "StringUtilities.h"
#else
	#include "MCCoordTypes.h"
#endif // _MINICAD_

#if _GSESDK_
	// XXX_JDW_ALTURA [2003-02-24] - fix GS_INTERFACE_FACTORING && !GS_CORE_BUILD
	//  wankosity in MiniCadCallBacks.h
	#include "MiniCadCallBacks.h"
#endif

#include "GSString.h"

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


/*============================================================================*/
/*============================================================================*/
/**************************************************/
/* Unicode Character String Manipulation Routines */
/**************************************************/


/*============================================================================*/
/***************************************/
/* Unicode Encoding Converter Routines */
/***************************************/


//*****************************************************************************
//
// Function Name:  StringToUnicodeMaxChars
//
// Description:
//	Returns the maximum number of Unicode characters (with a terminating NULL
//	word) required to represent a non-Unicode C- or Pascal-style string of the
//	specified number of character bytes.  Note that this is completely
//	simplistic, is based only on the length of the string and ignores its
//	encoding, and calculates the worst case as stated by Apple in its
//	publication "Text Encoding Manager Reference".  But using this and
//	reallocating the Unicode buffer after calling StringToUnicode should
//	usually be faster than doing the conversion an extra time beforehand to
//	get the character count.
//
// Parameters:
//	srcLen - Number of character bytes in the C- or Pascal-style string to be
//	          converted (simply call strlen or GetLength on the string)
//
// Return Value:
//	Maximum number of Unicode characters (including NULL terminator) required
//	to represent a string of the specified length in a non-Unicode encoding
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

UCCharCnt StringToUnicodeMaxChars (Uint32 srcLen)
{
	return((srcLen * 3) + 1);
} // StringToUnicodeMaxChars


//*****************************************************************************
//
// Function Name:  StringToUnicode (character array version)
//
// Description:
//	Converts the characters of the specified encoding in a character array to
//	an array of Unicode characters in UTF-16 encoding form.  Precomposed
//	Unicode characters are preferred in the converted string (but the
//	Macintosh does not permit specifying this as of OS X 10.3.7).
//	A terminating NULL word is NOT added following the converted characters.
//
// Parameters:
//	srcEncoding - Platform-specific text encoding value for the source
//	               string's characters
//	srcArray    - Pointer to an array containing the characters to be
//	               converted; these may be 8- or 16-bit depending on encoding
//	srcLen      - Number of 8-bit bytes (always) in srcArray containing
//	               characters to be converted
//	dstUCArray  - Pointer to an array of 16-bit words for the converted
//	               Unicode characters to be returned in (no terminator added)
//	maxDstChars - Maximum number of 16-bit words that dstUCArray can hold (note
//	               that surrogate and decomposed characters are > 16 bits);
//	               to handle all encodings safely, this must be:
//	               maxDstChars = (srcLen * 3)
//	               (just call StringToUnicodeMaxChars)
//	pDstLen     - Returns the number of 16-bit words the converted characters
//	               occupy in dstUCArray
//
// Return Value:
//	Whether the conversion was successful.  Failures include not enough space
//	in the destination array; source characters that can't be converted are
//	not considered a failure [a richer return type could be considered].
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool StringToUnicode (GSTextEncoding srcEncoding, const char* srcArray,
                       size_t srcLen, UCCharPtr dstUCArray,
                       UCCharCnt maxDstChars, UCCharCnt* pDstLen)
{
	bool	success;
	
	/* Check the Destination Array Provided */
	/* ------------------------------------ */
	if (maxDstChars == 0) {
		*pDstLen = 0;
		return(false);
	}
	if (!VERIFYPOINTER(kMike, dstUCArray, (maxDstChars*sizeof(UCChar)))) {
		*pDstLen = 0;
		return(false);
	}
	
	/* Convert the Supplied Characters to Unicode */
	/* ------------------------------------------ */
	if (srcLen == 0) {
		/* Source Array Is Empty */
		/* --------------------- */
		dstUCArray[0] = NULL;  // Just for grins; room available in this case
		*pDstLen = 0;
		success = true;
		}
	else {
		/* Source Array Is Not Empty */
		/* ------------------------- */
#if GS_WIN
		/* Convert the Source Text in the Specified */
		/*  Encoding to Unicode and Check for Error */
		// Non-zero options are a problem for some encodings, but we don't
		// want accented Unicode characters becoming two characters, so
		// we use MB_PRECOMPOSED unless srcEncoding is CP_SYMBOL; if this
		// is changed, a corresponding change should be made to the
		// WideCharToMultiByte call in UnicodeToString below
		DWORD mapOptions;
		if (srcEncoding == CP_SYMBOL)
			mapOptions = 0;
		else
			mapOptions = MB_PRECOMPOSED;
		*pDstLen = MultiByteToWideChar(srcEncoding, mapOptions,
		                 srcArray, srcLen, (LPWSTR) dstUCArray, maxDstChars);
		success = (*pDstLen != 0);  // Zero always returned on failure, unlike Mac
#else
		UnicodeMapping		textToUniMap;
		TextToUnicodeInfo	textToUniInfo;
		ByteCount			srcBytesConv, dstBytesUsed;
		OSStatus			err;
		
		/* Construct the Text-to-Unicode Mapping */
		// kUnicodeCanonicalCompVariant is not supported as of OS X 10.3.7!
		textToUniMap.unicodeEncoding =
		 CreateTextEncoding(kTextEncodingUnicodeDefault,
		  kUnicodeNoSubset, kUnicode16BitFormat);
		textToUniMap.otherEncoding   = srcEncoding;
		textToUniMap.mappingVersion  = kUnicodeUseLatestMapping;
		
		/* Construct the Text-to-Unicode Information Block */
		err = CreateTextToUnicodeInfo(&textToUniMap, &textToUniInfo);
		success = (err == noErr);
		
		/* Convert the Source Text to Unicode, Allowing Loose Mappings */
		/*   So All Characters Are Converted as Usefully as Possible   */
		if (success) {
			dstBytesUsed = 0;  // So we can hope to return partial conversions
			err = ConvertFromTextToUnicode(textToUniInfo, srcLen, srcArray,
			       kUnicodeLooseMappingsMask, 0, nil, nil, nil,
			       ((maxDstChars-1)*sizeof(UCChar)), &srcBytesConv,
			       &dstBytesUsed, dstUCArray);
			*pDstLen = dstBytesUsed / sizeof(UCChar);
			success = (err == noErr);
			err = DisposeTextToUnicodeInfo(&textToUniInfo);
   		}
#endif  // GS_WIN
	}
	return(success);
} // StringToUnicode


//*****************************************************************************
//
// Function Name:  StringToUnicode (C string version)
//
// Description:
//	Converts the characters of the specified encoding in a C-style string to
//	an array of Unicode characters in UTF-16 encoding form.  Precomposed
//	Unicode characters are preferred in the converted string (but the
//	Macintosh does not permit specifying this as of OS X 10.3.7).
//	A terminating NULL word is added following the converted characters.
//
// Parameters:
//	srcEncoding - Platform-specific text encoding value for the source
//	               string's characters
//	srcStr      - Pointer to a C-style string containing the characters to be
//	               converted
//	dstUCStr    - Pointer to an array of 16-bit words for the converted
//	               Unicode characters to be returned in (NULL-word terminated)
//	maxDstChars - Maximum number of 16-bit words that dstUCStr can hold (note
//	               that surrogate and decomposed characters are > 16 bits);
//	               to handle all encodings safely, this must be:
//	               maxDstChars = (strlen(srcStr) * 3) + 1
//	               (just call StringToUnicodeMaxChars)
//	pDstLen     - Returns the number of 16-bit words the converted string
//	               occupies in dstUCStr, not including the NULL-word terminator
//
// Return Value:
//	Whether the conversion was successful.  Failures include not enough space
//	in the destination array; source characters that can't be converted are
//	not considered a failure [a richer return type could be considered].
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool StringToUnicode (GSTextEncoding srcEncoding, const char* srcStr,
              UCStrPtr dstUCStr, UCCharCnt maxDstChars, UCCharCnt* pDstLen)
{
	bool	success;
	
	/* Call the Character Array Version and Append   */
	/*  a NULL-Word Terminator to the Returned Array */
	success = StringToUnicode(srcEncoding, srcStr, strlen(srcStr),
	                           dstUCStr, (maxDstChars-1), pDstLen);
	if (success || (*pDstLen < maxDstChars))
	  dstUCStr[*pDstLen] = NULL;
	return(success);
} // StringToUnicode


//*****************************************************************************
//
// Function Name:  StringToUnicode (Pascal string version)
//
// Description:
//	Converts the characters of the specified encoding in a Pascal-style string
//	to an array of Unicode characters in UTF-16 encoding form.  Precomposed
//	Unicode characters are preferred in the converted string (but the
//	Macintosh does not permit specifying this as of OS X 10.3.7).
//	A terminating NULL word is added following the converted characters.
//	Note that this routine simply converts the source string to C-style and
//	calls its routine.
//
// Parameters:
//	srcEncoding - Platform-specific text encoding value for the source
//	               string's characters
//	srcStr      - Pascal-style string containing the characters to be converted
//	dstUCStr    - Pointer to an array of 16-bit words for the converted
//	               Unicode characters to be returned in (NULL-word terminated)
//	maxDstChars - Maximum number of 16-bit words that dstUCStr can hold (note
//	               that surrogate and decomposed characters are > 16 bits);
//	               to handle all encodings safely, this must be:
//	               maxDstChars = (GetLength(srcStr) * 3) + 1
//	               (just call StringToUnicodeMaxChars)
//	pDstLen     - Returns the number of 16-bit words the converted string
//	               occupies in dstUCStr, not including the NULL-word terminator
//
// Return Value:
//	Whether the conversion was successful.  Failures include not enough space
//	in the destination array; source characters that can't be converted are
//	not considered a failure [a richer return type could be considered].
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool StringToUnicode (GSTextEncoding srcEncoding, ConstGSStr255Param srcStr,
              UCStrPtr dstUCStr, UCCharCnt maxDstChars, UCCharCnt* pDstLen)
{
	bool	success;
	
	/* Call the Character Array Version and Append   */
	/*  a NULL-Word Terminator to the Returned Array */
	success = StringToUnicode(srcEncoding, (char*)&srcStr[1], GetLength(srcStr),
	                           dstUCStr, (maxDstChars-1), pDstLen);
	if (success || (*pDstLen < maxDstChars))
	  dstUCStr[*pDstLen] = NULL;
	return(success);
} // StringToUnicode


//*****************************************************************************
//
// Function Name:  UnicodeToStringMaxChars
//
// Description:
//	Returns the maximum number of non-Unicode character bytes in a C- or
//	Pascal-style string (with a terminating NULL character or preceding length
//	byte) required to represent the specified number of Unicode characters.
//	Note that this is completely simplistic, is based only on the length of
//	the Unicode string and ignores its destination encoding, and calculates
//	the worst case as stated by Apple in its publication "Text Encoding
//	Manager Reference".  But using this and reallocating the string buffer
//	after calling UnicodeToString should usually be faster than doing the
//	conversion an extra time beforehand to get the character count.
//		The original formula multiplied srcLen + 1 by 1.5.  It's hard to
//	believe an Apple publication said that would be the worst case, as a
//	string of all Japanese characters would have 2 bytes per unicode char.
//	This caused all Japanese text objects with more than 4 or 5 characters
//	to be corrupted.  I changed it to multiply by 3, which I think is safe. [BAF 9/12/05]
//	XXX_JDW_REVIEW
//
// Parameters:
//	srcLen - Number of characters in the Unicode string to be converted
//
// Return Value:
//	Maximum number of non-Unicode character bytes (including NULL terminator
//	or preceding length byte) required to represent a Unicode string of the
//	specified length in a non-Unicode encoding
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

size_t UnicodeToStringMaxChars (UCCharCnt srcLen)
{
	return((size_t)((srcLen + 1) * 3) + 1);
} // UnicodeToStringMaxChars


//*****************************************************************************
//
// Function Name:  UnicodeToString (character array version)
//
// Description:
//	Converts the characters of Unicode encoding and UTF-16 form in an array to
//	a character array in the specified encoding.  A terminating NULL byte is
//	NOT added following the converted characters.
//
// Parameters:
//	dstEncoding - Platform-specific text encoding value for the destination
//	               string's characters
//	srcUCArray  - Pointer to an array of 16-bit words containing the Unicode
//	               characters to be converted
//	srcLen      - Number of words in srcUCArray containing characters to be
//	               converted
//	dstArray    - Pointer to an array for the converted characters to be
//	               returned in (may be 8- or 16-bit depending on encoding)
//	maxDstBytes - Maximum number of 8-bit bytes (always) that dstArray can
//	               hold; to handle all encodings, this must be:
//	               maxDstChars = (srcLen * 1.5) + 1
//	               (just call UnicodeToStringMaxChars)
//	pDstLen     - Returns the number of 8-bit bytes the converted characters
//	               occupy in dstArray
//
// Return Value:
//	Whether the conversion was successful.  Failures include not enough space
//	in the destination string; source characters that can't be converted are
//	not considered a failure [a richer return type could be considered].
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool UnicodeToString (GSTextEncoding dstEncoding, ConstUCCharPtr srcUCArray,
      UCCharCnt srcLen, char* dstArray, size_t maxDstBytes, size_t* pDstLen)
{
	/* Check the Destination String Provided */
	/* ------------------------------------- */
	if (maxDstBytes == 0) return(false);
	if (!VERIFYPOINTER(kMike, dstArray, maxDstBytes)) {
		*pDstLen = 0;
		return(false);
	}
	
	if (srcLen == 0) {
		/* Source Array Is Empty */
		/* --------------------- */
		dstArray[0] = NULL;  // Just for grins; room available in this case
		*pDstLen = 0;
		return(true);
		}
	else {
		/* Source Array Is Not Empty */
		/* ------------------------- */
#if GS_WIN
		Uint32		mapOptions;
		
		/* Convert the Unicode Text to the Specified */
		/*  Destination Encoding and Check for Error */
		// Non-zero mapping options are a problem with some encodings,
		//  but we don't want accented Unicode characters becoming two
		if (dstEncoding == CP_SYMBOL || dstEncoding == CP_UTF8)
			mapOptions = 0;
		else
			mapOptions = WC_COMPOSITECHECK;
		*pDstLen = WideCharToMultiByte(dstEncoding, mapOptions, (LPCWSTR) srcUCArray,
		            srcLen, dstArray, maxDstBytes, NULL, NULL);
		
		if (*pDstLen == 0)
			return false;

		if (dstEncoding != CP_UTF8) {
			// char encoding has been translated to Mac Roman at this point, so
			// construct tempArray to contain the corresponding ANSI vals before
			// doing Unicode conversion -- need to Kludge this as this file is in SDKLib
 			char *tempArray = new char[*pDstLen];
			memcpy(tempArray, dstArray, *pDstLen);
			#if _MINICAD_
				WinAnsiToMacBuf((unsigned char *)tempArray, (unsigned char *)dstArray, *pDstLen);
			#else
				TAnsiMacBufDataStructAlturaRemoval data;
				data.feFunctionEnumeration	= eAnsiToMacBuf;
				data.fpInAnsiString			= (unsigned char *)tempArray;
				data.fpOutMacString		    = (unsigned char *)dstArray;
				data.fStringLength			= *pDstLen;
			
				VERIFYN(kChao, GS_Kludge(gCBP, kKludgeAlturaBufFunctionAccess, &data, pDstLen));
			#endif
			delete[] tempArray;
		}
		return true;
#else
		static bool			firstTime = true;
		static GSTextEncoding prevEncoding = dstEncoding;
		static UnicodeMapping		uniToTextMap;
		static UnicodeToTextInfo	uniToTextInfo;
		ByteCount			srcBytesConv, dstBytesUsed;
		OSStatus			err = noErr;
		bool				success;

		/* Construct the Unicode-to-Text Mapping */
		if (firstTime) {
			uniToTextMap.unicodeEncoding =
				CreateTextEncoding(kTextEncodingUnicodeDefault,
				kUnicodeNoSubset, kUnicode16BitFormat);
			uniToTextMap.mappingVersion = kUnicodeUseLatestMapping;
		}
		uniToTextMap.otherEncoding   = dstEncoding;
		
		/* Construct the Text-to-Unicode Information Block */
		if (firstTime)
			err = CreateUnicodeToTextInfo(&uniToTextMap, &uniToTextInfo);
		else if (prevEncoding != dstEncoding) {
			DisposeUnicodeToTextInfo(&uniToTextInfo);
			err = CreateUnicodeToTextInfo(&uniToTextMap, &uniToTextInfo);
			prevEncoding = dstEncoding;
		}
		success = (err == noErr);
		
		/* Convert the Unicode Text to the Specified Encoding, */
		/*  Allowing Loose Mappings and Fallbacks So All       */
		/*  Characters Are Converted as Usefully as Possible   */
		if (success) {
			dstBytesUsed = 0;  // So we can hope to return partial conversions
			err = ConvertFromUnicodeToText(uniToTextInfo, (srcLen*sizeof(UCChar)),
			       srcUCArray, (kUnicodeUseFallbacksMask | kUnicodeLooseMappingsMask),
			       0, nil, nil, nil, (maxDstBytes-1), &srcBytesConv,
			       &dstBytesUsed, dstArray);
			*pDstLen = dstBytesUsed;
			success = (err == noErr) || (err == kTECUsedFallbacksStatus	);
   		}
		firstTime = false;
		return(success);
#endif  // GS_WIN
	}
} // UnicodeToString


//*****************************************************************************
//
// Function Name:  UnicodeToString (C string version)
//
// Description:
//	Converts the characters of Unicode encoding and UTF-16 form in an array to
//	a C-style string in the specified encoding.
//
// Parameters:
//	dstEncoding - Platform-specific text encoding value for the destination
//	               string's characters
//	srcUCStr    - Pointer to an array of 16-bit words containing the Unicode
//	               characters to be converted, terminated with a NULL word
//	dstStr      - Pointer to a C-style string for the converted characters
//	               to be returned in
//	maxDstBytes - Maximum number of 8-bit bytes that dstStr can hold
//	               (including the terminating NULL); to handle all
//	               encodings, this must be:
//	               maxDstChars = (StringLength(srcUCStr) * 1.5) + 1
//	               (just call UnicodeToStringMaxChars)
//	pDstLen     - Returns the number of 8-bit bytes the converted characters
//	               occupy in dstStr, not including the terminating NULL byte
//
// Return Value:
//	Whether the conversion was successful.  Failures include not enough space
//	in the destination string; source characters that can't be converted are
//	not considered a failure [a richer return type could be considered].
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool UnicodeToString (GSTextEncoding dstEncoding, ConstUCStrPtr srcUCStr,
                       char* dstStr, size_t maxDstBytes, size_t* pDstLen)
{
	bool		success;
	
	/* Call the Character Array Version and Append   */
	/*  a NULL-Byte Terminator to the Returned Array */
	success = UnicodeToString(dstEncoding, srcUCStr, StringLength(srcUCStr),
	                           dstStr, (maxDstBytes-1), pDstLen);
	if (success || (*pDstLen < maxDstBytes))
	  dstStr[*pDstLen] = NULL;
	return(success);
} // UnicodeToString


//*****************************************************************************
//
// Function Name:  UnicodeToString (Pascal string version)
//
// Description:
//	Converts the characters of Unicode encoding and UTF-16 form in an array to
//	a Pascal-style string in the specified encoding.  Note that this routine
//	simply calls the C-style routine and converts its result to Pascal-style.
//
// Parameters:
//	dstEncoding - Platform-specific text encoding value for the destination
//	               string's characters
//	srcUCStr    - Pointer to an array of 16-bit words containing the Unicode
//	               characters to be converted, terminated with a NULL word
//	dstStr      - Pointer to a Pascal-style string for the converted characters
//	               to be returned in
//	maxDstBytes - Maximum number of 8-bit bytes that dstStr can hold
//	               (including the preceding length byte); to handle all
//	               encodings, this must be:
//	               maxDstChars = (StringLength(srcUCStr) * 1.5) + 1
//	               (just call UnicodeToStringMaxChars)
//	pDstLen     - Returns the number of 8-bit bytes the converted characters
//	               occupy in dstStr, not including the preceding length byte
//
// Return Value:
//	Whether the conversion was successful.  Failures include not enough space
//	in the destination string; source characters that can't be converted are
//	not considered a failure [a richer return type could be considered].
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool UnicodeToString (GSTextEncoding dstEncoding, ConstUCStrPtr srcUCStr,
                       GSStringPtr dstStr, size_t maxDstBytes, size_t* pDstLen)
{
	bool	success;
	char	dstCStr[256];
	
	/* Call the C-String Version and Copy a  */
	/*  Successful Result to a Pascal String */
	ASSERTN(kMike, (maxDstBytes <= 256));
	success = UnicodeToString(dstEncoding, srcUCStr, dstCStr, sizeof(dstCStr),
	                           pDstLen);
	if (success) {
		StringCopy(dstCStr, dstStr, maxDstBytes);
		if (strlen(dstCStr) > (maxDstBytes - 1)) success = false;
		}
	else {
		SetLength(dstStr, 0);
	}
	*pDstLen = GetLength(dstStr);
	return(success);
} // UnicodeToString


/*============================================================================*/
/****************************************/
/* Unicode String Manipulation Routines */
/****************************************/


//*****************************************************************************
//
// Function Name:  StringLength
//
// Description:
//	Determines the number of 16-bit words that contain characters in a Unicode
//	string.  For our purposes, a Unicode string is an array of UTF-16
//	characters that is terminated with a NULL word (not included in length).
//	This is thus a syntactic rather than semantic character count
//	(e.g. a decomposed character counts per code-point, not as one character,
//	and a surrogate pair counts as two words, not as one character).
//
// Parameters:
//	ucString - Pointer to a UTF-16 string (NULL-word terminated UCChar array)
//
// Return Value:
//	Whether a cluster break was located
//
// Created by:  Michael Hartman, February 2005
//
//*****************************************************************************

UCCharCnt StringLength (ConstUCStrPtr ucString)
{
	UCCharCnt	len;
	
	len = 0;
	while (ucString[len] != 0) {
		len++;
	}
	return(len);
} // StringLength


//*****************************************************************************
//
// Function Name:  StringEqual (terminated string version)
//
// Description:
//	Determines whether the two strings of UTF-16 characters are equivalent
//	according to Unicode rules (as implemented in the underlying OS).  This
//	means, for example, that precomposed and decomposed versions of a character
//	are equal.  The comparison can be specified as case-insensitive or not.
//
// Parameters:
//	str1 - Pointer to a UTF-16 string (NULL-word terminated UCChar array)
//	str2 - Pointer to a UTF-16 string (NULL-word terminated UCChar array)
//
// Return Value:
//	Whether the two strings are equivalent by Unicode rules
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool StringEqual (ConstUCStrPtr str1, ConstUCStrPtr str2, bool ignoreCase)
{
	return(StringEqual(str1, StringLength(str1), str2, StringLength(str2),
	        ignoreCase));
} // StringEqual


//*****************************************************************************
//
// Function Name:  StringEqual (character array version)
//
// Description:
//	Determines whether the two arrays of UTF-16 characters are equivalent
//	according to Unicode rules (as implemented in the underlying OS).  This
//	means, for example, that precomposed and decomposed versions of a character
//	are equal.  The comparison can be specified as case-insensitive or not.
//
// Parameters:
//	chars1 - Pointer to an array of 16-bit words containing the UTF-16
//	          characters to be compared
//	len1   - Number of 16-bit words in 1 containing characters to be compared
//	chars2 - Pointer to an array of 16-bit words containing the UTF-16
//	          characters to be compared
//	len2   - Number of 16-bit words in 2 containing characters to be compared
//
// Return Value:
//	Whether the two arrays of characters are equivalent by Unicode rules
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool StringEqual (ConstUCCharPtr chars1, UCCharCnt len1,
                   ConstUCCharPtr chars2, UCCharCnt len2, bool ignoreCase)
{
	bool	equalResult;
	
	if ((len1 <  0) || (len2  < 0)) return(false);
	if ((len1 == 0) || (len2 == 0)) return(len1 == len2);
	
#if GS_WIN
	Uint32	compareFlags;
	Sint32	orderResult;
	
	compareFlags = NORM_IGNOREWIDTH | SORT_STRINGSORT;
	if (ignoreCase) compareFlags |= NORM_IGNORECASE;
	orderResult = CompareStringW(LOCALE_USER_DEFAULT, compareFlags,
	                              (LPCWSTR) chars1, len1, (LPCWSTR) chars2, len2);
	equalResult = (orderResult == CSTR_EQUAL);  // If error, != more probable
	
#else  /* Macintosh */
	UCCollateOptions	collateOpts;
	Boolean				eqlResult;
	OSStatus			err;
	
	collateOpts = kUCCollateStandardOptions | kUCCollatePunctuationSignificantMask;
	if (ignoreCase) collateOpts |= kUCCollateCaseInsensitiveMask;
	err = UCCompareTextDefault(collateOpts, chars1, len1, chars2, len2,
	                            &eqlResult, nil);
	if (err != noErr)
		equalResult = false;  // We have no idea, but no is more probable
	else
		equalResult = eqlResult;
#endif  // GS_WIN
	
	return(equalResult);
} // StringEqual


//*****************************************************************************
//
// Function Name:  StringEqual (terminated string version)
//
// Description:
//	Copies one string of UTF-16 characters to another string variable.  If the
//	length of the source string exceeds the specified size of the destination
//	string, the copy is truncated at a character cluster break.
//
// Parameters:
//	srcStr      - Pointer to a UTF-16 string to be copied from
//	dstStr      - Pointer to a UTF-16 string to be copied into
//	maxDstBytes - Size in bytes of the destination string parameter (in bytes
//	               rather than characters so can call sizeof(dstStr) to get;
//	               if instead have a character count (including NULL term)
//	               for an allocated buffer, multiply that by sizeof(UCChar)
//
// Return Value:
//	None
//
// Created by:  Michael Hartman, February 2005
//
//*****************************************************************************

void StringCopy (ConstUCStrPtr srcStr, UCStrPtr dstStr, size_t maxDstBytes)
{
	UCCharCnt	srcLen, maxDstChars, charsToCopy;
	bool		success;
	
	/* Determine How Many Characters from the String Will Fit, */
	/*  Making Sure to Truncate the Copy on a Cluster Boundary */
	srcLen = StringLength(srcStr);
	maxDstChars = maxDstBytes / sizeof(UCChar);
	if (maxDstChars > srcLen)
		charsToCopy = srcLen;
	else {
		charsToCopy = maxDstChars - 1;
		success = FindCharClusterBreak(srcStr, srcLen, charsToCopy, kFindRev,
		                                &charsToCopy);
	}
	
	/* Copy the Source Characters that Fit to the Destination String */
	if (!VERIFYPOINTER(kMike, dstStr, ((charsToCopy+1)*sizeof(UCChar))))
		dstStr[0] = NULL;
	else {
		memcpy(dstStr, srcStr, (charsToCopy*sizeof(UCChar)));
		dstStr[charsToCopy] = NULL;
	}
} // StringCopy


//*****************************************************************************
//
// Function Name:  FindCharClusterBreak
//
// Description:
//	Finds the Unicode character cluster break at or nearest the specified
//	position in the character array, looking in the direction specified.
//	Note that the starting position specified and the cluster position
//	returned of "n" ask/state that a cluster ends with the character at (n-1).
//	The caller should always pass a pointer to the beginning of the character
//	array being scanned and its full length rather than simply a pointer to
//	(or trimmed length of) the starting position, because it is assumed that
//	the character array as specified begins and ends on a cluster break.
//
// Parameters:
//	charArray - Pointer to an array of 16-bit words containing the UTF-16
//	             characters to be searched for the cluster break;
//               it is assumed that this begins and ends on a cluster break
//	length    - Number of 16-bit words in charArray that contain characters
//	             to be searched
//	startPos  - Zero-based starting position to search for a cluster break;
//	             note that the value of "length" is valid when moving backwards
//	forward   - Whether to move forward or backward from startPos if
//	             it is not at a cluster break
//	pBreakPos - Returns the nearest-found character position that is the
//	             boundary between clusters (this character starts new cluster);
//	             if an error occurs, startPos is returned as the best guess
//
// Return Value:
//	Whether a cluster break was located
//
// Created by:  Michael Hartman, December 2004
//
//*****************************************************************************

bool FindCharClusterBreak (ConstUCCharPtr charArray, UCCharCnt length,
		             UCCharCnt startPos, bool forward, UCCharCnt* pBreakPos)
{
	bool		success;
	
	/* Return Result Immediately for Trivial Cases */
	/*  (Assume Array Contains Complete Clusters)  */
	/* ------------------------------------------- */
	if (startPos > length) startPos = length;
	if ((startPos == 0) || (startPos == length)) {
		*pBreakPos = startPos;
		return(true);
	}
	
	/* Look for the Nearest Character Cluster Break Either */
	/*  At, Following or Preceding the Specified Position  */
	/* --------------------------------------------------- */
#if GS_WIN
	const	Uint32	kMaxCombine = 4;  // Guessed maximum of combining chars
	const	Sint32  kBadStartValue = -99;
	
	UCCharCnt		searchPos, numCharsCheck, pos;
	SCRIPT_ITEM		scriptItems[kMaxCombine*4];
	SCRIPT_LOGATTR	scriptAttrs[kMaxCombine*4];
	int				numScriptItems;
	Sint32			itemWithStart, item;
	bool			found;
	HRESULT			err;

	itemWithStart = kBadStartValue;
	
	/* Parse an Area of Twice the Expected Combining Characters around */
	/*  the Specified Start Position (for Speed and Memory Reasons)    */
	success = false;
	*pBreakPos = startPos;
	searchPos = max((long)((long)startPos-(kMaxCombine*2)), 0);
	numCharsCheck  = min((startPos+(kMaxCombine*2)), length) - searchPos + 1;
	
	/* Determine the Individually Shapeable Runs (by Text Direction or  */
	/*  Glyph Shaping Engine), and Find the One with the Start Position */
	err = ScriptItemize((const WCHAR *) &charArray[searchPos], numCharsCheck,
	             (sizeof(scriptItems)/sizeof(SCRIPT_ITEM)), NULL, NULL,
	             scriptItems, &numScriptItems);
	if (err == S_OK) {
		for (item = 0; item < (numScriptItems-1); item++) {
			if ((scriptItems[item].iCharPos <= (long)startPos) &&
			 ((long)startPos <= scriptItems[item+1].iCharPos)) {
			 	itemWithStart = item;
				break;
			}
		}
	}
	
	/* Get the Attributes for the Characters in that Run, then */
	/*  Search for a Cluster Break in the Specified Direction; */
	/*  We Can Limit Our Search to the One Run Because a Run   */
	/*  Must Begin With and Be Followed By a Cluster Break     */
	if (err == S_OK && itemWithStart != kBadStartValue) {
		searchPos = scriptItems[itemWithStart].iCharPos;
		numCharsCheck = scriptItems[itemWithStart+1].iCharPos - searchPos;
		err = ScriptBreak((const WCHAR *) &charArray[searchPos], numCharsCheck,
		                   &scriptItems[itemWithStart].a, scriptAttrs);
		if (err == S_OK) {
			found = false;
			if (forward) {
				for (pos = (startPos-searchPos); pos < numCharsCheck; pos++) {
					if (scriptAttrs[pos].fCharStop == 1) {
						*pBreakPos = pos + searchPos;
						break;
					}
				}
				if (!found) *pBreakPos = numCharsCheck + searchPos;  // Okay
				}
			else {
				for (pos = (startPos-searchPos); pos >= 0; pos--) {
					if (scriptAttrs[pos].fCharStop == 1) {
						*pBreakPos = pos + searchPos;
						break;
					}
				}
				if (!found) *pBreakPos = searchPos;  // Should never happen
			}
			success = true;
		}
	}
	
#else  /* Macintosh */
	UCCharCnt				searchPos;
	TextBreakLocatorRef		breakRef;
	UCTextBreakOptions		breakOpts;
	OSStatus				err;
	
	/* Adjust the Search Start, Since Break Routines  */
	/*  Look for the Next Break of the Specified Type */
	success = false;
	*pBreakPos = startPos;
	if (forward) {
		/* Zero startPos handled as trivial case above,  */
		/*  so won't get a negative UCCharCnt value here */
		searchPos = startPos - 1;
		}
	else {
		/* Shouldn't have a startPos of length here, either */
		searchPos = min((startPos+1), length);
	}
	
	/* Build a Text Break Locator Structure */
	err = UCCreateTextBreakLocator(nil, 0, kUCTextBreakClusterMask, &breakRef);
	
	/* Find the Next Cluster Break in the Specified Direction */
	if (err == noErr) {
		if (forward)
			breakOpts = kUCTextBreakLeadingEdgeMask;
		else
			breakOpts = kUCTextBreakGoBackwardsMask;
		err = UCFindTextBreak(breakRef, kUCTextBreakClusterMask, breakOpts,
		                       charArray, length, searchPos, pBreakPos);
		if (err != noErr)
			*pBreakPos = startPos;
		else
			success = true;
		err = UCDisposeTextBreakLocator(&breakRef);
	}
#endif  // GS_WIN
	
	return(success);
} // FindCharClusterBreak


/*============================================================================*/
/*============================================================================*/
/***********************************************************************/
/* Non-Unicode Pascal and C String Manuipulation Routines and Classes */
/***********************************************************************/


/*============================================================================*/
// System and Application Encoding Retrieval Routines


//*****************************************************************************
//
// Function Name:  GetSystemEncoding
//
// Description:
//	Returns the encoding to use in Unicode conversion functions for text
//	that is in the current "system" encoding.  This actually reflects the
//	choice of the user in the "International" system configuration preferences.
//	Note that the meaning of the encoding value returned is defined by the
//	Macintosh and Windows platforms independently (platform constants).
//
// Parameters:
//	None
//
// Return Value:
//	Platform-defined value specifying the current default "system" (user-set)
//	character encoding
//
// Created by:  Robert McBride, ????
//
//*****************************************************************************

GSTextEncoding GetSystemEncoding ()
{
	GSTextEncoding	encoding;
	
#if GS_WIN
	/* Use "ANSI" Code Page on Windows */
	encoding = GetACP();
	
#else
	encoding = CFStringGetSystemEncoding();
#endif  // GS_WIN
	
	return(encoding);
}


//*****************************************************************************
//
// Function Name:  GetApplicationEncoding
//
// Description:
//	Returns the encoding to use in Unicode conversion functions for text
//	that is in the current application encoding.  On the Mac for a non-bundled
//	application (which VW 12 is), this is based on information in the PList.
//	On Windows, DON'T YET HAVE THE SOLUTION.
//
// Parameters:
//	None
//
// Return Value:
//	Platform-defined value specifying the current application's character
//	encoding.
//
// Created by:  Michael Hartman, January 2005
//
//*****************************************************************************

GSTextEncoding GetApplicationEncoding ()
{
	GSTextEncoding	encoding;
	
#if GS_WIN
	/* Use "ANSI" Code Page on Windows */
	// XXX: Don't know what to do for Windows--VW doesn't seem to call
	//  InitMUILanguage, so GetMUILanguage and getting that language's
	//  encoding doesn't seem useful.  Perhaps GetUserDefaultLangID?  Or maybe
	//  GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE, buff, 6).  MFH 01/05
	encoding = GetACP();
#else
	// We'll just have to see if this has the same Icelandic bug as
	//  CFStringGetSystemEncoding, but have not heard anything  MFH 01/05
	encoding = GetApplicationTextEncoding();
#endif  // GS_WIN
	
	return(encoding);
} // GetApplicationEncoding


#if GS_MAC
bool UseIcelandicEncoding () {
	return(GetSystemEncoding() == kTextEncodingMacIcelandic);
} // UseIcelandicEncoding
#endif


// ===========================================================================
// == Strings
// ==

// ---------------------------------------------------------------------------
// -- StringCopy

void StringCopy(ConstGSStr255Param src, GSStringPtr dst, size_t n)
// Pascal- to Pascal-style string copy. Includes length byte so
// to copy m characters n should be m + 1. Example: to copy to
// Str255, n = 256 or sizeof(Str255).
{
	if (n == 0 || !dst) 
		return /*dst*/;
	if (!src || src[0] == 0) { 
		*dst = 0;
		return /*dst*/;
	}
	
	ASSERTN(kChao, n <= 256);
		
	size_t		i = ((*src + 1 < (long)n) ? *src : n - 1);
	
	//IFPRINT(*src > n - 1, "### StringCopy - source too long; truncating to fit.");

	*dst = (unsigned char) i;
	dst++, src++;
	for (; i > 0; i--) 
		*dst++ = *src++;
	
	return /*d*/;
}

void StringCopy(const char *src, GSStringPtr dst, size_t n)
// C- to Pascal-style string copy. Includes length byte so
// to copy m characters n should be m + 1. Example: to copy to
// Str255, n = 256 or sizeof(Str255).
{
	size_t				l = 0;				// Length counter.
	const GSStringPtr	d = dst;			// Save original dst pointer.
	
	if (n <= 0 || !dst)
		return /*dst*/;
		
	ASSERTN(kChao, n <= 256);
	if (n > 256) n = 256;
				
	if (src) {
		dst++;								// Skip over length byte.
		while (l < n - 1 && (*dst++ = *src++) != 0) // Copy characters, keeping track of how many.
			l++;
	}
	*d = (unsigned char) l;					// Set length byte.
	return /*d*/;
}

void StringCopy(const void *src, size_t len, GSStringPtr dst, size_t n)
// Raw storage to Pascal-style string copy. Includes length byte so
// to copy m characters n should be m + 1. Example: to copy to
// Str255, n = 256 or sizeof(Str255) and len is up to 255 (bigger len
// is ok but will be truncated).
{
	if (!dst || n == 0) 
		return /*dst*/;
	
	if (src) {
		const char *		src_p = (const char *) src;
	
		ASSERTN(kChao, n <= 256);
		
		n = Min(n-1, len);
		*dst++ = (unsigned char) n;
		for (; n > 0; n--) 
			*dst++ = *src_p++;
		return;
	}
	else {
		*dst = 0;
		return;
	}
}

void StringCopy(ConstGSStr255Param src, char *dst, size_t n)
// Pascal- to C-style string copy. Includes length byte/null terminator so
// to copy m characters n should be m + 1. Example: to copy from
// Str255 to a char[256], n = 256.
{
	if (n <= 0 || !dst) return /*dst*/;
	
	if (src) {
		ASSERTN(kChao, n <= 256);
		
		size_t sl = Min((size_t) src[0], n-1);
		dst[sl] = '\0';	// Terminate destination, since strncpy won't do it.
		if (sl)
			/*return*/ strncpy(dst, (const char *) &src[1], sl);
		return;
	}
	else {
		*dst = '\0';
		return /*dst*/;
	}
}

void StringCopy(const char *src, char *dst, size_t n)
// C- to C-style string copy. Includes null terminator so
// to copy m characters n should be m + 1.
{
	// NOTE: This function is NOT the same as strncpy, because strncpy
	// won't necessarily null-terminate the destination, and doesn't
	// handle null string pointers!
	
	if (n <= 0 || !dst) 
		return /*dst*/;
	
	if (src) {
		size_t sl = Min(strlen(src), n-1);
		dst[sl] = '\0';
		if (sl)
			/*return*/ strncpy(dst, src, sl);
		return;
	}
	else {
		*dst = '\0';
		return /*dst*/;
	}
}

void StringCopy(const void *src, size_t len, char *dst, size_t n)
// Raw storage to C-style string copy. Includes null terminator so
// to copy m characters n should be m + 1. 
{
	if (!dst || n <= 0) 
		return /*dst*/;

	if (src) {
		//char * const		d = dst;
		const char *		src_p = (const char *) src;

		n = Min(n-1, len);
		for (; n > 0; n--) 
			*dst++ = *src_p++;
		*dst = '\0';
		return /*d*/;
	}
	else {
		*dst = '\0';
		return /*dst*/;
	}
}

// ---------------------------------------------------------------------------
// -- StringCat

GSStringPtr StringCat(GSStringPtr dst, ConstGSStr255Param src, size_t n)
	// ## Catenate Pascal- to Pascal-style string.
{
	if (n <= (size_t) (GetLength(dst) + 1) || !dst || !src) return dst;
	
	const GSStringPtr	d = dst;
	size_t				copylen = ((size_t)(*dst + *src + 1) <= n) ? *src : n - *dst - 1;
	
	#if 0
	IFPRINT(copylen != *src, "### StringCat - result would be too long; truncating to fit.");
	#endif
	
	dst += *dst + 1;	// Step to end of destination string.
	src++;				// Skip past length byte of source.
	*d += copylen;		// Increment length of destination.
	for (; copylen > 0; copylen--) *dst++ = *src++;	// Do the copy.
	
	return d;
}

GSStringPtr StringCat(GSStringPtr dst, const char *src, size_t n)
	// ## Catenate C- to Pascal-style string.
{
	if (n <= (size_t) GetLength(dst) + 1 || !dst || !src) return dst;
	
	const GSStringPtr	d = dst;
	size_t				copylen = n - GetLength(dst) - 1;
	
	for (dst += *dst + 1; copylen > 0 && *src; copylen--)
		(*d)++, *dst++ = *src++;
	
	return d;
}

GSStringPtr StringCat(GSStringPtr dst, const void *src, size_t len, size_t n)
	// ## Catenate raw storage to Pascal-style string.
{
	if (n <= (size_t) GetLength(dst) + 1 || !dst || !src) return dst;
	
	const GSStringPtr	d = dst;
	const char		   *src_p = (const char *) src;
	size_t				copylen = Min(n - GetLength(dst) - 1, len);
	
	for (dst += *dst + 1; copylen > 0; copylen--)
		(*d)++, *dst++ = *src_p++;
	
	return d;
}

char * StringCat(char *dst, ConstGSStr255Param src, size_t n)
	// ## Catenate Pascal- to C-style string.
{
	if (n > 0 && src && dst) {
		size_t dl = strlen(dst);
		char *dst2 = dst+dl;
		StringCopy(src, dst2, n > dl ? n-dl : 0);
		return dst2;
	}
	return dst;
}

char * StringCat(char *dst, const char *src, size_t n)
	// ## Catenate C- to C-style string.
{
	if (n > 0 && src && dst) {
		size_t dl = strlen(dst);
		StringCopy(src, dst+dl, n > dl ? n-dl : 0);
	}
	return dst;
}

char * StringCat(char *dst, const void *src, size_t len, size_t n)
	// ## Catenate raw storage to Pascal-style string.
{
	if (n > 0 && src && dst) {
		size_t dl = strlen(dst);
		StringCopy((const char *) src, len, dst+dl, n > dl ? n-dl : 0);
	}
	return dst;
}

// ---------------------------------------------------------------------------
// -- StringEqual

Bool8 StringEqual(ConstGSStr255Param a, ConstGSStr255Param b)
{
	if (a && b && (a[0] == b[0])) {
		for (short i = *a; i >= 0; i--)
			if (*a++ != *b++) return false;
	
		return true;
	}
	else {
		// If one string is nil, then the other must be nil or empty.
		return IsEmpty(a) && IsEmpty(b);
	}
}

Bool8 StringEqual(ConstGSStr255Param a, const char *b)
{
	if (a && b) {
		// make sure the full length of a is identical to the equivalent characters in b and that b ends with the final character of a
		// faster way of saying a[0] == strlen(b) - PCP
		if (strncmp((const char *) &a[1], b, a[0]) == 0) {
			// if we know the strings match to the last character
			// of string a, then we can be sure they are equal only
			// if the lengths match
			// this makes sure the next character in b is the NUL terminator
			return b[a[0]] == '\0';
		}
		else
			return false;
	}
	else {
		// If one string is nil, then the other must be nil or empty.
		return IsEmpty(a) && IsEmpty(b);
	}
}

Bool8 StringEqual(const char *a, const char *b)
{
	if (a && b) {
		return strcmp(a, b) == 0;
	}
	else {
		// If one string is nil, then the other must be nil or empty.
		return IsEmpty(a) && IsEmpty(b);
	}
}

// ---------------------------------------------------------------------------
// -- StringEqualIgnoringCase

Bool8 StringEqualIgnoringCase(ConstGSStr255Param a, ConstGSStr255Param b)
{
#if _MINICAD_
	return FasterMagIDString(a+1, b+1, a[0], b[0]) == 0;
#else
	CStr255 cA(a), cB(b);
	return GS_CStringEqual(gCBP, cA, cB, kIgnoreCase);
#endif
}

// XXX_JDW_ALTURA CLEANUP [2003-02-20]
Bool8 StringEqualIgnoringCase(ConstGSStr255Param a, const char *b)
{
#if _MINICAD_
	return FasterMagIDString(&a[1], b, a[0], (short) strlen(b)) == 0;
#else
	CStr255 cA(a);
	return GS_CStringEqual(gCBP, cA, b, kIgnoreCase);
#endif
}


// XXX_JDW_ALTURA CLEANUP [2003-02-20]
Bool8 StringEqualIgnoringCase(const char *a, const char *b)
{
#if _MINICAD_
	return FasterMagIDString(a, b, strlen(a), strlen(b)) == 0;
#else
	return GS_CStringEqual(gCBP, a, b, kIgnoreCase);
#endif
}


// ---------------------------------------------------------------------------
// -- StringCompare

short StringCompare(ConstGSStr255Param a, ConstGSStr255Param b)
{
	 #if _MINICAD_
		return FasterMagString(a+1, b+1, a[0], b[0]);
	// For SDK plug-ins, the CompareString call was failing on Windows.  Altura has it
	// defined to IUCompPString, and its implementation of IUCompPString just returns 0.
	// In the main application, we have our own implementation of IUCompPString that
	// calls IUCompString.  So for Windows, I changed this to just call IUCompString. 
	// [BF 3/29/02]
	//#elif GS_MAC
	//	return CompareString(a, b, nil);
	//#else
	//	//>>>> ORIGINAL XXX_JDW_ALTURA
	//	//return CompareString(a, b, nil);
	//	//==== THEIRS 
	//	//return IUCompString(a, b);
	//	//==== YOURS 
	//	//return MacCompareString(a, b, nil);
	//	//<<<<
	//	return CompareText(&a[1], &b[1], a[0], b[0], nil);
	//#endif
	#else
		CStr255 cA(a), cB(b);
		return GS_CStringCompare(gCBP, cA, cB, kIncludeCase);
	#endif
}


// XXX_JDW_ALTURA CLEANUP [2003-02-20]
short StringCompare(ConstGSStr255Param a, ConstGSCStrPtr b)
{
	#if _MINICAD_
		return FasterMagString(&a[1], b, a[0], (short) strlen(b));
	#else
	//	return CompareText(&a[1], b, a[0], (short) strlen(b), nil);
		CStr255 cA(a);
		return GS_CStringCompare(gCBP, cA, b, kIncludeCase);
	#endif
}


// XXX_JDW_ALTURA CLEANUP [2003-02-20]
short StringCompare(const char *a, const char *b)
{
	#if _MINICAD_
		return FasterMagString(a, b, (short) strlen(a), (short) strlen(b));
	#else
	//	return CompareText(a, b, (short) strlen(a), (short) strlen(b), nil);
		return GS_CStringCompare(gCBP, a, b, kIncludeCase);
	#endif
}


// ---------------------------------------------------------------------------
// -- StringCompareIgnoringCase

short StringCompareIgnoringCase(ConstGSStr255Param a, ConstGSStr255Param b)
{
	#if _MINICAD_
		if (FasterMagIDString(a+1, b+1, a[0], b[0]) == 0)		// case-insensitive equality
			return 0;	// strings are equal
		else
			return FasterMagString(a+1, b+1, a[0], b[0]);		// case-sensitive ordering
	#else
		CStr255 cA(a), cB(b);
		return GS_CStringCompare(gCBP, cA, cB, kIgnoreCase);
	#endif
}


short StringCompareIgnoringCase(ConstGSStr255Param a, const char *b)
{
	#if _MINICAD_
		if (FasterMagIDString(a+1, b, a[0], (short) strlen(b)) == 0)	// case-insensitive equality
			return 0;
		else
			return FasterMagString(a+1, b, a[0], (short) strlen(b));	// case-sensitive ordering
	#else
		CStr255 cA(a);
		return GS_CStringCompare(gCBP, cA, b, kIgnoreCase);
	#endif
}


short StringCompareIgnoringCase(const char *a, const char *b)
{
	#if _MINICAD_
		if (FasterMagIDString(a, b, (short) strlen(a), (short) strlen(b)) == 0)		// case-insensitive equality
			return 0;
		else
			return FasterMagString(a, b, (short) strlen(a), (short) strlen(b));		// case-sensitive ordering
	#else
		return GS_CStringCompare(gCBP, a, b, kIgnoreCase);
	#endif
}

// ---------------------------------------------------------------------------
// -- StringInsert

void StringInsert(ConstGSStr255Param src, GSStringPtr dst, Uint8 index)
{
	/* NOTE: In the event that the full result is bigger than 255
		characters, StringInsert will preserve all of dst in preference
		to inserting all of src.  E.g., if you ask to insert a string
		of 255 x's in the middle of "foobar", you'll get "foo<249 x's>bar",
		not "foo<252 x's>".
	*/
	
	if (!src || !dst) return;
	if (!inrange(index, 1, 255)) return;
	
	int	copylen = (*src + *dst > 255) ? 255 - *dst : *src;
	int	x = index + copylen;
	
	memmove(&dst[x], &dst[index], 255 - x);
	memmove(&dst[index], &src[1], copylen);
	
	dst[0] += (unsigned char) copylen;
}

// ---------------------------------------------------------------------------
// -- StringDelete

void StringDelete(GSStringPtr s, Uint8 start, Uint8 count)
{
	if (!s) return;
	if (start > *s) return;
	
	if (start + count - 1 > *s) count = *s - start;
	
	GSStringPtr	src = s + start + count, dst = s + start;
	short		copylen = *s - start - count + 1;

	*s -= count;
	for (; copylen > 0; copylen--) *dst++ = *src++;
}	

void StringDelete(char *s, size_t start, size_t count)
{
	if (!s) return;
	
	size_t		len = strlen(s);
	
	if (start > len) return;
	if (start + count > len) count = len - start;
	if (count < 1) return;
	
	memmove(s + start, s + start + count, len - start - count + 1);
}


#ifdef _MINICAD_

// ---------------------------------------------------------------------------
// -- StringPos

short StringPos (ConstGSStr255Param findStr, ConstGSStr255Param str)
/* Searches for the first occurrence of findStr within str, and returns */
/*  its starting position.  If findStr is not found, 0 is returned.     */
/*  The comparison is case and diacritical sensitive, and may not yield */
/*  the proper result with 2-byte script systems.                       */
{
	
	short foundPos = 0;
	size_t findLen = GetLength(findStr);
	size_t strLen  = GetLength(str);
	if ((findLen > 0) && (strLen > 0) && (findLen <= strLen)) {

		Handle	strHdl;
		PtrToHandCk(&str[1], strHdl, strLen);
		if (strHdl != nil) {

			foundPos = (short) Munger(strHdl, 0, &findStr[1], findLen, nil, 0);
			if (foundPos < 0)
				foundPos = 0;
			else {
				foundPos += 1;  // Change from zero-based to one-based
				// Munger reports partial match at end, which we don't want
				if (DemoteTo<size_t>(kVictor, foundPos) > (strLen-findLen+1)) foundPos = 0;
			}

			DisposeHandle(strHdl);
		}
	}

	return(foundPos);
}

#endif // _MINICAD_


// GS_CARBON_REVISIT
// There are some places where c2pstr is simply necessary in the short term.
// Revisit. - PCP
static void localc2pstr (char* s)
{
	size_t	len;
	
	len = strlen(s);
	if (len > 255) len = 255;
	memmove(&s[1], &s[0], len);
	s[0] = len;
}

// ---------------------------------------------------------------------------
// -- psprintf

#define VSPRINTF(s, format, PostAction) \
	va_list ap; int r; \
	va_start(ap, format); r = vsprintf((char *) s, format, ap); va_end(ap); \
	PostAction; return r

int psprintf(char *s, const char *format, ...)
{
	VSPRINTF(s, format, NULL);
}

int psprintf(GSStr255 s, const char *format, ...)
{
	VSPRINTF(s, format, localc2pstr((char *) s));
}

int psprintf(char *s, ConstGSStr255Param format, ...)
{
	char		cformat[256];
	
	StringCopy(format, cformat, 256);
	va_list ap; int r;
	va_start(ap, format);
	r = vsprintf(s, cformat, ap);
	va_end(ap);
	localc2pstr( s);

	return r;
}

int psprintf(GSStr255 s, ConstGSStr255Param format, ...)
{
	char		cformat[256];

	StringCopy(format, cformat, 256);
	va_list ap; int r;
	va_start(ap, format);
	r = vsprintf((char *) s, cformat, ap);
	va_end(ap);
	localc2pstr((char *) s);

	return r;
}

#undef VSPRINTF


// ---------------------------------------------------------------------------
// -- Other functions

#if !_WINDOWS

int stricmp(const char *a, const char *b)
{
	char ua, ub;
	for (; *a && *b; ++a, ++b) {
		ua = toupper(*a);
		ub = toupper(*b);
		if (ua != ub)
			return (ua < ub ? -1 : 1);
	}
	// At this point at least one of *a or *b is equal to 0.
	return (*a == *b) ? 0 : (*a ? 1 : -1);
}

int strnicmp(const char *a, const char *b, size_t max)
{
	char ua, ub;
	size_t n;
	for (n=0; *a && *b && n<max; ++a, ++b, ++n) {
		ua = toupper(*a);
		ub = toupper(*b);
		if (ua != ub)
			return (ua < ub ? -1 : 1);
	}
	// At this point, either we have reached 'max' or at least one of *a or *b is equal to 0.
	return (*a == *b || n >= max) ? 0 : (*a ? 1 : -1);
}


#endif // !_WINDOWS


#endif // !_WINDOWS || _MINICAD_ || _GSESDK_


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