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