CFLite/CFTest/SuperString.cpp

From kJams Wiki
Jump to navigation Jump to search
/*
 *  SuperString.cpp
 *  CFTest
 *
 *  Created by David M. Cotter on 6/25/08.
 *  Copyright 2008 David M. Cotter. All rights reserved.
 *
 */
#include "stdafx.h"

#ifndef KJAMS
	#include "SuperString.h"
#else
	#include "CApp.h"
	#include "CPreferences.h"
	#include "StringUtils.h"
	#include "MessageAlert.h"
#endif


void CCFLog::operator()(CFTypeRef valRef) {
	#if 1
		SuperString			valStr;	valStr.Set_CFType(valRef);
		FILE				*log_fileP = stdout;

		if (i_crB) {
			valStr.append("\n");
		}

		#if defined(__WIN32__)

			#ifdef __MWERKS__
				//	console app
				static	HANDLE	consoleH = NULL;
				
				if (!consoleH) {
					consoleH = GetStdHandle(STD_OUTPUT_HANDLE);
				}
				
				unsigned long	outL;
				
				WriteConsoleW(consoleH, valStr.w_str(), valStr.uni().i_lengthL, &outL, NULL);
				
				log_fileP = NULL;
			#else
				//	GUI APP
				static	FILE	*s_logP = NULL;

				if (s_logP == NULL) {
					(void)fopen_s(&s_logP, "log.txt", "a");
				}
				
				log_fileP = s_logP;
			#endif
		#endif

		if (log_fileP) {
			
			#ifdef KJAMS
				Log(valStr.utf8Z(), false);
			#else
				fprintf(log_fileP, "%s", valStr.utf8Z());
				fflush(log_fileP);
			#endif
		}
	#else
		fflush(stdout);
		CFShow(valRef);
		if (i_crB) {
			printf("\n");
		}
		fflush(stdout);
	#endif
}

void CCFLog::operator()(CFStringRef keyRef, CFTypeRef valRef) {
	SuperString		keyStr(keyRef);
	
	keyStr.append(": ");
	
	{
		bool	wasB = i_crB;
		i_crB = false;
		
		operator()(keyStr.ref());
		i_crB = wasB;
	}

	operator()(valRef);
}

#ifndef KJAMS
void		LogErr(const char *utf8Z, OSStatus err, bool crB, bool unixB)
{
	if (err) {	//	&& gApp->Logging()) {
		SuperString		keyStr(uc(utf8Z));
		SuperString		valStr((long)err);
		CCFLog			logger(crB);
		
		logger(keyStr.ref(), valStr.ref());
	}
}
#endif

char*	strrstr(const char* stringZ, const char* findZ)
{
	bool		firstB = true, doneB = false;
	const char	*nextZ;
	
	do {
		if (firstB) {
			nextZ = strstr(stringZ, findZ);
		} else {
			nextZ = strstr(&stringZ[1], findZ);
		}
		
		doneB = nextZ == NULL;
		
		if (!doneB) {
			stringZ = nextZ;
		} else if (firstB) {
			stringZ = NULL;
		}
		
		firstB = false;		
	} while (!doneB);
	
	return const_cast<char *>(stringZ);
}

const char *		CopyLongToC(long valL)
{
	static	char	s_bufAC[256];
	
	sprintf(s_bufAC, "%d", valL);
	return s_bufAC;
}

float			CStringToFloat(const char *numF)
{
	float	valF = 0;
	
	sscanf(numF, "%f", &valF);
	return valF;
}

class Asciify {
	public: void operator()(char &ch) {
		if (ch < 32 || ch > 126) ch = '?';
	}
};

/********************************************************/
bool		CFStringIsEmpty(CFStringRef nameRef)
{
	return nameRef == NULL || CFStringEqual(nameRef, CFSTR(""));
}

CFStringEncoding	s_file_encoding = kCFStringEncodingInvalidId;
void				SetDefaultEncoding(CFStringEncoding encoding)
{
	s_file_encoding = encoding;
}

