File: | vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m |
Location: | line 274, column 9 |
Description: | Memory Leak |
Code is compiled without garbage collection. |
1 | // Copyright 1997-2005, 2007 Omni Development, Inc. All rights reserved. |
2 | // |
3 | // This software may only be used and reproduced according to the |
4 | // terms in the file OmniSourceLicense.html, which should be |
5 | // distributed with this project and can also be found at |
6 | // <http://www.omnigroup.com/developer/sourcecode/sourcelicense/>. |
7 | |
8 | #import <OmniFoundation/OFUtilities.h> |
9 | |
10 | #import <objc/objc-runtime.h> |
11 | |
12 | #import <OmniFoundation/NSString-OFExtensions.h> |
13 | #import <OmniFoundation/OFObject.h> |
14 | #import <pthread.h> |
15 | |
16 | RCS_IDstatic __attribute__ ( ( used , section ( "__TEXT,rcsid" ) ) ) const char rcs_id [ ] = "$Header: svn+ssh://source.omnigroup.com/Source/svn/Omni/tags/OmniSourceRelease_2007-10-25/OmniGroup/Frameworks/OmniFoundation/OFUtilities.m 90130 2007-08-15 07:15:53Z bungi $" ;("$Header: svn+ssh://source.omnigroup.com/Source/svn/Omni/tags/OmniSourceRelease_2007-10-25/OmniGroup/Frameworks/OmniFoundation/OFUtilities.m 90130 2007-08-15 07:15:53Z bungi $") |
17 | |
18 | #define OF_GET_INPUT_CHUNK_LENGTH 80 |
19 | |
20 | void OFLog(NSString *messageFormat, ...) |
21 | { |
22 | va_list argList; |
23 | NSString *message; |
24 | |
25 | va_start__builtin_va_start ( argList , messageFormat )(argList, messageFormat); |
26 | message = [[[NSString alloc] initWithFormat:messageFormat arguments:argList] autorelease]; |
27 | va_end__builtin_va_end ( argList )(argList); |
28 | |
29 | fputs([message UTF8String], stdout__stdoutp); |
30 | } |
31 | |
32 | NSString *OFGetInput(NSStringEncoding encoding, NSString *promptFormat, ...) |
33 | { |
34 | va_list argList; |
35 | NSString *prompt; |
36 | NSString *input; |
37 | char buf[OF_GET_INPUT_CHUNK_LENGTH80]; |
38 | |
39 | va_start__builtin_va_start ( argList , promptFormat )(argList, promptFormat); |
40 | prompt = [[[NSString alloc] initWithFormat:promptFormat arguments:argList] autorelease]; |
41 | va_end__builtin_va_end ( argList )(argList); |
42 | |
43 | printf("%s", [prompt UTF8String]); |
44 | input = [NSString string]; |
45 | while (!ferror(stdin__stdinp)) { |
46 | memset(buf, 0, sizeof(buf)); |
47 | if (fgets(buf, sizeof(buf), stdin__stdinp) == NULL( ( void * ) 0 )) { |
48 | // EOF |
49 | break; |
50 | } |
51 | |
52 | input = [input stringByAppendingString:[NSString stringWithCString:buf encoding:encoding]]; |
53 | if ([input hasSuffix:@"\n"]) |
54 | break; |
55 | } |
56 | |
57 | if ([input length]) |
58 | return [input substringToIndex:[input length] - 1]; |
59 | |
60 | return nil( ( void * ) 0 ); |
61 | } |
62 | |
63 | #if 0 // Should probably use KVC |
64 | void OFSetIvar(NSObject *object, NSString *ivarName, NSObject *ivarValue) |
65 | { |
66 | Ivar ivar; |
67 | id *ivarSlot; |
68 | |
69 | // TODO:At some point, this function should take a void * and should look at the type of the ivar and deal with scalar values correctly. |
70 | |
71 | ivar = class_getInstanceVariable(*(Class *) object, [ivarName cString]); |
72 | OBASSERT(ivar); |
73 | |
74 | ivarSlot = (id *)((char *)object + ivar->ivar_offset); |
75 | |
76 | if (*ivarSlot != ivarValue) { |
77 | [*ivarSlot release]; |
78 | *ivarSlot = [ivarValue retain]; |
79 | } |
80 | } |
81 | |
82 | NSObject *OFGetIvar(NSObject *object, NSString *ivarName) |
83 | { |
84 | Ivar ivar; |
85 | id *ivarSlot; |
86 | |
87 | ivar = class_getInstanceVariable(*(Class *) object, [ivarName cString]); |
88 | OBASSERT(ivar); |
89 | |
90 | ivarSlot = (id *)((char *)object + ivar->ivar_offset); |
91 | |
92 | return *ivarSlot; |
93 | } |
94 | #endif |
95 | |
96 | // Port later if needed |
97 | #if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 |
98 | static const char *hexTable = "0123456789abcdef"; |
99 | |
100 | char *OFNameForPointer(id object, char *pointerName) |
101 | { |
102 | char firstChar, *p = pointerName; |
103 | const char *className; |
104 | unsigned long pointer; |
105 | |
106 | if (!object) { |
107 | *pointerName++ = '*'; |
108 | *pointerName++ = 'N'; |
109 | *pointerName++ = 'I'; |
110 | *pointerName++ = 'L'; |
111 | *pointerName++ = '*'; |
112 | *pointerName++ = '\0'; |
113 | return p; |
114 | } |
115 | |
116 | if (OBPointerIsClass(object)) { |
117 | firstChar = '+'; |
118 | pointer = (unsigned long)object; |
119 | } else { |
120 | firstChar = '-'; |
121 | pointer = (unsigned long)object->isa; |
122 | } |
123 | |
124 | // Rather than calling sprintf, we'll just format the string by hand. This is much faster. |
125 | |
126 | // Mark whether it is an instance or not |
127 | *pointerName++ = firstChar; |
128 | |
129 | // Write the class name |
130 | // BUG: We don't actually enforce the name length limit |
131 | if (!(className = ((Class)pointer)->name)) |
132 | className = "Bogus name!"; |
133 | |
134 | while ((*pointerName++ = *className++)) |
135 | ; |
136 | |
137 | // Back up over the trailing null |
138 | pointerName--; |
139 | *pointerName++ = ' '; |
140 | *pointerName++ = '('; |
141 | |
142 | // Write the pointer as hex |
143 | *pointerName++ = '0'; |
144 | *pointerName++ = 'x'; |
145 | |
146 | pointer = (unsigned long) object; |
147 | pointerName += 7; |
148 | |
149 | // 8 |
150 | *pointerName-- = hexTable[pointer & 0xf]; |
151 | pointer >>= 4; |
152 | |
153 | // 7 |
154 | *pointerName-- = hexTable[pointer & 0xf]; |
155 | pointer >>= 4; |
156 | |
157 | // 6 |
158 | *pointerName-- = hexTable[pointer & 0xf]; |
159 | pointer >>= 4; |
160 | |
161 | // 5 |
162 | *pointerName-- = hexTable[pointer & 0xf]; |
163 | pointer >>= 4; |
164 | |
165 | // 4 |
166 | *pointerName-- = hexTable[pointer & 0xf]; |
167 | pointer >>= 4; |
168 | |
169 | // 3 |
170 | *pointerName-- = hexTable[pointer & 0xf]; |
171 | pointer >>= 4; |
172 | |
173 | // 2 |
174 | *pointerName-- = hexTable[pointer & 0xf]; |
175 | pointer >>= 4; |
176 | |
177 | // 1 |
178 | *pointerName-- = hexTable[pointer & 0xf]; |
179 | |
180 | pointerName += 9; |
181 | |
182 | *pointerName++ = ')'; |
183 | *pointerName++ = '\0'; |
184 | |
185 | return p; |
186 | } |
187 | #endif |
188 | |
189 | BOOL OFInstanceIsKindOfClass(id instance, Class aClass) |
190 | { |
191 | Class sourceClass = OB_object_getClass(instance); |
192 | |
193 | while (sourceClass) { |
194 | if (sourceClass == aClass) |
195 | return YES( BOOL ) 1; |
196 | sourceClass = OB_class_getSuperclass(sourceClass); |
197 | } |
198 | return NO( BOOL ) 0; |
199 | } |
200 | |
201 | NSString *OFDescriptionForObject(id object, NSDictionary *locale, unsigned indentLevel) |
202 | { |
203 | if ([object isKindOfClass:[NSString class]]) |
204 | return object; |
205 | else if ([object respondsToSelector:@selector(descriptionWithLocale:indent:)]) |
206 | return [(id)object descriptionWithLocale:locale indent:indentLevel + 1]; |
207 | else if ([object respondsToSelector:@selector(descriptionWithLocale:)]) |
208 | return [(id)object descriptionWithLocale:locale]; |
209 | else |
210 | return [NSString stringWithFormat: @"%@%@", |
211 | [NSString spacesOfLength:(indentLevel + 1) * 4], |
212 | [object description]]; |
213 | } |
214 | |
215 | |
216 | /*" |
217 | Ensures that the given selName maps to a registered selector. If it doesn't, a copy of the string is made and it is registered with the runtime. The registered selector is returned, in any case. |
218 | "*/ |
219 | SEL OFRegisterSelectorIfAbsent(const char *selName) |
220 | { |
221 | SEL sel; |
222 | |
223 | if (!(sel = sel_getUid(selName))) { |
224 | unsigned int len; |
225 | char *newSel; |
226 | |
227 | // On NS4.0 and later, sel_registerName copies the selector name. But |
228 | // we won't assume that is the case -- we'll make a temporary copy |
229 | // and get the assertion rather than crashing the runtime (in case they |
230 | // change this in the future). |
231 | len = strlen(selName); |
232 | newSel = (char *)NSZoneMalloc(NULL( ( void * ) 0 ), len + 1); |
233 | strcpy(newSel, selName); |
234 | OBASSERTdo { if ( ! ( newSel [ len ] == '\0' ) ) OBAssertFailed ( "ASSERT" , "newSel[len] == '\\0'" , "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m" , 234 ) ; } while ( ( BOOL ) 0 )(newSel[len] == '\0'); |
235 | sel = sel_registerName(newSel); |
236 | |
237 | // Make sure the copy happened |
238 | OBASSERTdo { if ( ! ( ( void * ) sel_getUid ( selName ) != ( void * ) newSel ) ) OBAssertFailed ( "ASSERT" , "(void *)sel_getUid(selName) != (void *)newSel" , "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m" , 238 ) ; } while ( ( BOOL ) 0 )((void *)sel_getUid(selName) != (void *)newSel); |
239 | OBASSERTdo { if ( ! ( ( void * ) sel != ( void * ) newSel ) ) OBAssertFailed ( "ASSERT" , "(void *)sel != (void *)newSel" , "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m" , 239 ) ; } while ( ( BOOL ) 0 )((void *)sel != (void *)newSel); |
240 | |
241 | NSZoneFree(NULL( ( void * ) 0 ), newSel); |
242 | } |
243 | |
244 | return sel; |
245 | } |
246 | |
247 | #import <SystemConfiguration/SystemConfiguration.h> |
248 | #include <sys/socket.h> |
249 | #include <netinet/in.h> |
250 | #include <arpa/inet.h> |
251 | |
252 | unsigned int OFLocalIPv4Address(void) |
253 | { |
254 | SCDynamicStoreRef store; |
255 | CFStringRef interfacesKey; |
256 | NSDictionary *interfacesDictionary; |
257 | NSArray *interfaces; |
258 | unsigned int interfaceIndex, interfaceCount; |
259 | |
260 | store = SCDynamicStoreCreate(NULL( ( void * ) 0 ), (CFStringRef)[[NSProcessInfo processInfo] processName], NULL( ( void * ) 0 ), NULL( ( void * ) 0 )); |
261 | interfacesKey = SCDynamicStoreKeyCreateNetworkInterface(NULL( ( void * ) 0 ), kSCDynamicStoreDomainStatekSCDynamicStoreDomainState); |
262 | interfacesDictionary = (NSDictionary *)SCDynamicStoreCopyValue(store, interfacesKey); |
263 | interfaces = [interfacesDictionary objectForKey:(NSString *)kSCDynamicStorePropNetInterfaceskSCDynamicStorePropNetInterfaces]; |
264 | interfaceCount = [interfaces count]; |
[1] Loop condition is true. Entering loop body. | |
[4] Loop condition is true. Entering loop body. | |
[7] Loop condition is true. Entering loop body. | |
265 | for (interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) { |
266 | CFStringRef interfaceName; |
267 | CFStringRef ipv4Key, linkKey; |
268 | NSDictionary *ipv4Dictionary, *linkDictionary; |
269 | NSNumber *activeValue; |
270 | NSArray *ipAddresses; |
271 | |
272 | interfaceName = (CFStringRef)[interfaces objectAtIndex:interfaceIndex]; |
[8] Function call returns an object with a +1 retain count (owning reference). | |
273 | linkKey = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL( ( void * ) 0 ), kSCDynamicStoreDomainStatekSCDynamicStoreDomainState, interfaceName, kSCEntNetLinkkSCEntNetLink); |
274 | linkDictionary = (NSDictionary *)SCDynamicStoreCopyValue(store, linkKey); |
[9] Object allocated on line 273 and stored into 'linkKey' is no longer referenced after this point and has a retain count of +1 (object leaked). | |
275 | activeValue = [linkDictionary objectForKey:(NSString *)kSCPropNetLinkActivekSCPropNetLinkActive]; |
[2] Taking false branch. | |
[5] Taking false branch. | |
276 | if (activeValue == nil( ( void * ) 0 ) || ![activeValue boolValue]) |
277 | continue; |
278 | ipv4Key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL( ( void * ) 0 ), kSCDynamicStoreDomainStatekSCDynamicStoreDomainState, interfaceName, kSCEntNetIPv4kSCEntNetIPv4); |
279 | ipv4Dictionary = (NSDictionary *)SCDynamicStoreCopyValue(store, ipv4Key); |
280 | ipAddresses = [ipv4Dictionary objectForKey:(NSString *)kSCPropNetIPv4AddresseskSCPropNetIPv4Addresses]; |
[3] Taking false branch. | |
[6] Taking false branch. | |
281 | if ([ipAddresses count] != 0) { |
282 | NSString *ipAddressString; |
283 | unsigned long int address; |
284 | |
285 | ipAddressString = [ipAddresses objectAtIndex:0]; |
286 | address = inet_addr([ipAddressString UTF8String]); |
287 | if (address != (unsigned int)-1) |
288 | return address; |
289 | } |
290 | } |
291 | return (unsigned int)INADDR_LOOPBACK( u_int32_t ) 0x7f000001; // Localhost (127.0.0.1) |
292 | } |
293 | |
294 | |
295 | NSString *OFISOLanguageCodeForEnglishName(NSString *languageName) |
296 | { |
297 | return [[NSBundle bundleForClass:[OFObject class]] localizedStringForKey:languageName value:@"" table:@"EnglishToISO"]; |
298 | } |
299 | |
300 | NSString *OFLocalizedNameForISOLanguageCode(NSString *languageCode) |
301 | { |
302 | return [[NSBundle bundleForClass:[OFObject class]] localizedStringForKey:languageCode value:@"" table:@"Language"]; |
303 | } |
304 | |
305 | |
306 | // Adapted from OmniNetworking. May be replaced by something cleaner in the future. |
307 | |
308 | #import <sys/ioctl.h> |
309 | #import <sys/socket.h> |
310 | #import <net/if.h> |
311 | #import <net/if_dl.h> // for 'struct sockaddr_dl' |
312 | #import <unistd.h> // for close() |
313 | |
314 | // We'll guess that this is wildly larger than the maximum number of interfaces on the machine. I don't see that there is a way to get the number of interfaces so that you don't have to have a hard-coded value here. Sucks. |
315 | #define MAX_INTERFACES 100 |
316 | |
317 | #define IFR_NEXT(ifr) \ |
318 | ((struct ifreq *) ((char *) (ifr) + sizeof(*(ifr)) + \ |
319 | MAX(0, (int) (ifr)->ifr_addr.sa_len - (int) sizeof((ifr)->ifr_addr)))) |
320 | |
321 | static NSDictionary *InterfaceAddresses = nil( ( void * ) 0 ); |
322 | |
323 | static NSDictionary *OFLinkLayerInterfaceAddresses(void) |
324 | { |
325 | if (InterfaceAddresses != nil( ( void * ) 0 )) // only need to do this once |
326 | return InterfaceAddresses; |
327 | |
328 | int interfaceSocket; |
329 | if ((interfaceSocket = socket(AF_INET2, SOCK_DGRAM2, 0)) < 0) |
330 | [NSException raise:NSGenericException format:@"Unable to create temporary socket, errno = %d", OMNI_ERRNO( * __error ( ) )()]; |
331 | |
332 | struct ifreq requestBuffer[MAX_INTERFACES100]; |
333 | struct ifconf ifc; |
334 | ifc.ifc_len = sizeof(requestBuffer); |
335 | ifc.ifc_bufifc_ifcu . ifcu_buf = (caddr_t)requestBuffer; |
336 | if (ioctl(interfaceSocket, SIOCGIFCONF( ( ( unsigned long ) 0x80000000 | ( unsigned long ) 0x40000000 ) | ( ( sizeof ( struct ifconf ) & 0x1fff ) << 16 ) | ( ( ( 'i' ) ) << 8 ) | ( ( 36 ) ) ), &ifc) != 0) { |
337 | close(interfaceSocket); |
338 | [NSException raise:NSGenericException format:@"Unable to get list of network interfaces, errno = %d", OMNI_ERRNO( * __error ( ) )()]; |
339 | } |
340 | |
341 | NSMutableDictionary *interfaceAddresses = [NSMutableDictionary dictionary]; |
342 | |
343 | struct ifreq *linkInterface = (struct ifreq *) ifc.ifc_bufifc_ifcu . ifcu_buf; |
344 | while ((char *) linkInterface < &ifc.ifc_bufifc_ifcu . ifcu_buf[ifc.ifc_len]) { |
345 | // The ioctl returns both the entries having the address (AF_INET) and the link layer entries (AF_LINK). The AF_LINK entry has the link layer address which contains the interface type. This is the only way I can see to get this information. We cannot assume that we will get both an AF_LINK and AF_INET entry since the interface may not be configured. For example, if you have a 10Mb port on the motherboard and a 100Mb card, you may not configure the motherboard port. |
346 | |
347 | // For each AF_LINK entry... |
348 | if (linkInterface->ifr_addrifr_ifru . ifru_addr.sa_family == AF_LINK18) { |
349 | unsigned int nameLength; |
350 | for (nameLength = 0; nameLength < IFNAMSIZ16; nameLength++) |
351 | if (linkInterface->ifr_name[nameLength] == '\0') |
352 | break; |
353 | |
354 | NSString *ifname = [[[NSString alloc] initWithBytes:linkInterface->ifr_name length:nameLength encoding:NSASCIIStringEncoding] autorelease]; |
355 | // get the link layer address (for ethernet, this is the MAC address) |
356 | struct sockaddr_dl *linkSocketAddress = (struct sockaddr_dl *)&linkInterface->ifr_addrifr_ifru . ifru_addr; |
357 | int linkLayerAddressLength = linkSocketAddress->sdl_alen; |
358 | |
359 | if (linkLayerAddressLength > 0) { |
360 | const unsigned char *bytes = (unsigned char *)LLADDR( ( caddr_t ) ( ( linkSocketAddress ) -> sdl_data + ( linkSocketAddress ) -> sdl_nlen ) )(linkSocketAddress); |
361 | NSMutableString *addressString = [NSMutableString string]; |
362 | |
363 | int byteIndex; |
364 | for (byteIndex = 0; byteIndex < linkLayerAddressLength; byteIndex++) { |
365 | if (byteIndex > 0) |
366 | [addressString appendString:@":"]; |
367 | unsigned int byteValue = (unsigned int)bytes[byteIndex]; |
368 | [addressString appendFormat:@"%02x", byteValue]; |
369 | } |
370 | [interfaceAddresses setObject:addressString forKey:ifname]; |
371 | } |
372 | } |
373 | linkInterface = IFR_NEXT( ( struct ifreq * ) ( ( char * ) ( linkInterface ) + sizeof ( * ( linkInterface ) ) + ( { __typeof__ ( 0 ) __a = ( 0 ) ; __typeof__ ( ( int ) ( linkInterface ) -> ifr_ifru . ifru_addr . sa_len - ( int ) sizeof ( ( linkInterface ) -> ifr_ifru . ifru_addr ) ) __b = ( ( int ) ( linkInterface ) -> ifr_ifru . ifru_addr . sa_len - ( int ) sizeof ( ( linkInterface ) -> ifr_ifru . ifru_addr ) ) ; __a < __b ? __b : __a ; } ) ) )(linkInterface); |
374 | } |
375 | |
376 | close(interfaceSocket); |
377 | InterfaceAddresses = [interfaceAddresses copy]; |
378 | return InterfaceAddresses; |
379 | } |
380 | |
381 | // There is no perfect unique identifier for a machine since Apple doesn't guarantee that the machine's serial number will be accessible or present. It's unclear if the caveats to the machine serial number are really for old Macs or current ones. |
382 | NSString *OFUniqueMachineIdentifier(void) |
383 | { |
384 | NSDictionary *interfaces = OFLinkLayerInterfaceAddresses(); |
385 | |
386 | // Prefer the 'en0' interface for backwards compatibility. |
387 | NSString *identifier = [interfaces objectForKey:@"zen0"]; |
388 | |
389 | if (![NSString isEmptyString:identifier]) |
390 | return identifier; |
391 | |
392 | // If there is no such interface (it can get renamed, for one thing -- see RT ticket 191290>), look for another. Sort the interface names so we don't suffer changes based on |
393 | NSArray *names = [[interfaces allKeys] sortedArrayUsingSelector:@selector(compare:)]; |
394 | unsigned int nameIndex, nameCount = [names count]; |
395 | for (nameIndex = 0; nameIndex < nameCount; nameIndex++) { |
396 | NSString *name = [names objectAtIndex:nameIndex]; |
397 | identifier = [interfaces objectForKey:name]; |
398 | if (![NSString isEmptyString:identifier]) |
399 | return identifier; |
400 | } |
401 | |
402 | // TODO: We could try using the machine's serial number via <http://developer.apple.com/technotes/tn/tn1103.html>, but even this can fail. Often all we want is a globally unique string that at least lasts until the machine is rebooted. It would be nice if the machine had a 'boot-uuid'... perhaps we could write a file in /tmp with a UUID that we'd check for before generating our own. Race conditions would be an issue there (particularly at login with multple apps auto-launching). |
403 | OBASSERT_NOT_REACHEDdo { OBAssertFailed ( "NOTREACHED" , "No active interfaces?" , "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m" , 403 ) ; } while ( ( BOOL ) 0 )("No active interfaces?"); |
404 | return @"no unique machine identifier found"; |
405 | } |
406 | |
407 | NSString *OFHostName(void) |
408 | { |
409 | static NSString *cachedHostname = nil( ( void * ) 0 ); |
410 | |
411 | if (cachedHostname == nil( ( void * ) 0 )) { |
412 | char hostnameBuffer[MAXHOSTNAMELEN256 + 1]; |
413 | if (gethostname(hostnameBuffer, MAXHOSTNAMELEN256) == 0) { |
414 | hostnameBuffer[MAXHOSTNAMELEN256] = '\0'; // Ensure that the C string is NUL terminated |
415 | cachedHostname = [[NSString alloc] initWithCString:hostnameBuffer encoding:NSASCIIStringEncoding]; |
416 | } else { |
417 | cachedHostname = @"localhost"; |
418 | } |
419 | } |
420 | |
421 | return cachedHostname; |
422 | } |
423 | |
424 | static inline char _toHex(unsigned int i) |
425 | { |
426 | if (i >= 0 && i <= 9) |
427 | return '0' + i; |
428 | if (i >= 0xa && i <= 0xf) |
429 | return 'a' + i; |
430 | return '?'; |
431 | } |
432 | |
433 | static inline unsigned int _fillByte(unsigned char c, char *out) |
434 | { |
435 | if (isascii(c)) { |
436 | *out = c; |
437 | return 1; |
438 | } else { |
439 | out[0] = '\\'; |
440 | out[1] = _toHex(c >> 4); |
441 | out[2] = _toHex(c & 0xf); |
442 | return 3; |
443 | } |
444 | } |
445 | |
446 | char *OFFormatFCC(unsigned long fcc, char fccString[13]) |
447 | { |
448 | char *s = fccString; |
449 | |
450 | s = s + _fillByte((fcc & 0xff000000) >> 24, s); |
451 | s = s + _fillByte((fcc & 0x00ff0000) >> 16, s); |
452 | s = s + _fillByte((fcc & 0x0000ff00) >> 8, s); |
453 | s = s + _fillByte((fcc & 0x000000ff) >> 0, s); |
454 | *s = '\0'; |
455 | |
456 | return fccString; |
457 | } |
458 | |
459 | // Sigh. UTGetOSTypeFromString() / UTCreateStringForOSType() are in ApplicationServices, which we don't want to link from Foundation. Sux. |
460 | // Taking this opportunity to make the API a little better. |
461 | static BOOL ofGet4CCFromNSData(NSData *d, uint32_t *v) |
462 | { |
463 | union { |
464 | uint32_t i; |
465 | char c[4]; |
466 | } buf; |
467 | |
468 | if ([d length] == 4) { |
469 | [d getBytes:buf.c]; |
470 | *v = CFSwapInt32BigToHost(buf.i); |
471 | return YES( BOOL ) 1; |
472 | } else { |
473 | return NO( BOOL ) 0; |
474 | } |
475 | } |
476 | |
477 | BOOL OFGet4CCFromPlist(id pl, uint32_t *v) |
478 | { |
479 | if (!pl) |
480 | return NO( BOOL ) 0; |
481 | |
482 | if ([pl isKindOfClass:[NSString class]]) { |
483 | |
484 | /* Special case thanks to UTCreateStringForOSType() */ |
485 | if ([pl length] == 0) { |
486 | *v = 0; |
487 | return YES( BOOL ) 1; |
488 | } |
489 | |
490 | NSData *d = [(NSString *)pl dataUsingEncoding:NSMacOSRomanStringEncoding allowLossyConversion:NO( BOOL ) 0]; |
491 | if (d) |
492 | return ofGet4CCFromNSData(d, v); |
493 | else |
494 | return NO( BOOL ) 0; |
495 | } |
496 | |
497 | if ([pl isKindOfClass:[NSData class]]) |
498 | return ofGet4CCFromNSData((NSData *)pl, v); |
499 | |
500 | if ([pl isKindOfClass:[NSNumber class]]) { |
501 | *v = [pl unsignedIntValue]; |
502 | return YES( BOOL ) 1; |
503 | } |
504 | |
505 | return NO( BOOL ) 0; |
506 | } |
507 | |
508 | id OFCreatePlistFor4CC(uint32_t v) |
509 | { |
510 | // Characters which are maybe less-than-safe to store in an NSString: either characters which are invalid in MacRoman, or which produce combining marks instead of plain characters (e.g., MacRoman 0x41 0xFB could undergo Unicode recombination and come out as 0x81). |
511 | static const uint32_t ok_chars[8] = { |
512 | 0x00000000u, 0xAFFFFF3Bu, 0xFFFFFFFFu, 0x7FFFFFFFu, |
513 | 0xFFFFFFFFu, 0xFFFFE7FFu, 0xFFFFFFFFu, 0x113EFFFFu |
514 | }; |
515 | union { |
516 | uint32_t i; |
517 | UInt8 c[4]; |
518 | } buf; |
519 | |
520 | #define OK(ch) ( ok_chars[ch / 32] & (1 << (ch % 32)) ) |
521 | buf.i = CFSwapInt32HostToBig(v); |
522 | |
523 | if (!OK( ok_chars [ buf . c [ 0 ] / 32 ] & ( 1 << ( buf . c [ 0 ] % 32 ) ) )(buf.c[0]) || !OK( ok_chars [ buf . c [ 1 ] / 32 ] & ( 1 << ( buf . c [ 1 ] % 32 ) ) )(buf.c[1]) || !OK( ok_chars [ buf . c [ 2 ] / 32 ] & ( 1 << ( buf . c [ 2 ] % 32 ) ) )(buf.c[2]) || !OK( ok_chars [ buf . c [ 3 ] / 32 ] & ( 1 << ( buf . c [ 3 ] % 32 ) ) )(buf.c[3])) |
524 | return [[NSData alloc] initWithBytes:buf.c length:4]; |
525 | else { |
526 | CFStringRef s = CFStringCreateWithBytes(kCFAllocatorDefault, buf.c, 4, kCFStringEncodingMacRoman, FALSE0); |
527 | return (id)s; |
528 | } |
529 | } |
530 | |
531 | static const struct { |
532 | const char *typespec; |
533 | CFNumberType cfNumberType; |
534 | } cfNumberTypes[] = { |
535 | // This works because @encode returns an encoding indicating the concrete implementation of a type, so that for example @encode(pid_t) and @encode(int) both return "i" (since pid_t is currently typedef'd to int32_t, and the compiler int is 32 bits). |
536 | // Note that on versions later than 10.1, there's no precise way to represent an unsigned int in an NSNumber/CFNumber (despite the existence of +numberWithUnsignedInt:): RADAR #3513632. (In 10.5, +numberWithUnsignedInt: produces a kCFNumberSInt64Type, which at least is better than the old behavior of interpreting the arg as signed... In general, CFNumber is much less flexible than the NSNumber it replaced.) |
537 | |
538 | // Also note that the types here are somewhat redundant (SInt32 is the same as an int right now); using @encode ensures that the table is accurate, but there will be two or three entries for any given ObjC type. |
539 | |
540 | #define T(t) { @encode(t), kCFNumber ## t ## Type } |
541 | T{ @ encode ( SInt8 ) , kCFNumberSInt8Type }(SInt8), T{ @ encode ( SInt16 ) , kCFNumberSInt16Type }(SInt16), T{ @ encode ( SInt32 ) , kCFNumberSInt32Type }(SInt32), T{ @ encode ( SInt64 ) , kCFNumberSInt64Type }(SInt64), T{ @ encode ( Float32 ) , kCFNumberFloat32Type }(Float32), T{ @ encode ( Float64 ) , kCFNumberFloat64Type }(Float64), |
542 | #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) |
543 | T{ @ encode ( NSInteger ) , kCFNumberNSIntegerType }(NSInteger), T{ @ encode ( CGFloat ) , kCFNumberCGFloatType }(CGFloat), |
544 | // Yep, there's no kCFNumberNSUIntegerType. WTF, Apple?!? |
545 | #endif |
546 | #undef T |
547 | |
548 | #define T(n, v) { @encode(n), kCFNumber ## v ## Type } |
549 | T{ @ encode ( char ) , kCFNumberCharType }(char, Char), |
550 | T{ @ encode ( short ) , kCFNumberShortType }(short, Short), |
551 | T{ @ encode ( int ) , kCFNumberIntType }(int, Int), |
552 | T{ @ encode ( long ) , kCFNumberLongType }(long, Long), |
553 | T{ @ encode ( float ) , kCFNumberFloatType }(float, Float), |
554 | T{ @ encode ( double ) , kCFNumberDoubleType }(double, Double), |
555 | #undef T |
556 | |
557 | { NULL( ( void * ) 0 ), 0 } |
558 | }; |
559 | |
560 | CFNumberType OFCFNumberTypeForObjCType(const char *objCType) |
561 | { |
562 | int i; |
563 | for(i = 0; cfNumberTypes[i].typespec != NULL( ( void * ) 0 ); i ++) { |
564 | if (!strcmp(objCType, cfNumberTypes[i].typespec)) |
565 | return cfNumberTypes[i].cfNumberType; |
566 | } |
567 | |
568 | OBASSERT_NOT_REACHEDdo { OBAssertFailed ( "NOTREACHED" , "ObjC type with no corresponding CFNumber type" , "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m" , 568 ) ; } while ( ( BOOL ) 0 )("ObjC type with no corresponding CFNumber type"); |
569 | return 0; |
570 | } |
571 | |
572 | const char *OFObjCTypeForCFNumberType(CFNumberType cfType) |
573 | { |
574 | int i; |
575 | for(i = 0; cfNumberTypes[i].typespec != NULL( ( void * ) 0 ); i ++) { |
576 | if (cfNumberTypes[i].cfNumberType == cfType) |
577 | return cfNumberTypes[i].typespec; |
578 | } |
579 | |
580 | // This should never happen, unless Apple adds more types to CoreFoundation and we don't add them to the array. |
581 | OBASSERT_NOT_REACHEDdo { OBAssertFailed ( "NOTREACHED" , "CFNumber type with no corresponding ObjC type" , "/Volumes/Local/Users/amaxwell/build/bibdesk-clean/vendorsrc/OmniGroup/OmniFoundation/OFUtilities.m" , 581 ) ; } while ( ( BOOL ) 0 )("CFNumber type with no corresponding ObjC type"); |
582 | return NULL( ( void * ) 0 ); |
583 | } |
584 |