* Added kv*Set utility functions for handling KV grunge when mutating an NSSet property.
* Change CFMakeCollectable to NSMakeCollectable.
5 // Created by Jens Alfke on 2/25/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYErrorUtils.h"
11 #import "CollectionUtils.h"
12 #import <Foundation/Foundation.h>
15 #import <Security/SecBase.h>
19 NSString* const MYErrorDomain = @"MYErrorDomain";
22 static NSError *MYMakeErrorV( int errorCode, NSString *domain, NSString *message, va_list args )
24 message = [[NSString alloc] initWithFormat: message arguments: args];
25 NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
26 message, NSLocalizedDescriptionKey,
29 return [NSError errorWithDomain: domain
35 NSError *MYError( int errorCode, NSString *domain, NSString *message, ... )
38 va_start(args,message);
39 NSError *error = MYMakeErrorV(errorCode,domain,message,args);
45 BOOL MYReturnError( NSError **outError,
46 int errorCode, NSString *domain, NSString *messageFormat, ... )
51 va_start(args,messageFormat);
52 *outError = MYMakeErrorV(errorCode, domain, messageFormat, args);
54 Log(@"MYReturnError: %@",*outError);
56 Log(@"MYReturnError: %@/%i",domain,errorCode);
63 BOOL MYMiscError( NSError **error, NSString *message, ... )
67 va_start(args,message);
68 *error = MYMakeErrorV(kMYErrorMisc,MYErrorDomain, message,args);
75 static NSString* printableOSType( OSType t ) {
76 if (t < 0x20202020 || t > 0x7e7e7e7e)
82 buf.ostype = CFSwapInt32HostToBig(t);
83 for (int i=0; i<4; i++)
84 if (buf.ch[i] < 0x20 || buf.ch[i] > 0x7E)
86 return [[[NSString alloc] initWithBytes: &buf.ch length: 4 encoding: NSMacOSRomanStringEncoding]
91 static NSString* printableErrorCode( NSInteger code ) {
93 return $sprintf(@"%u", code); // CSSM errors are huge unsigned values > 0x80000000
94 NSString *result = printableOSType((OSType)code);
96 return result; // CoreAudio errors are OSTypes (4-char strings)
97 return $sprintf(@"%i", code); // Default: OSStatus and errno values are signed
100 static NSString* MYShortErrorDomainName( NSString *domain ) {
101 if ([domain hasPrefix: @"kCFErrorDomain"])
102 domain = [domain substringFromIndex: 14];
104 if ([domain hasSuffix: @"ErrorDomain"])
105 domain = [domain substringToIndex: domain.length - 11];
106 if ([domain hasPrefix: @"NS"])
107 domain = [domain substringFromIndex: 2];
112 NSString* MYErrorName( NSString *domain, NSInteger code ) {
115 NSString *codeStr = printableErrorCode(code);
118 NSString *result = nil;
120 if ($equal(domain,NSPOSIXErrorDomain)) {
121 // Interpret POSIX errors via strerror
122 // (which unfortunately returns a description, not the name of the constant)
123 const char *name = strerror(code);
125 result = [NSString stringWithCString: name encoding: NSASCIIStringEncoding];
126 if ([result hasPrefix: @"Unknown error"])
130 #if !TARGET_OS_IPHONE || defined(__SEC_TYPES__)
131 else if ($equal(domain,NSOSStatusErrorDomain)) {
132 // If it's an OSStatus, check whether CarbonCore knows its name:
133 const char *name = NULL;
134 #if !TARGET_OS_IPHONE
135 name = GetMacOSStatusErrorString(code);
138 result = [NSString stringWithCString: name encoding: NSMacOSRomanStringEncoding];
141 result = (id) SecCopyErrorMessageString(code,NULL);
143 [NSMakeCollectable(result) autorelease];
144 if ([result hasPrefix: @"OSStatus "])
145 result = nil; // just a generic message
153 // Look up errors in string files keyed by the domain name:
154 NSString *table = [@"MYError_" stringByAppendingString: domain];
155 result = [[NSBundle mainBundle] localizedStringForKey: codeStr value: @"?" table: table];
156 if ([result isEqualToString: @"?"])
160 codeStr = $sprintf(@"%@ %@", MYShortErrorDomainName(domain), codeStr);;
161 return result ? $sprintf(@"%@ (%@)", result, codeStr) : codeStr;
167 @implementation NSError (MYUtils)
169 - (NSError*) my_errorByPrependingMessage: (NSString*)message
171 if( message.length ) {
172 NSDictionary *oldUserInfo = self.userInfo;
173 NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
175 [userInfo addEntriesFromDictionary: oldUserInfo];
176 NSString *desc = [oldUserInfo objectForKey: NSLocalizedDescriptionKey];
178 message = $sprintf(@"%@: %@", message, desc);
179 [userInfo setObject: message forKey: NSLocalizedDescriptionKey];
180 return [NSError errorWithDomain: self.domain
187 - (NSString*) my_nameOfCode {
188 return MYErrorName(self.domain, self.code);
194 TestCase(MYErrorUtils) {
195 CAssertEqual(printableOSType('abcd'), @"abcd");
196 CAssertEqual(printableOSType(' '), @" ");
197 CAssertEqual(printableOSType(0x7e7e7e7e), @"~~~~");
198 CAssertEqual(printableOSType(0x7e7F7e7e), nil);
199 CAssertEqual(printableOSType(0x7e0D7e7e), nil);
200 CAssertEqual(printableOSType(0), nil);
201 CAssertEqual(printableOSType((OSType)-123456), nil);
203 CAssertEqual(MYErrorName(nil,0), nil);
204 CAssertEqual(MYErrorName(nil,12345), @"12345");
205 CAssertEqual(MYErrorName(nil,1), @"1");
206 CAssertEqual(MYErrorName(nil,-1), @"-1");
207 CAssertEqual(MYErrorName(nil,12345), @"12345");
208 CAssertEqual(MYErrorName(nil,-12345), @"-12345");
209 CAssertEqual(MYErrorName(nil,2147549184u), @"2147549184");
211 CAssertEqual(MYErrorName(@"foobar",0), nil);
212 CAssertEqual(MYErrorName(@"foobar",'fmt?'), @"foobar fmt?");
213 CAssertEqual(MYErrorName(@"foobar",1), @"foobar 1");
214 CAssertEqual(MYErrorName(@"FoobarErrorDomain",-1), @"Foobar -1");
215 CAssertEqual(MYErrorName(@"NSFoobarErrorDomain",12345), @"Foobar 12345");
218 err = [NSError errorWithDomain: NSPOSIXErrorDomain code: EPERM userInfo: nil];
219 CAssertEqual(err.my_nameOfCode, @"Operation not permitted (POSIX 1)");
220 err = [NSError errorWithDomain: NSPOSIXErrorDomain code: 12345 userInfo: nil];
221 CAssertEqual(err.my_nameOfCode, @"POSIX 12345");
223 #if !TARGET_OS_IPHONE
224 err = [NSError errorWithDomain: NSOSStatusErrorDomain code: paramErr userInfo: nil];
225 CAssertEqual(err.my_nameOfCode, @"paramErr (OSStatus -50)");
226 err = [NSError errorWithDomain: NSOSStatusErrorDomain code: fnfErr userInfo: nil];
227 CAssertEqual(err.my_nameOfCode, @"fnfErr (OSStatus -43)");
228 err = [NSError errorWithDomain: NSOSStatusErrorDomain code: -25291 userInfo: nil];
229 CAssertEqual(err.my_nameOfCode, @"errKCNotAvailable / errSecNotAvailable (OSStatus -25291)");
230 err = [NSError errorWithDomain: NSOSStatusErrorDomain code: -25260 userInfo: nil];
231 CAssertEqual(err.my_nameOfCode, @"Passphrase is required for import/export. (OSStatus -25260)");
233 err = [NSError errorWithDomain: NSOSStatusErrorDomain code: 12345 userInfo: nil];
234 CAssertEqual(err.my_nameOfCode, @"OSStatus 12345");
236 err = [NSError errorWithDomain: @"CSSMErrorDomain" code: 2147549184u userInfo: nil];
237 CAssertEqual(err.my_nameOfCode, @"CSSM_CSSM_BASE_ERROR (CSSM 2147549184)");
239 err = [NSError errorWithDomain: (id)kCFErrorDomainCocoa code: 100 userInfo: nil];
240 CAssertEqual(err.my_nameOfCode, @"Cocoa 100");