static CFStringEncoding	ValidateEncoding(CFStringEncoding encoding = kCFStringEncodingInvalidId)
{
	if (encoding == kCFStringEncodingInvalidId) {
		encoding = s_file_encoding;
		
		CF_ASSERT(encoding != kCFStringEncodingInvalidId);
	}
	
	return encoding;
}

CFStringRef		CFStringCreateWithC(
	const char *		bufZ, 
	CFStringEncoding	encoding)
{
	if (bufZ) {
		encoding = ValidateEncoding(encoding);
		
		CFStringRef		cf = NULL;
		
		cf = CFStringCreateWithCString(kCFAllocatorDefault, bufZ, encoding);
		if (!cf) cf = CFStringCreateWithCString(kCFAllocatorDefault, bufZ, kCFStringEncodingWindowsLatin1);
		if (!cf) cf = CFStringCreateWithCString(kCFAllocatorDefault, bufZ, kCFStringEncodingISOLatin1);
		if (!cf) cf = CFStringCreateWithCString(kCFAllocatorDefault, bufZ, kCFStringEncodingMacRoman);
		
		CF_ASSERT(cf);
		if (!cf) return NULL;
		
		return (CFStringRef)CFRetainDebug(cf, false);
	} else {
		return (CFStringRef)CFRetainDebug(CFSTR(""));
	}
}

CFStringRef		CFStringCreateWithCu(
	const UTF8Char *	bufZ, 
	CFStringEncoding	encoding)
{
	return CFStringCreateWithC((const char *)bufZ, encoding);
}

static	inline CFRange		CFStrGetRange(CFStringRef ref)
{
	return CFRangeMake(0, CFStringGetLength(ref));
}

ustring		&CopyCFStringToUString(CFStringRef str, ustring &result, CFStringEncoding encoding, bool externalB)
{
	result.clear();
	
	if (str) {
		#define						kBufSize		256
		UTF8Char					utf8Buf[kBufSize];
		CFRange						cfRange = CFStrGetRange(str);
		CFIndex						resultSize;
		CFIndex						numChars;
		
		encoding = ValidateEncoding(encoding);
		
		while (cfRange.length > 0) {
			
			numChars = CFStringGetBytes(
										str, cfRange, encoding, '?', externalB, 
										&utf8Buf[0], kBufSize, &resultSize);
			
			if (numChars == 0) break;   // Failed to convert anything...
			
			result.append(&utf8Buf[0], &utf8Buf[resultSize]);
			
			cfRange.location	+= numChars;
			cfRange.length		-= numChars;
		}
	}
	
	return result;
}

std::string		&CopyCFStringToStd(
	CFStringRef			str, 
	std::string			&stdstr, 
	CFStringEncoding	encoding)
{
	stdstr.clear();
	
	encoding = ValidateEncoding(encoding);
	
	if (str) {
		const char	*charZ = CFStringGetCStringPtr(str, encoding);
		
		if (charZ) {
			stdstr = charZ;
		} else {
			ustring		ustr;
			
			CopyCFStringToUString(str, ustr, encoding);
			stdstr.append(ustr.begin(), ustr.end());
		}
	}
	
	return stdstr;
}

char		*OSTypeToChar(OSType osType, char *bufZ)
{
	osType = CFSwapInt32HostToBig(osType);
	*((OSType *)bufZ) = osType;
	bufZ[4] = 0;
	return bufZ;
}

SuperString		OSTypeToString(OSType osType)
{
	SuperString		str;
	
	if (osType == -1) {
		str.Set("-");
	} else {
		char	bufAC[5];
		
		str.Set(OSTypeToChar(osType, bufAC));
	}
	
	return str;
}

OSType		CharToOSType(const char *bufZ)
{
	OSType		osType;
	short		lenL = strlen(bufZ);
	
	osType = *((OSType *)(&bufZ[lenL - 4]));
	osType = CFSwapInt32BigToHost(osType);
	return osType;
}

