#include <CoreFoundation/CFError.h>
#include <CoreFoundation/CFError_Private.h>
#include "CFInternal.h"
#include <CoreFoundation/CFPriv.h>
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
#include <mach/mach_error.h>
#endif
CONST_STRING_DECL(kCFErrorLocalizedDescriptionKey, "NSLocalizedDescription");
CONST_STRING_DECL(kCFErrorLocalizedFailureReasonKey, "NSLocalizedFailureReason");
CONST_STRING_DECL(kCFErrorLocalizedRecoverySuggestionKey, "NSLocalizedRecoverySuggestion");
CONST_STRING_DECL(kCFErrorDescriptionKey, "NSDescription");
CONST_STRING_DECL(kCFErrorDebugDescriptionKey, "NSDebugDescription");
CONST_STRING_DECL(kCFErrorUnderlyingErrorKey, "NSUnderlyingError");
CONST_STRING_DECL(kCFErrorURLKey, "NSURL");
CONST_STRING_DECL(kCFErrorFilePathKey, "NSFilePath");
CONST_STRING_DECL(kCFErrorDomainPOSIX, "NSPOSIXErrorDomain");
CONST_STRING_DECL(kCFErrorDomainOSStatus, "NSOSStatusErrorDomain");
CONST_STRING_DECL(kCFErrorDomainMach, "NSMachErrorDomain");
CONST_STRING_DECL(kCFErrorDomainCocoa, "NSCocoaErrorDomain");
static CFDictionaryRef _CFErrorGetUserInfo(CFErrorRef err);
static CFStringRef _CFErrorCopyUserInfoKey(CFErrorRef err, CFStringRef key);
static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator);
#define __CFAssertIsError(cf) __CFGenericValidateType(cf, __kCFErrorTypeID)
static CFSpinLock_t _CFErrorSpinlock = CFSpinLockInit;
struct __CFError { CFRuntimeBase _base;
CFIndex code;
CFStringRef domain; CFDictionaryRef userInfo; };
static Boolean __CFErrorEqual(CFTypeRef cf1, CFTypeRef cf2) {
CFErrorRef err1 = (CFErrorRef)cf1;
CFErrorRef err2 = (CFErrorRef)cf2;
if (CFErrorGetCode(err1) != CFErrorGetCode(err2)) return false;
if (!CFEqual(CFErrorGetDomain(err1), CFErrorGetDomain(err2))) return false;
CFDictionaryRef dict1 = CFErrorCopyUserInfo(err1);
CFDictionaryRef dict2 = CFErrorCopyUserInfo(err2);
Boolean result = false;
if (dict1 == dict2) {
result = true;
} else if (dict1 && dict2 && CFEqual(dict1, dict2)) {
result = true;
}
if (dict1) CFRelease(dict1);
if (dict2) CFRelease(dict2);
return result;
}
static CFHashCode __CFErrorHash(CFTypeRef cf) {
CFErrorRef err = (CFErrorRef)cf;
return CFHash(err->domain) + err->code;
}
static CFStringRef __CFErrorCopyDescription(CFTypeRef cf) {
return _CFErrorCreateDebugDescription((CFErrorRef)cf);
}
static CFStringRef __CFErrorCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
CFErrorRef err = (CFErrorRef)cf;
return CFErrorCopyDescription(err); }
static void __CFErrorDeallocate(CFTypeRef cf) {
CFErrorRef err = (CFErrorRef)cf;
CFRelease(err->domain);
CFRelease(err->userInfo);
}
static CFTypeID __kCFErrorTypeID = _kCFRuntimeNotATypeID;
static const CFRuntimeClass __CFErrorClass = {
0,
"CFError",
NULL, NULL, __CFErrorDeallocate,
__CFErrorEqual,
__CFErrorHash,
__CFErrorCopyFormattingDescription,
__CFErrorCopyDescription
};
__private_extern__ void __CFErrorInitialize(void) {
__kCFErrorTypeID = _CFRuntimeRegisterClass(&__CFErrorClass);
}
CFTypeID CFErrorGetTypeID(void) {
return __kCFErrorTypeID;
}
static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator) {
if (allocator == NULL) allocator = __CFGetDefaultAllocator();
if (_CFAllocatorIsSystemDefault(allocator)) {
static CFDictionaryRef emptyErrorDictionary = NULL;
if (emptyErrorDictionary == NULL) {
CFDictionaryRef tmp = CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
__CFSpinLock(&_CFErrorSpinlock);
if (emptyErrorDictionary == NULL) {
emptyErrorDictionary = tmp;
__CFSpinUnlock(&_CFErrorSpinlock);
} else {
__CFSpinUnlock(&_CFErrorSpinlock);
CFRelease(tmp);
}
}
return (CFDictionaryRef)CFRetain(emptyErrorDictionary);
} else {
return CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
}
static CFDictionaryRef _CFErrorGetUserInfo(CFErrorRef err) {
CF_OBJC_FUNCDISPATCHV(__kCFErrorTypeID, CFDictionaryRef, (NSError *)err, userInfo);
__CFAssertIsError(err);
return err->userInfo;
}
static CFStringRef _CFErrorCopyUserInfoKey(CFErrorRef err, CFStringRef key) {
CFStringRef result = NULL;
CFDictionaryRef userInfo = _CFErrorGetUserInfo(err);
if (userInfo) result = (CFStringRef)CFDictionaryGetValue(userInfo, key);
if (result) {
CFRetain(result);
} else {
CFErrorUserInfoKeyCallBack callBack = CFErrorGetCallBackForDomain(CFErrorGetDomain(err));
if (callBack) result = (CFStringRef)callBack(err, key);
}
return result;
}
CFStringRef _CFErrorCreateLocalizedDescription(CFErrorRef err) {
CFStringRef localizedDesc = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedDescriptionKey);
if (localizedDesc) return localizedDesc;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
CFBundleRef cfBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreFoundation"));
if (!cfBundle) { #endif
CFStringRef result = NULL, reasonOrDesc;
if ((reasonOrDesc = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey))) { result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("The operation couldn\\U2019t be completed. %@"), reasonOrDesc);
} else if ((reasonOrDesc = _CFErrorCopyUserInfoKey(err, kCFErrorDescriptionKey))) { result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("The operation couldn\\U2019t be completed. (%@ error %ld - %@)"), CFErrorGetDomain(err), (long)CFErrorGetCode(err), reasonOrDesc);
} else { result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("The operation couldn\\U2019t be completed. (%@ error %ld.)"), CFErrorGetDomain(err), (long)CFErrorGetCode(err));
}
if (reasonOrDesc) CFRelease(reasonOrDesc);
return result;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
}
#endif
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
CFStringRef reason = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey);
if (reason) {
CFStringRef operationFailedStr = CFCopyLocalizedStringFromTableInBundle(CFSTR("The operation couldn\\U2019t be completed. %@"), CFSTR("Error"), cfBundle, "A generic error string indicating there was a problem. The %@ will be replaced by a second sentence which indicates why the operation failed.");
CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, reason);
CFRelease(operationFailedStr);
CFRelease(reason);
return result;
}
CFStringRef result;
CFStringRef desc = _CFErrorCopyUserInfoKey(err, kCFErrorDescriptionKey);
CFStringRef localizedDomain = CFCopyLocalizedStringFromTableInBundle(CFErrorGetDomain(err), CFSTR("Error"), cfBundle, "These are localized in the comment above");
if (desc) { CFStringRef operationFailedStr = CFCopyLocalizedStringFromTableInBundle(CFSTR("The operation couldn\\U2019t be completed. (%@ error %ld - %@)"), CFSTR("Error"), cfBundle, "A generic error string indicating there was a problem, followed by a parenthetical sentence which indicates error domain, code, and a description when there is no other way to present an error to the user. The first %@ indicates the error domain, %ld indicates the error code, and the second %@ indicates the description; so this might become '(Mach error 42 - Server error.)' for instance.");
result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, localizedDomain, (long)CFErrorGetCode(err), desc);
CFRelease(operationFailedStr);
CFRelease(desc);
} else { CFStringRef operationFailedStr = CFCopyLocalizedStringFromTableInBundle(CFSTR("The operation couldn\\U2019t be completed. (%@ error %ld.)"), CFSTR("Error"), cfBundle, "A generic error string indicating there was a problem, followed by a parenthetical sentence which indicates error domain and code when there is no other way to present an error to the user. The %@ indicates the error domain while %ld indicates the error code; so this might become '(Mach error 42.)' for instance.");
result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, localizedDomain, (long)CFErrorGetCode(err));
CFRelease(operationFailedStr);
}
CFRelease(localizedDomain);
return result;
#endif
}
CFStringRef _CFErrorCreateLocalizedFailureReason(CFErrorRef err) {
return _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey);
}
CFStringRef _CFErrorCreateLocalizedRecoverySuggestion(CFErrorRef err) {
return _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedRecoverySuggestionKey);
}
static void userInfoKeyValueShow(const void *key, const void *value, void *context) {
CFStringRef desc;
if (CFEqual(key, kCFErrorUnderlyingErrorKey) && (desc = CFErrorCopyDescription((CFErrorRef)value))) { CFStringAppendFormat((CFMutableStringRef)context, NULL, CFSTR("%@=%p \"%@\", "), key, value, desc);
CFRelease(desc);
} else {
CFStringAppendFormat((CFMutableStringRef)context, NULL, CFSTR("%@=%@, "), key, value);
}
}
CFStringRef _CFErrorCreateDebugDescription(CFErrorRef err) {
CFStringRef desc = CFErrorCopyDescription(err);
CFStringRef debugDesc = _CFErrorCopyUserInfoKey(err, kCFErrorDebugDescriptionKey);
CFDictionaryRef userInfo = _CFErrorGetUserInfo(err);
CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
CFStringAppendFormat(result, NULL, CFSTR("Error Domain=%@ Code=%d"), CFErrorGetDomain(err), (long)CFErrorGetCode(err));
CFStringAppendFormat(result, NULL, CFSTR(" \"%@\""), desc);
if (debugDesc && CFStringGetLength(debugDesc) > 0) CFStringAppendFormat(result, NULL, CFSTR(" (%@)"), debugDesc);
if (userInfo && CFDictionaryGetCount(userInfo)) {
CFStringAppendFormat(result, NULL, CFSTR(" UserInfo=%p {"), userInfo);
CFDictionaryApplyFunction(userInfo, userInfoKeyValueShow, (void *)result);
CFIndex commaLength = (CFStringHasSuffix(result, CFSTR(", "))) ? 2 : 0;
CFStringReplace(result, CFRangeMake(CFStringGetLength(result)-commaLength, commaLength), CFSTR("}"));
}
if (debugDesc) CFRelease(debugDesc);
if (desc) CFRelease(desc);
return result;
}
CFErrorRef CFErrorCreate(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, CFDictionaryRef userInfo) {
__CFGenericValidateType(domain, CFStringGetTypeID());
if (userInfo) __CFGenericValidateType(userInfo, CFDictionaryGetTypeID());
CFErrorRef err = (CFErrorRef)_CFRuntimeCreateInstance(allocator, __kCFErrorTypeID, sizeof(struct __CFError) - sizeof(CFRuntimeBase), NULL);
if (NULL == err) return NULL;
err->domain = CFStringCreateCopy(allocator, domain);
err->code = code;
err->userInfo = userInfo ? CFDictionaryCreateCopy(allocator, userInfo) : _CFErrorCreateEmptyDictionary(allocator);
return err;
}
CFErrorRef CFErrorCreateWithUserInfoKeysAndValues(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, const void *const *userInfoKeys, const void *const *userInfoValues, CFIndex numUserInfoValues) {
__CFGenericValidateType(domain, CFStringGetTypeID());
CFErrorRef err = (CFErrorRef)_CFRuntimeCreateInstance(allocator, __kCFErrorTypeID, sizeof(struct __CFError) - sizeof(CFRuntimeBase), NULL);
if (NULL == err) return NULL;
err->domain = CFStringCreateCopy(allocator, domain);
err->code = code;
err->userInfo = CFDictionaryCreate(allocator, (const void **)userInfoKeys, (const void **)userInfoValues, numUserInfoValues, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
return err;
}
CFStringRef CFErrorGetDomain(CFErrorRef err) {
CF_OBJC_FUNCDISPATCHV(__kCFErrorTypeID, CFStringRef, (NSError *)err, domain);
__CFAssertIsError(err);
return err->domain;
}
CFIndex CFErrorGetCode(CFErrorRef err) {
CF_OBJC_FUNCDISPATCHV(__kCFErrorTypeID, CFIndex, (NSError *)err, code);
__CFAssertIsError(err);
return err->code;
}
CFDictionaryRef CFErrorCopyUserInfo(CFErrorRef err) {
CFDictionaryRef userInfo = _CFErrorGetUserInfo(err);
return userInfo ? (CFDictionaryRef)CFRetain(userInfo) : _CFErrorCreateEmptyDictionary(CFGetAllocator(err));
}
CFStringRef CFErrorCopyDescription(CFErrorRef err) {
if (CF_IS_OBJC(__kCFErrorTypeID, err)) { CFStringRef desc = (CFStringRef) CF_OBJC_CALLV((NSError *)err, localizedDescription);
return desc ? (CFStringRef)CFRetain(desc) : NULL; }
__CFAssertIsError(err);
return _CFErrorCreateLocalizedDescription(err);
}
CFStringRef CFErrorCopyFailureReason(CFErrorRef err) {
if (CF_IS_OBJC(__kCFErrorTypeID, err)) { CFStringRef str = (CFStringRef) CF_OBJC_CALLV((NSError *)err, localizedFailureReason);
return str ? (CFStringRef)CFRetain(str) : NULL; }
__CFAssertIsError(err);
return _CFErrorCreateLocalizedFailureReason(err);
}
CFStringRef CFErrorCopyRecoverySuggestion(CFErrorRef err) {
if (CF_IS_OBJC(__kCFErrorTypeID, err)) { CFStringRef str = (CFStringRef) CF_OBJC_CALLV((NSError *)err, localizedRecoverySuggestion);
return str ? (CFStringRef)CFRetain(str) : NULL; }
__CFAssertIsError(err);
return _CFErrorCreateLocalizedRecoverySuggestion(err);
}
static CFMutableDictionaryRef _CFErrorCallBackTable = NULL;
static CFTypeRef _CFErrorPOSIXCallBack(CFErrorRef err, CFStringRef key) {
if (!CFEqual(key, kCFErrorDescriptionKey) && !CFEqual(key, kCFErrorLocalizedFailureReasonKey)) return NULL;
const char *errCStr = strerror(CFErrorGetCode(err));
CFStringRef errStr = (errCStr && strlen(errCStr)) ? CFStringCreateWithCString(kCFAllocatorSystemDefault, errCStr, kCFStringEncodingUTF8) : NULL;
if (!errStr) return NULL;
if (CFEqual(key, kCFErrorDescriptionKey)) return errStr;
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
CFArrayRef paths = CFCopySearchPathForDirectoriesInDomains(kCFLibraryDirectory, kCFSystemDomainMask, false);
if (paths) {
if (CFArrayGetCount(paths) > 0) {
CFStringRef fileSystemPath = CFURLCopyFileSystemPath((CFURLRef)CFArrayGetValueAtIndex(paths, 0), kCFURLPOSIXPathStyle);
if (fileSystemPath) {
CFStringRef path = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/CoreServices/CoreTypes.bundle"), fileSystemPath);
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false );
CFRelease(fileSystemPath);
if (url) {
CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
if (bundle) {
CFStringRef localizedErrStr = CFBundleCopyLocalizedString(bundle, errStr, errStr, CFSTR("ErrnoErrors"));
if (localizedErrStr == errStr) {
CFRelease(localizedErrStr);
CFRelease(errStr);
errStr = NULL;
} else {
CFRelease(errStr);
errStr = localizedErrStr;
}
CFRelease(bundle);
}
CFRelease(url);
}
CFRelease(path);
}
}
CFRelease(paths);
}
#endif
return errStr;
}
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
static CFTypeRef _CFErrorMachCallBack(CFErrorRef err, CFStringRef key) {
if (CFEqual(key, kCFErrorDescriptionKey)) {
const char *errStr = mach_error_string(CFErrorGetCode(err));
if (errStr && strlen(errStr)) return CFStringCreateWithCString(kCFAllocatorSystemDefault, errStr, kCFStringEncodingUTF8);
}
return NULL;
}
#endif
static void _CFErrorInitializeCallBackTable(void) {
CFMutableDictionaryRef table = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL);
__CFSpinLock(&_CFErrorSpinlock);
if (!_CFErrorCallBackTable) {
_CFErrorCallBackTable = table;
__CFSpinUnlock(&_CFErrorSpinlock);
} else {
__CFSpinUnlock(&_CFErrorSpinlock);
CFRelease(table);
}
CFErrorSetCallBackForDomain(kCFErrorDomainPOSIX, _CFErrorPOSIXCallBack);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
CFErrorSetCallBackForDomain(kCFErrorDomainMach, _CFErrorMachCallBack);
#endif
}
void CFErrorSetCallBackForDomain(CFStringRef domainName, CFErrorUserInfoKeyCallBack callBack) {
if (!_CFErrorCallBackTable) _CFErrorInitializeCallBackTable();
__CFSpinLock(&_CFErrorSpinlock);
if (callBack) {
CFDictionarySetValue(_CFErrorCallBackTable, domainName, (void *)callBack);
} else {
CFDictionaryRemoveValue(_CFErrorCallBackTable, domainName);
}
__CFSpinUnlock(&_CFErrorSpinlock);
}
CFErrorUserInfoKeyCallBack CFErrorGetCallBackForDomain(CFStringRef domainName) {
if (!_CFErrorCallBackTable) _CFErrorInitializeCallBackTable();
__CFSpinLock(&_CFErrorSpinlock);
CFErrorUserInfoKeyCallBack callBack = (CFErrorUserInfoKeyCallBack)CFDictionaryGetValue(_CFErrorCallBackTable, domainName);
__CFSpinUnlock(&_CFErrorSpinlock);
return callBack;
}