]> git.saurik.com Git - apple/cf.git/blame - CFError.c
CF-635.15.tar.gz
[apple/cf.git] / CFError.c
CommitLineData
bd5b749c 1/*
8ca704e1 2 * Copyright (c) 2011 Apple Inc. All rights reserved.
bd5b749c
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
f64f9b69 23
bd5b749c 24/* CFError.c
8ca704e1 25 Copyright (c) 2006-2011, Apple Inc. All rights reserved.
bd5b749c
A
26 Responsibility: Ali Ozer
27*/
28
29#include <CoreFoundation/CFError.h>
30#include <CoreFoundation/CFError_Private.h>
31#include "CFInternal.h"
cf7d2af9
A
32#include <CoreFoundation/CFPriv.h>
33#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
bd5b749c
A
34#include <mach/mach_error.h>
35#endif
36
37/* Pre-defined userInfo keys
38*/
39CONST_STRING_DECL(kCFErrorLocalizedDescriptionKey, "NSLocalizedDescription");
40CONST_STRING_DECL(kCFErrorLocalizedFailureReasonKey, "NSLocalizedFailureReason");
41CONST_STRING_DECL(kCFErrorLocalizedRecoverySuggestionKey, "NSLocalizedRecoverySuggestion");
42CONST_STRING_DECL(kCFErrorDescriptionKey, "NSDescription");
43CONST_STRING_DECL(kCFErrorDebugDescriptionKey, "NSDebugDescription");
44CONST_STRING_DECL(kCFErrorUnderlyingErrorKey, "NSUnderlyingError");
8ca704e1
A
45CONST_STRING_DECL(kCFErrorURLKey, "NSURL");
46CONST_STRING_DECL(kCFErrorFilePathKey, "NSFilePath");
bd5b749c
A
47
48/* Pre-defined error domains
49*/
50CONST_STRING_DECL(kCFErrorDomainPOSIX, "NSPOSIXErrorDomain");
51CONST_STRING_DECL(kCFErrorDomainOSStatus, "NSOSStatusErrorDomain");
52CONST_STRING_DECL(kCFErrorDomainMach, "NSMachErrorDomain");
53CONST_STRING_DECL(kCFErrorDomainCocoa, "NSCocoaErrorDomain");
54
55/* We put the localized names of domain names here so genstrings can pick them out. Any additional domains that are added should be listed here if we'd like them localized.
56
57CFCopyLocalizedStringWithDefaultValue(CFSTR("NSMachErrorDomain"), CFSTR("Error"), NULL, CFSTR("Mach"), "Name of the 'Mach' error domain when showing to user. This probably will not get localized, unless there is a generally recognized phrase for 'Mach' in the language.")
58CFCopyLocalizedStringWithDefaultValue(CFSTR("NSCoreFoundationErrorDomain"), CFSTR("Error"), NULL, CFSTR("Core Foundation"), "Name of the 'Core Foundation' error domain when showing to user. Very likely this will not get localized differently in other languages.")
59CFCopyLocalizedStringWithDefaultValue(CFSTR("NSPOSIXErrorDomain"), CFSTR("Error"), NULL, CFSTR("POSIX"), "Name of the 'POSIX' error domain when showing to user. This probably will not get localized, unless there is a generally recognized phrase for 'POSIX' in the language.")
60CFCopyLocalizedStringWithDefaultValue(CFSTR("NSOSStatusErrorDomain"), CFSTR("Error"), NULL, CFSTR("OSStatus"), "Name of the 'OSStatus' error domain when showing to user. Very likely this will not get localized.")
61CFCopyLocalizedStringWithDefaultValue(CFSTR("NSCocoaErrorDomain"), CFSTR("Error"), NULL, CFSTR("Cocoa"), "Name of the 'Cocoa' error domain when showing to user. Very likely this will not get localized.")
62*/
63
64
65
66/* Forward declarations
67*/
68static CFDictionaryRef _CFErrorGetUserInfo(CFErrorRef err);
69static CFStringRef _CFErrorCopyUserInfoKey(CFErrorRef err, CFStringRef key);
70static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator);
71
72/* Assertions and other macros/inlines
73*/
74#define __CFAssertIsError(cf) __CFGenericValidateType(cf, __kCFErrorTypeID)
75
76/* This lock is used in the few places in CFError where we create and access shared static objects. Should only be around tiny snippets of code; no recursion
77*/
78static CFSpinLock_t _CFErrorSpinlock = CFSpinLockInit;
79
80
81
82
83/**** CFError CF runtime stuff ****/
84
85struct __CFError { // Potentially interesting to keep layout same as NSError (but currently not a requirement)
86 CFRuntimeBase _base;
87 CFIndex code;
88 CFStringRef domain; // !!! Could compress well-known domains down to few bits, but probably not worth its weight in code since CFErrors are rare
89 CFDictionaryRef userInfo; // !!! Could avoid allocating this slot if userInfo is NULL, but probably not worth its weight in code since CFErrors are rare
90};
91
92/* CFError equal checks for equality of domain, code, and userInfo.
93*/
94static Boolean __CFErrorEqual(CFTypeRef cf1, CFTypeRef cf2) {
95 CFErrorRef err1 = (CFErrorRef)cf1;
96 CFErrorRef err2 = (CFErrorRef)cf2;
97
98 // First do quick checks of code and domain (in that order for performance)
99 if (CFErrorGetCode(err1) != CFErrorGetCode(err2)) return false;
100 if (!CFEqual(CFErrorGetDomain(err1), CFErrorGetDomain(err2))) return false;
101
102 // If those are equal, then check the dictionaries
103 CFDictionaryRef dict1 = CFErrorCopyUserInfo(err1);
104 CFDictionaryRef dict2 = CFErrorCopyUserInfo(err2);
105
106 Boolean result = false;
107
108 if (dict1 == dict2) {
109 result = true;
110 } else if (dict1 && dict2 && CFEqual(dict1, dict2)) {
111 result = true;
112 }
113
114 if (dict1) CFRelease(dict1);
115 if (dict2) CFRelease(dict2);
116
117 return result;
118}
119
120/* CFError hash code is hash(domain) + code
121*/
122static CFHashCode __CFErrorHash(CFTypeRef cf) {
123 CFErrorRef err = (CFErrorRef)cf;
124 /* !!! We do not need an assertion here, as this is called by the CFBase runtime only */
125 return CFHash(err->domain) + err->code;
126}
127
128/* This is the full debug description. Shows the description (possibly localized), plus the domain, code, and userInfo explicitly. If there is a debug description, shows that as well.
129*/
130static CFStringRef __CFErrorCopyDescription(CFTypeRef cf) {
131 return _CFErrorCreateDebugDescription((CFErrorRef)cf);
132}
133
134/* This is the description you get for %@; we tone it down a bit from what you get in __CFErrorCopyDescription().
135*/
136static CFStringRef __CFErrorCopyFormattingDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
137 CFErrorRef err = (CFErrorRef)cf;
138 return CFErrorCopyDescription(err); // No need to release, since we are returning from a Copy function
139}
140
141static void __CFErrorDeallocate(CFTypeRef cf) {
142 CFErrorRef err = (CFErrorRef)cf;
143 CFRelease(err->domain);
144 CFRelease(err->userInfo);
145}
146
147
148static CFTypeID __kCFErrorTypeID = _kCFRuntimeNotATypeID;
149
150static const CFRuntimeClass __CFErrorClass = {
151 0,
152 "CFError",
153 NULL, // init
154 NULL, // copy
155 __CFErrorDeallocate,
156 __CFErrorEqual,
157 __CFErrorHash,
158 __CFErrorCopyFormattingDescription,
159 __CFErrorCopyDescription
160};
161
162__private_extern__ void __CFErrorInitialize(void) {
163 __kCFErrorTypeID = _CFRuntimeRegisterClass(&__CFErrorClass);
164}
165
166CFTypeID CFErrorGetTypeID(void) {
167 return __kCFErrorTypeID;
168}
169
170
171
172
173/**** CFError support functions ****/
174
175/* Returns a shared empty dictionary (unless the allocator is not kCFAllocatorSystemDefault, in which case returns a newly allocated one).
176*/
177static CFDictionaryRef _CFErrorCreateEmptyDictionary(CFAllocatorRef allocator) {
178 if (allocator == NULL) allocator = __CFGetDefaultAllocator();
8ca704e1 179 if (_CFAllocatorIsSystemDefault(allocator)) {
bd5b749c
A
180 static CFDictionaryRef emptyErrorDictionary = NULL;
181 if (emptyErrorDictionary == NULL) {
182 CFDictionaryRef tmp = CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
183 __CFSpinLock(&_CFErrorSpinlock);
184 if (emptyErrorDictionary == NULL) {
185 emptyErrorDictionary = tmp;
186 __CFSpinUnlock(&_CFErrorSpinlock);
187 } else {
188 __CFSpinUnlock(&_CFErrorSpinlock);
189 CFRelease(tmp);
190 }
191 }
192 return (CFDictionaryRef)CFRetain(emptyErrorDictionary);
193 } else {
194 return CFDictionaryCreate(allocator, NULL, NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
195 }
196}
197
198/* A non-retained accessor for the userInfo. Might return NULL in some cases, if the subclass of NSError returned nil for some reason. It works with a CF or NSError.
199*/
200static CFDictionaryRef _CFErrorGetUserInfo(CFErrorRef err) {
201 CF_OBJC_FUNCDISPATCH0(__kCFErrorTypeID, CFDictionaryRef, err, "userInfo");
202 __CFAssertIsError(err);
203 return err->userInfo;
204}
205
206/* This function retrieves the value of the specified key from the userInfo, or from the callback. It works with a CF or NSError.
207*/
208static CFStringRef _CFErrorCopyUserInfoKey(CFErrorRef err, CFStringRef key) {
209 CFStringRef result = NULL;
210 // First consult the userInfo dictionary
211 CFDictionaryRef userInfo = _CFErrorGetUserInfo(err);
212 if (userInfo) result = (CFStringRef)CFDictionaryGetValue(userInfo, key);
213 // If that doesn't work, consult the callback
214 if (result) {
215 CFRetain(result);
216 } else {
217 CFErrorUserInfoKeyCallBack callBack = CFErrorGetCallBackForDomain(CFErrorGetDomain(err));
218 if (callBack) result = (CFStringRef)callBack(err, key);
219 }
220 return result;
221}
222
223/* The real guts of the description creation functionality. See the header file for the steps this function goes through to compute the description. This function can take a CF or NSError. It's called by NSError for the localizedDescription computation.
224*/
225CFStringRef _CFErrorCreateLocalizedDescription(CFErrorRef err) {
226 // First look for kCFErrorLocalizedDescriptionKey; if non-NULL, return that as-is.
227 CFStringRef localizedDesc = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedDescriptionKey);
228 if (localizedDesc) return localizedDesc;
229
8ca704e1
A
230
231#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
cf7d2af9 232 // Cache the CF bundle since we will be using it for localized strings.
bd5b749c 233 CFBundleRef cfBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreFoundation"));
8ca704e1 234
cf7d2af9 235 if (!cfBundle) { // This should be rare, but has been observed in the wild, due to running out of file descriptors. Normally we might not go to such extremes, but since we want to be able to present reasonable errors even in the case of errors such as running out of file descriptors, why not. This is CFError after all. !!! Be sure to have the same logic here as below for going through various options for fetching the strings.
8ca704e1 236#endif
cf7d2af9
A
237
238 CFStringRef result = NULL, reasonOrDesc;
239
240 if ((reasonOrDesc = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey))) { // First look for kCFErrorLocalizedFailureReasonKey
241 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("The operation couldn\\U2019t be completed. %@"), reasonOrDesc);
242 } else if ((reasonOrDesc = _CFErrorCopyUserInfoKey(err, kCFErrorDescriptionKey))) { // Then try kCFErrorDescriptionKey
243 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("The operation couldn\\U2019t be completed. (%@ error %ld - %@)"), CFErrorGetDomain(err), (long)CFErrorGetCode(err), reasonOrDesc);
244 } else { // Last resort, just the domain and code
245 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("The operation couldn\\U2019t be completed. (%@ error %ld.)"), CFErrorGetDomain(err), (long)CFErrorGetCode(err));
246 }
247 if (reasonOrDesc) CFRelease(reasonOrDesc);
248 return result;
8ca704e1 249#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
cf7d2af9 250 }
8ca704e1 251#endif
cf7d2af9 252
8ca704e1 253#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
bd5b749c
A
254 // Then look for kCFErrorLocalizedFailureReasonKey; if there, create a full sentence from that.
255 CFStringRef reason = _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey);
256 if (reason) {
cf7d2af9 257 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.");
bd5b749c
A
258 CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, reason);
259 CFRelease(operationFailedStr);
260 CFRelease(reason);
261 return result;
262 }
263
264 // Otherwise, generate a semi-user presentable string from the domain, code, and if available, the presumably non-localized kCFErrorDescriptionKey.
265 CFStringRef result;
266 CFStringRef desc = _CFErrorCopyUserInfoKey(err, kCFErrorDescriptionKey);
267 CFStringRef localizedDomain = CFCopyLocalizedStringFromTableInBundle(CFErrorGetDomain(err), CFSTR("Error"), cfBundle, "These are localized in the comment above");
268 if (desc) { // We have kCFErrorDescriptionKey, so include that with the error domain and code
cf7d2af9 269 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.");
bd5b749c
A
270 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, localizedDomain, (long)CFErrorGetCode(err), desc);
271 CFRelease(operationFailedStr);
272 CFRelease(desc);
273 } else { // We don't have kCFErrorDescriptionKey, so just use error domain and code
cf7d2af9 274 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.");
bd5b749c
A
275 result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, operationFailedStr, localizedDomain, (long)CFErrorGetCode(err));
276 CFRelease(operationFailedStr);
277 }
278 CFRelease(localizedDomain);
279 return result;
8ca704e1 280#endif
bd5b749c
A
281}
282
283/* The real guts of the failure reason creation functionality. This function can take a CF or NSError. It's called by NSError for the localizedFailureReason computation.
284*/
285CFStringRef _CFErrorCreateLocalizedFailureReason(CFErrorRef err) {
286 // We simply return the value of kCFErrorLocalizedFailureReasonKey; no other searching takes place
287 return _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedFailureReasonKey);
288}
289
290/* The real guts of the recovery suggestion functionality. This function can take a CF or NSError. It's called by NSError for the localizedRecoverySuggestion computation.
291*/
292CFStringRef _CFErrorCreateLocalizedRecoverySuggestion(CFErrorRef err) {
293 // We simply return the value of kCFErrorLocalizedRecoverySuggestionKey; no other searching takes place
294 return _CFErrorCopyUserInfoKey(err, kCFErrorLocalizedRecoverySuggestionKey);
295}
296
297/* The "debug" description, used by CFCopyDescription and -[NSObject description].
298*/
8ca704e1
A
299static void userInfoKeyValueShow(const void *key, const void *value, void *context) {
300 CFStringRef desc;
301 if (CFEqual(key, kCFErrorUnderlyingErrorKey) && (desc = CFErrorCopyDescription((CFErrorRef)value))) { // We check desc, see <rdar://problem/8415727>
302 CFStringAppendFormat((CFMutableStringRef)context, NULL, CFSTR("%@=%p \"%@\", "), key, value, desc);
303 CFRelease(desc);
304 } else {
305 CFStringAppendFormat((CFMutableStringRef)context, NULL, CFSTR("%@=%@, "), key, value);
306 }
307}
308
bd5b749c
A
309CFStringRef _CFErrorCreateDebugDescription(CFErrorRef err) {
310 CFStringRef desc = CFErrorCopyDescription(err);
311 CFStringRef debugDesc = _CFErrorCopyUserInfoKey(err, kCFErrorDebugDescriptionKey);
312 CFDictionaryRef userInfo = _CFErrorGetUserInfo(err);
313 CFMutableStringRef result = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
314 CFStringAppendFormat(result, NULL, CFSTR("Error Domain=%@ Code=%d"), CFErrorGetDomain(err), (long)CFErrorGetCode(err));
bd5b749c
A
315 CFStringAppendFormat(result, NULL, CFSTR(" \"%@\""), desc);
316 if (debugDesc && CFStringGetLength(debugDesc) > 0) CFStringAppendFormat(result, NULL, CFSTR(" (%@)"), debugDesc);
8ca704e1
A
317 if (userInfo) {
318 CFStringAppendFormat(result, NULL, CFSTR(" UserInfo=%p {"), userInfo);
319 CFDictionaryApplyFunction(userInfo, userInfoKeyValueShow, (void *)result);
320 CFIndex commaLength = (CFStringHasSuffix(result, CFSTR(", "))) ? 2 : 0;
321 CFStringReplace(result, CFRangeMake(CFStringGetLength(result)-commaLength, commaLength), CFSTR("}"));
cf7d2af9 322 }
bd5b749c
A
323 if (debugDesc) CFRelease(debugDesc);
324 if (desc) CFRelease(desc);
325 return result;
326}
327
328
329
330
331/**** CFError API/SPI ****/
332
333/* Note that there are two entry points for creating CFErrors. This one does it with a presupplied userInfo dictionary.
334*/
335CFErrorRef CFErrorCreate(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, CFDictionaryRef userInfo) {
336 __CFGenericValidateType(domain, CFStringGetTypeID());
337 if (userInfo) __CFGenericValidateType(userInfo, CFDictionaryGetTypeID());
338
339 CFErrorRef err = (CFErrorRef)_CFRuntimeCreateInstance(allocator, __kCFErrorTypeID, sizeof(struct __CFError) - sizeof(CFRuntimeBase), NULL);
340 if (NULL == err) return NULL;
341
342 err->domain = CFStringCreateCopy(allocator, domain);
343 err->code = code;
344 err->userInfo = userInfo ? CFDictionaryCreateCopy(allocator, userInfo) : _CFErrorCreateEmptyDictionary(allocator);
345
346 return err;
347}
348
349/* Note that there are two entry points for creating CFErrors. This one does it with individual keys and values which are used to create the userInfo dictionary.
350*/
351CFErrorRef CFErrorCreateWithUserInfoKeysAndValues(CFAllocatorRef allocator, CFStringRef domain, CFIndex code, const void *const *userInfoKeys, const void *const *userInfoValues, CFIndex numUserInfoValues) {
352 __CFGenericValidateType(domain, CFStringGetTypeID());
353
354 CFErrorRef err = (CFErrorRef)_CFRuntimeCreateInstance(allocator, __kCFErrorTypeID, sizeof(struct __CFError) - sizeof(CFRuntimeBase), NULL);
355 if (NULL == err) return NULL;
356
357 err->domain = CFStringCreateCopy(allocator, domain);
358 err->code = code;
359 err->userInfo = CFDictionaryCreate(allocator, (const void **)userInfoKeys, (const void **)userInfoValues, numUserInfoValues, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
360
361 return err;
362}
363
364CFStringRef CFErrorGetDomain(CFErrorRef err) {
365 CF_OBJC_FUNCDISPATCH0(__kCFErrorTypeID, CFStringRef, err, "domain");
366 __CFAssertIsError(err);
367 return err->domain;
368}
369
370CFIndex CFErrorGetCode(CFErrorRef err) {
371 CF_OBJC_FUNCDISPATCH0(__kCFErrorTypeID, CFIndex, err, "code");
372 __CFAssertIsError(err);
373 return err->code;
374}
375
376/* This accessor never returns NULL. For usage inside this file, consider __CFErrorGetUserInfo().
377*/
378CFDictionaryRef CFErrorCopyUserInfo(CFErrorRef err) {
379 CFDictionaryRef userInfo = _CFErrorGetUserInfo(err);
380 return userInfo ? (CFDictionaryRef)CFRetain(userInfo) : _CFErrorCreateEmptyDictionary(CFGetAllocator(err));
381}
382
383CFStringRef CFErrorCopyDescription(CFErrorRef err) {
384 if (CF_IS_OBJC(__kCFErrorTypeID, err)) { // Since we have to return a retained result, we need to treat the toll-free bridging specially
385 CFStringRef desc;
386 CF_OBJC_CALL0(CFStringRef, desc, err, "localizedDescription");
387 return desc ? (CFStringRef)CFRetain(desc) : NULL; // !!! It really should never return nil.
388 }
389 __CFAssertIsError(err);
390 return _CFErrorCreateLocalizedDescription(err);
391}
392
393CFStringRef CFErrorCopyFailureReason(CFErrorRef err) {
394 if (CF_IS_OBJC(__kCFErrorTypeID, err)) { // Since we have to return a retained result, we need to treat the toll-free bridging specially
395 CFStringRef str;
396 CF_OBJC_CALL0(CFStringRef, str, err, "localizedFailureReason");
397 return str ? (CFStringRef)CFRetain(str) : NULL; // It's possible for localizedFailureReason to return nil
398 }
399 __CFAssertIsError(err);
400 return _CFErrorCreateLocalizedFailureReason(err);
401}
402
403CFStringRef CFErrorCopyRecoverySuggestion(CFErrorRef err) {
404 if (CF_IS_OBJC(__kCFErrorTypeID, err)) { // Since we have to return a retained result, we need to treat the toll-free bridging specially
405 CFStringRef str;
406 CF_OBJC_CALL0(CFStringRef, str, err, "localizedRecoverySuggestion");
407 return str ? (CFStringRef)CFRetain(str) : NULL; // It's possible for localizedRecoverySuggestion to return nil
408 }
409 __CFAssertIsError(err);
410 return _CFErrorCreateLocalizedRecoverySuggestion(err);
411}
412
413
414/**** CFError CallBack management ****/
415
416/* Domain-to-callback mapping dictionary
417*/
418static CFMutableDictionaryRef _CFErrorCallBackTable = NULL;
419
420
421/* Built-in callback for POSIX domain. Note that we will pick up localizations from ErrnoErrors.strings in /System/Library/CoreServices/CoreTypes.bundle, if the file happens to be there.
422*/
423static CFTypeRef _CFErrorPOSIXCallBack(CFErrorRef err, CFStringRef key) {
424 if (!CFEqual(key, kCFErrorDescriptionKey) && !CFEqual(key, kCFErrorLocalizedFailureReasonKey)) return NULL;
425
426 const char *errCStr = strerror(CFErrorGetCode(err));
427 CFStringRef errStr = (errCStr && strlen(errCStr)) ? CFStringCreateWithCString(kCFAllocatorSystemDefault, errCStr, kCFStringEncodingUTF8) : NULL;
428
429 if (!errStr) return NULL;
430 if (CFEqual(key, kCFErrorDescriptionKey)) return errStr; // If all we wanted was the non-localized description, we're done
431
8ca704e1 432#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
bd5b749c
A
433 // We need a kCFErrorLocalizedFailureReasonKey, so look up a possible localization for the error message
434 // Look for the bundle in /System/Library/CoreServices/CoreTypes.bundle
435 CFArrayRef paths = CFCopySearchPathForDirectoriesInDomains(kCFLibraryDirectory, kCFSystemDomainMask, false);
436 if (paths) {
437 if (CFArrayGetCount(paths) > 0) {
438 CFStringRef path = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/CoreServices/CoreTypes.bundle"), CFArrayGetValueAtIndex(paths, 0));
439 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, path, kCFURLPOSIXPathStyle, false /* not a directory */);
440 if (url) {
441 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
442 if (bundle) {
443 // We only want to return a result if there was a localization
444 CFStringRef localizedErrStr = CFBundleCopyLocalizedString(bundle, errStr, errStr, CFSTR("ErrnoErrors"));
445 if (localizedErrStr == errStr) {
446 CFRelease(localizedErrStr);
447 CFRelease(errStr);
448 errStr = NULL;
449 } else {
450 CFRelease(errStr);
451 errStr = localizedErrStr;
452 }
453 CFRelease(bundle);
454 }
455 CFRelease(url);
456 }
457 CFRelease(path);
458 }
459 CFRelease(paths);
460 }
8ca704e1 461#endif
bd5b749c
A
462
463 return errStr;
464}
465
cf7d2af9 466#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
bd5b749c
A
467/* Built-in callback for Mach domain.
468*/
469static CFTypeRef _CFErrorMachCallBack(CFErrorRef err, CFStringRef key) {
470 if (CFEqual(key, kCFErrorDescriptionKey)) {
471 const char *errStr = mach_error_string(CFErrorGetCode(err));
472 if (errStr && strlen(errStr)) return CFStringCreateWithCString(kCFAllocatorSystemDefault, errStr, kCFStringEncodingUTF8);
473 }
474 return NULL;
475}
476#endif
477
478
479/* This initialize function is meant to be called lazily, the first time a callback is registered or requested. It creates the table and registers the built-in callbacks. Clearly doing this non-lazily in _CFErrorInitialize() would be simpler, but this is a fine example of something that should not have to happen at launch time.
480*/
481static void _CFErrorInitializeCallBackTable(void) {
482 // Create the table outside the lock
483 CFMutableDictionaryRef table = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, NULL);
484 __CFSpinLock(&_CFErrorSpinlock);
485 if (!_CFErrorCallBackTable) {
486 _CFErrorCallBackTable = table;
487 __CFSpinUnlock(&_CFErrorSpinlock);
488 } else {
489 __CFSpinUnlock(&_CFErrorSpinlock);
490 CFRelease(table);
491 // Note, even though the table looks like it was initialized, we go on to register the items on this thread as well, since otherwise we might consult the table before the items are actually registered.
492 }
493 CFErrorSetCallBackForDomain(kCFErrorDomainPOSIX, _CFErrorPOSIXCallBack);
cf7d2af9 494#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
bd5b749c
A
495 CFErrorSetCallBackForDomain(kCFErrorDomainMach, _CFErrorMachCallBack);
496#endif
497}
498
499void CFErrorSetCallBackForDomain(CFStringRef domainName, CFErrorUserInfoKeyCallBack callBack) {
500 if (!_CFErrorCallBackTable) _CFErrorInitializeCallBackTable();
501 __CFSpinLock(&_CFErrorSpinlock);
502 if (callBack) {
503 CFDictionarySetValue(_CFErrorCallBackTable, domainName, callBack);
504 } else {
505 CFDictionaryRemoveValue(_CFErrorCallBackTable, domainName);
506 }
507 __CFSpinUnlock(&_CFErrorSpinlock);
508}
509
510CFErrorUserInfoKeyCallBack CFErrorGetCallBackForDomain(CFStringRef domainName) {
511 if (!_CFErrorCallBackTable) _CFErrorInitializeCallBackTable();
512 __CFSpinLock(&_CFErrorSpinlock);
513 CFErrorUserInfoKeyCallBack callBack = (CFErrorUserInfoKeyCallBack)CFDictionaryGetValue(_CFErrorCallBackTable, domainName);
514 __CFSpinUnlock(&_CFErrorSpinlock);
515 return callBack;
516}
517
518
519