//	you should provide this function.  stubbed out here because my CF doesn't have it
#define		CFStringTransform(a, b, c, d)	true

SuperString&		SuperString::Normalize()
{
	ScCFReleaser<CFMutableStringRef>	str1;

	str1.Set(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, ref()));
	
	CFStringNormalize(str1, kCFStringNormalizationFormD);
		
	CFRange		range = CFStrGetRange(str1);
	
	if (CFStringTransform(str1, &range, kCFStringTransformStripCombiningMarks, false)) {
		Set(str1);
	}

//	void CFStringFold(CFMutableStringRef theString, CFOptionFlags theFlags, CFLocaleRef theLocale) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER;

	return *this;
}

	
void		CFStrReplaceWith(CFMutableStringRef stringRef, CFStringRef replaceStr, CFStringRef withStr)
{
	ScCFReleaser<CFArrayRef>	arrayRef;
	
	arrayRef.Set(CFStringCreateArrayWithFindResults(
													NULL, stringRef, replaceStr, CFStrGetRange(stringRef), kCFCompareCaseInsensitive));
	
	if (arrayRef.Get()) {
		CFRange			*rangeRef;
		
		loop_reverse (CFArrayGetCount(arrayRef)) {
			rangeRef = (CFRange *)CFArrayGetValueAtIndex(arrayRef, _indexS);
			CFStringReplace(stringRef, *rangeRef, withStr);
		}
	}
}

#ifndef __WIN32__

	#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
		enum {
			kCFCompareDiacriticInsensitive = 128, /* If specified, ignores diacritics (o-umlaut == o) */
			kCFCompareWidthInsensitive = 256, /* If specified, ignores width differences ('a' == UFF41) */
			kCFCompareForcedOrdering = 512 /* If specified, comparisons are forced to return either kCFCompareLessThan or kCFCompareGreaterThan if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with kCFCompareCaseInsensitive specified) */
		};
	#endif

#endif

#ifdef KJAMS
	ushort		GetSystemVers(void);
	#define		diacritic_insensitiveB		(gApp && gApp->i_prefs && gApp->i_prefs->i_pref.diacritic_insensitive_searchB)
#else
	bool		g_pref_diacritic_insensitive_searchB	= true;
	#define		diacritic_insensitiveB					g_pref_diacritic_insensitive_searchB

	#ifdef _ROSE_
		unsigned short		GetSystemVers(void);
	#else
		static unsigned short		GetSystemVers(void) {
			return 0x1050;	//	fake it to be OS 10.5
		}
	#endif
#endif

static CFOptionFlags		GetFlags_NormalizeStrings(
	SuperString&	str1, 
	SuperString&	str2, 
	CFOptionFlags	optionFlags = 0
		| kCFCompareNonliteral 
		| kCFCompareLocalized)
{
	if (diacritic_insensitiveB) {
		static	bool	s_diacritic_compare_inittedB = false;
		static	bool	s_has_diacritic_insensitive_compareB;
		
		if (!s_diacritic_compare_inittedB) {
		
			#if defined(__WIN32__)
				SuperString		str_e("e");
				UInt32			e_grave(CFSwapInt32HostToBig(0xC3A90000));
				SuperString		str_e_grave((UInt8*)&e_grave);
				
				s_has_diacritic_insensitive_compareB = ::CFStringCompare(
					str_e.ref(), str_e_grave.ref(), (CFOptionFlags)kCFCompareDiacriticInsensitive)
					== kCFCompareEqualTo;
			#else
				s_has_diacritic_insensitive_compareB = GetSystemVers() >= 0x1050;
			#endif
			
			s_diacritic_compare_inittedB = true;
		}
		
		if (s_has_diacritic_insensitive_compareB) {
			optionFlags |= (CFOptionFlags)(0
				| kCFCompareDiacriticInsensitive
				| kCFCompareWidthInsensitive);
		} else {
			str1.Normalize();
			str2.Normalize();
		}
	}
	
	return optionFlags;
}

bool					CFStringContains(CFStringRef inRef, CFStringRef findRef, bool case_sensitiveB)
{
	if (inRef == NULL || findRef == NULL) {
		return false;
	}
	
	SuperString		str1(inRef), str2(findRef);
	CFOptionFlags	optionFlags = case_sensitiveB ? kCFCompareCaseInsensitive : 0;
	
	optionFlags = GetFlags_NormalizeStrings(str1, str2, optionFlags);
	
	return !!CFStringFindWithOptions(str1.ref(), str2.ref(), CFStrGetRange(str1.ref()), optionFlags, NULL);
}

CFComparisonResult		CFStringCompare(CFStringRef ref1, CFStringRef ref2, bool case_sensitiveB)
{
	CFComparisonResult		compareResult = kCFCompareEqualTo;
	
	if ((ref1 == NULL) || (ref2 == NULL)) {
		
		if ((ref1 == NULL) ^ (ref2 == NULL)) {
			if (ref1) {
				compareResult = kCFCompareLessThan;
			} else {
				compareResult = kCFCompareGreaterThan;
			}
		}
	} else {
		SuperString		str1(ref1), str2(ref2);
		CFOptionFlags	optionFlags = case_sensitiveB ? 0 : kCFCompareCaseInsensitive;
		
		optionFlags = GetFlags_NormalizeStrings(str1, str2, optionFlags);
		compareResult = ::CFStringCompare(str1.ref(), str2.ref(), optionFlags);
	}
	
	return compareResult;
}

bool		CFStringEqual(CFStringRef str1, CFStringRef str2, bool case_sensitiveB)
{
	return CFStringCompare(str1, str2, case_sensitiveB) == kCFCompareEqualTo;
}

bool		CFStringLess(CFStringRef lhs, CFStringRef rhs, bool case_sensitiveB)
{
	bool	lessB = CFStringCompare(lhs, rhs, case_sensitiveB) == kCFCompareLessThan;
	
	return lessB;
}

SuperString		operator+(const SuperString &lhs, SuperString rhs)
{
	SuperString		str(lhs);
	
	str.append(rhs);
	return str;
}

#ifndef KJAMS
static  void		mt_vsnprintf(char *destZ, size_t sizeL, const char *formatZ, va_list &args)
{
	vsnprintf(destZ, sizeL - 1, formatZ, args);
	destZ[sizeL - 1] = 0;
}
#endif

bool	SuperString::Split(const char *splitZ, SuperString *rhsP0, bool from_endB)
{
	bool			splitB;
	
	if (from_endB) {
		splitB = strrstr(utf8Z(), splitZ) != NULL;
	} else {
		splitB = strstr(utf8Z(), splitZ) != NULL;
	}
	
	if (splitB) {
		UCharVec	bufAC;
		
		bufAC.assign(utf8().begin(), utf8().end());
		bufAC.push_back(0);
		
		UTF8Char	*chZ;
		
		if (from_endB) {
			chZ	= (UTF8Char *)strrstr((char *)&bufAC[0], (char *)splitZ);
		} else {
			chZ	= (UTF8Char *)strstr((char *)&bufAC[0], (char *)splitZ);
		}
		
		SuperString		temp(chZ + strlen(splitZ));
		*chZ = 0;
		Set(&bufAC[0]);
		
		if (rhsP0) {
			rhsP0->Set(temp);
		}
	}
	
	return splitB;
}

SuperString&	SuperString::ssprintf(const char *formatZ0, ...)
{
	CharVec		buf(2048);
	va_list		args;
	
	CF_ASSERT(utf8().size() < 1024);
	va_start(args, formatZ0);
	mt_vsnprintf(&buf[0], buf.size(), formatZ0 ? formatZ0 : utf8Z(), args);
	va_end(args);
	
	Set(uc(&buf[0]));
	return *this;
}

SuperString	&		SuperString::pop_back(UInt32 numCharsL)
{
	ustring		ustr(utf8());
	
	if (!ustr.empty()) {
		ustr.resize(ustr.size() - numCharsL);
		Set(ustr.c_str());
	}
	
	return *this;
}

#ifdef __WIN32__

#if defined(__MWERKS__)
class CIsZero {
	public: bool operator()(const wchar_t& ch) {
		return ch == 0;
	}
};

static size_t	wcslen(const wchar_t *wcharZ)
{
	const wchar_t*		it(std::find_if(&wcharZ[0], &wcharZ[512], CIsZero()));
	
	return std::distance(&wcharZ[0], it);
}
#endif

SuperString::SuperString(const wchar_t *wcharZ)
{
	UTF16Vec	vec((UTF16Char *)&wcharZ[0], (UTF16Char *)&wcharZ[wcslen(wcharZ)]);

	SetNULL();
	Set(vec);
}
#endif

void	SuperString::Set_p(ConstStr255Param strZ, CFStringEncoding encoding)
{
	UCharVec		charVec;
	
	charVec.assign(&strZ[1], &strZ[strZ[0] + 1]);
	charVec.push_back(0);
	Set(&charVec[0], encoding);
}

void	SuperString::Set_CFType(CFTypeRef cfType)
{
	CFTypeID						cfTypeID(CFGetTypeID(cfType));
	
	if (cfTypeID == CFStringGetTypeID()) {
		Set((CFStringRef)cfType);
	} else {
		ScCFReleaser<CFStringRef>		descIDRef(CFCopyTypeIDDescription(cfTypeID));
		ScCFReleaser<CFStringRef>		descRef(CFCopyDescription(cfType));
		SuperString						descIDStr(descIDRef.Get()), descStr(descRef.Get());
		SuperString						logStr("Type: <%s>, Value: <%s>");
		
		logStr.ssprintf(NULL, descIDStr.utf8Z(), descStr.utf8Z());
		Set(logStr);
	}
}

#ifndef KJAMS
int		AssertAlert(const char *msgZ, const char *fileZ, long lineL, bool noThrowB)
{
	SuperString		formatStr("$$$ Assert Fail: %s, in file: '%s' at line %ld\n");
	
	formatStr.ssprintf(NULL, msgZ, fileZ, lineL);
	CCFLog()(formatStr.ref());
	return 1;
}
#endif

bool	Read_PList(const CFURLRef &url, CFDictionaryRef *plistP)
{
	bool						successB = false;
	ScCFReleaser<CFDataRef>     xmlData;
	
	*plistP = NULL;
	
	if (CFURLCreateDataAndPropertiesFromResource(
		kCFAllocatorDefault, url, xmlData.AddressOf(), NULL, NULL, NULL)
	) {
		//Log("created xml from file");
		
		*plistP = (CFDictionaryRef)CFPropertyListCreateFromXMLData(
			kCFAllocatorDefault,
			xmlData,
			kCFPropertyListImmutable,
			NULL);
		
		successB = *plistP != NULL;
		
		if (successB) {
			//	Log("created plist from xml");
		} else {
			CCFLog()("FAILED converting xml to plist\n");
		}
	} else {
		CCFLog()("FAILED creating xml from file\n");
	}
	
	return successB;
}

OSStatus		Write_PList(
	CFPropertyListRef	plist,
	CFURLRef			urlRef)
{
	OSStatus							err	= noErr;
	ScCFReleaser<CFDataRef>				xmlData;
	
	// Convert the property list into XML data.
	xmlData.Set(CFPropertyListCreateXMLData(kCFAllocatorDefault, plist));
	ETRL(xmlData.Get() == NULL, "creating xml data");
	
	if (!err) {
		(void)CFURLWriteDataAndPropertiesToResource (
			urlRef,		// URL to use
			xmlData,		// data to write
			NULL,   
			&err);
	}
	
	ETRL(err, "writing xml");
	
	return err;
}