2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 // CFBundle_InfoPlist.c
28 // Created by Tony Parker on 5/30/12.
32 #include <CoreFoundation/CFBundle.h>
33 #include <CoreFoundation/CFNumber.h>
34 #include "CFBundle_Internal.h"
35 #include "CFByteOrder.h"
36 #include "CFURLAccess.h"
38 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_EMBEDDED_MINI
40 #include <sys/sysctl.h>
44 // The following strings are initialized 'later' (i.e., not at static initialization time) because static init time is too early for CFSTR to work, on platforms without constant CF strings
45 #if !__CONSTANT_STRINGS__
47 #define _CFBundleNumberOfPlatforms 7
48 static CFStringRef _CFBundleSupportedPlatforms
[_CFBundleNumberOfPlatforms
] = { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
49 static const char *_CFBundleSupportedPlatformStrings
[_CFBundleNumberOfPlatforms
] = { "iphoneos", "macos", "windows", "linux", "freebsd", "solaris", "hpux" };
51 #define _CFBundleNumberOfProducts 3
52 static CFStringRef _CFBundleSupportedProducts
[_CFBundleNumberOfProducts
] = { NULL
, NULL
, NULL
};
53 static const char *_CFBundleSupportedProductStrings
[_CFBundleNumberOfProducts
] = { "iphone", "ipod", "ipad" };
55 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3
56 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts
[_CFBundleNumberOfiPhoneOSPlatformProducts
] = { NULL
, NULL
, NULL
};
57 static const char *_CFBundleSupportediPhoneOSPlatformProductStrings
[_CFBundleNumberOfiPhoneOSPlatformProducts
] = { "iphone", "ipod", "ipad" };
59 CF_PRIVATE
void _CFBundleResourcesInitialize() {
60 for (unsigned int i
= 0; i
< _CFBundleNumberOfPlatforms
; i
++) _CFBundleSupportedPlatforms
[i
] = CFStringCreateWithCString(kCFAllocatorSystemDefault
, _CFBundleSupportedPlatformStrings
[i
], kCFStringEncodingUTF8
);
62 for (unsigned int i
= 0; i
< _CFBundleNumberOfProducts
; i
++) _CFBundleSupportedProducts
[i
] = CFStringCreateWithCString(kCFAllocatorSystemDefault
, _CFBundleSupportedProductStrings
[i
], kCFStringEncodingUTF8
);
64 for (unsigned int i
= 0; i
< _CFBundleNumberOfiPhoneOSPlatformProducts
; i
++) _CFBundleSupportediPhoneOSPlatformProducts
[i
] = CFStringCreateWithCString(kCFAllocatorSystemDefault
, _CFBundleSupportediPhoneOSPlatformProductStrings
[i
], kCFStringEncodingUTF8
);
69 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
70 // On iOS, we only support one platform
71 #define _CFBundleNumberOfPlatforms 1
72 static CFStringRef _CFBundleSupportedPlatforms
[_CFBundleNumberOfPlatforms
] = { CFSTR("iphoneos") };
74 // On other platforms, we support the following platforms
75 #define _CFBundleNumberOfPlatforms 7
76 static CFStringRef _CFBundleSupportedPlatforms
[_CFBundleNumberOfPlatforms
] = { CFSTR("iphoneos"), CFSTR("macos"), CFSTR("windows"), CFSTR("linux"), CFSTR("freebsd"), CFSTR("solaris"), CFSTR("hpux") };
79 #define _CFBundleNumberOfProducts 3
80 static CFStringRef _CFBundleSupportedProducts
[_CFBundleNumberOfProducts
] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") };
82 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3
83 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts
[_CFBundleNumberOfiPhoneOSPlatformProducts
] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") };
85 CF_PRIVATE
void _CFBundleResourcesInitialize() { }
89 #pragma mark Product and Platform Getters - Exported
91 static CFStringRef _cfBundlePlatform
= NULL
;
92 CF_EXPORT
void _CFSetProductName(CFStringRef str
) {
93 // TODO: This should be removed. The "CLASSIC" check below removes the need to set the product name manually.
94 if (str
) CFRetain(str
);
95 _cfBundlePlatform
= str
;
96 // Note that the previous value is leaked, which is fine normally
97 // because the initial values would tend to be the constant strings
98 // below. That is required for thread-safety value due to the Get
99 // function [not being Copy]. It is also fine because people
100 // shouldn't be screwing around with this value casually.
103 CF_EXPORT CFStringRef
_CFGetProductName(void) {
104 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
105 if (!_cfBundlePlatform
) {
106 const char *isClassic
= __CFgetenv("CLASSIC");
107 if (isClassic
&& strnlen(isClassic
, 1) >= 1 && isClassic
[0] == '1') {
108 _cfBundlePlatform
= CFSTR("iphone");
111 memset(buffer
, 0, sizeof(buffer
));
112 size_t buflen
= sizeof(buffer
);
113 int ret
= sysctlbyname("hw.machine", buffer
, &buflen
, NULL
, 0);
114 if (0 == ret
|| (-1 == ret
&& ENOMEM
== errno
)) {
115 if (6 <= buflen
&& 0 == memcmp(buffer
, "iPhone", 6)) {
116 _cfBundlePlatform
= CFSTR("iphone");
117 } else if (4 <= buflen
&& 0 == memcmp(buffer
, "iPod", 4)) {
118 _cfBundlePlatform
= CFSTR("ipod");
119 } else if (4 <= buflen
&& 0 == memcmp(buffer
, "iPad", 4)) {
120 _cfBundlePlatform
= CFSTR("ipad");
122 const char *env
= __CFgetenv("IPHONE_SIMULATOR_DEVICE");
124 if (0 == strcmp(env
, "iPhone")) {
125 _cfBundlePlatform
= CFSTR("iphone");
126 } else if (0 == strcmp(env
, "iPad")) {
127 _cfBundlePlatform
= CFSTR("ipad");
129 // fallback, unrecognized IPHONE_SIMULATOR_DEVICE
132 // fallback, unrecognized hw.machine and no IPHONE_SIMULATOR_DEVICE
137 if (!_cfBundlePlatform
) _cfBundlePlatform
= CFSTR("iphone"); // fallback
139 return _cfBundlePlatform
;
144 // All new-style bundles will have these extensions.
145 CF_EXPORT CFStringRef
_CFGetPlatformName(void) {
146 #if DEPLOYMENT_TARGET_MACOSX
147 return _CFBundleMacOSXPlatformName
;
148 #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
149 return _CFBundleiPhoneOSPlatformName
;
150 #elif DEPLOYMENT_TARGET_WINDOWS
151 return _CFBundleWindowsPlatformName
;
152 #elif DEPLOYMENT_TARGET_SOLARIS
153 return _CFBundleSolarisPlatformName
;
154 #elif DEPLOYMENT_TARGET_HPUX
155 return _CFBundleHPUXPlatformName
;
156 #elif DEPLOYMENT_TARGET_LINUX
157 return _CFBundleLinuxPlatformName
;
158 #elif DEPLOYMENT_TARGET_FREEBSD
159 return _CFBundleFreeBSDPlatformName
;
161 #error Unknown or unspecified DEPLOYMENT_TARGET
165 CF_EXPORT CFStringRef
_CFGetAlternatePlatformName(void) {
166 #if DEPLOYMENT_TARGET_MACOSX
167 return _CFBundleAlternateMacOSXPlatformName
;
168 #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
169 return _CFBundleMacOSXPlatformName
;
170 #elif DEPLOYMENT_TARGET_WINDOWS
173 #error Unknown or unspecified DEPLOYMENT_TARGET
178 #pragma mark Product and Platform Suffix Processing - Internal
180 // TODO: Merge with below function, they do the same thing
181 static Boolean
_isValidPlatformSuffix(CFStringRef suffix
) {
182 for (CFIndex idx
= 0; idx
< _CFBundleNumberOfPlatforms
; idx
++) {
183 if (CFEqual(suffix
, _CFBundleSupportedPlatforms
[idx
])) return true;
188 // Returns true if the searchRange of the fileName is equal to a valid platform name (e.g., macos, iphoneos)
189 CF_PRIVATE Boolean
_CFBundleSupportedPlatformName(CFStringRef fileName
, CFRange searchRange
) {
190 for (CFIndex i
= 0; i
< _CFBundleNumberOfPlatforms
; i
++) {
191 if (CFStringFindWithOptions(fileName
, _CFBundleSupportedPlatforms
[i
], searchRange
, kCFCompareAnchored
, NULL
)) {
198 // TODO: Merge with below function, they do the same thing
199 static Boolean
_isValidProductSuffix(CFStringRef suffix
) {
200 for (CFIndex idx
= 0; idx
< _CFBundleNumberOfProducts
; idx
++) {
201 if (CFEqual(suffix
, _CFBundleSupportedProducts
[idx
])) return true;
206 // Returns true if the searchRange of the fileName is equal to a a valid product name (e.g., ipod, ipad)
207 CF_PRIVATE Boolean
_CFBundleSupportedProductName(CFStringRef fileName
, CFRange searchRange
) {
208 for (CFIndex i
= 0; i
< _CFBundleNumberOfProducts
; i
++) {
209 if (CFStringFindWithOptions(fileName
, _CFBundleSupportedProducts
[i
], searchRange
, kCFCompareAnchored
, NULL
)) {
216 static Boolean
_isValidiPhoneOSPlatformProductSuffix(CFStringRef suffix
) {
217 for (CFIndex idx
= 0; idx
< _CFBundleNumberOfiPhoneOSPlatformProducts
; idx
++) {
218 if (CFEqual(suffix
, _CFBundleSupportediPhoneOSPlatformProducts
[idx
])) return true;
223 static Boolean
_isValidPlatformAndProductSuffixPair(CFStringRef platform
, CFStringRef product
) {
224 if (!platform
&& !product
) return true;
226 return _isValidProductSuffix(product
);
229 return _isValidPlatformSuffix(platform
);
231 if (CFEqual(platform
, _CFBundleiPhoneOSPlatformName
)) {
232 return _isValidiPhoneOSPlatformProductSuffix(product
);
237 static Boolean
_isBlacklistedKey(CFStringRef keyName
) {
238 #if __CONSTANT_STRINGS__
239 #define _CFBundleNumberOfBlacklistedInfoDictionaryKeys 2
240 static const CFStringRef _CFBundleBlacklistedInfoDictionaryKeys
[_CFBundleNumberOfBlacklistedInfoDictionaryKeys
] = { CFSTR("CFBundleExecutable"), CFSTR("CFBundleIdentifier") };
242 for (CFIndex idx
= 0; idx
< _CFBundleNumberOfBlacklistedInfoDictionaryKeys
; idx
++) {
243 if (CFEqual(keyName
, _CFBundleBlacklistedInfoDictionaryKeys
[idx
])) return true;
249 static Boolean
_isOverrideKey(CFStringRef fullKey
, CFStringRef
*outBaseKey
, CFStringRef
*outPlatformSuffix
, CFStringRef
*outProductSuffix
) {
253 if (outPlatformSuffix
) {
254 *outPlatformSuffix
= NULL
;
256 if (outProductSuffix
) {
257 *outProductSuffix
= NULL
;
261 CFRange minusRange
= CFStringFind(fullKey
, CFSTR("-"), kCFCompareBackwards
);
262 CFRange tildeRange
= CFStringFind(fullKey
, CFSTR("~"), kCFCompareBackwards
);
263 if (minusRange
.location
== kCFNotFound
&& tildeRange
.location
== kCFNotFound
) return false;
264 // minus must come before tilde if both are present
265 if (minusRange
.location
!= kCFNotFound
&& tildeRange
.location
!= kCFNotFound
&& tildeRange
.location
<= minusRange
.location
) return false;
267 CFIndex strLen
= CFStringGetLength(fullKey
);
268 CFRange baseKeyRange
= (minusRange
.location
!= kCFNotFound
) ? CFRangeMake(0, minusRange
.location
) : CFRangeMake(0, tildeRange
.location
);
269 CFRange platformRange
= CFRangeMake(kCFNotFound
, 0);
270 CFRange productRange
= CFRangeMake(kCFNotFound
, 0);
271 if (minusRange
.location
!= kCFNotFound
) {
272 platformRange
.location
= minusRange
.location
+ minusRange
.length
;
273 platformRange
.length
= ((tildeRange
.location
!= kCFNotFound
) ? tildeRange
.location
: strLen
) - platformRange
.location
;
275 if (tildeRange
.location
!= kCFNotFound
) {
276 productRange
.location
= tildeRange
.location
+ tildeRange
.length
;
277 productRange
.length
= strLen
- productRange
.location
;
279 if (baseKeyRange
.length
< 1) return false;
280 if (platformRange
.location
!= kCFNotFound
&& platformRange
.length
< 1) return false;
281 if (productRange
.location
!= kCFNotFound
&& productRange
.length
< 1) return false;
283 CFStringRef platform
= (platformRange
.location
!= kCFNotFound
) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, fullKey
, platformRange
) : NULL
;
284 CFStringRef product
= (productRange
.location
!= kCFNotFound
) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, fullKey
, productRange
) : NULL
;
285 Boolean result
= _isValidPlatformAndProductSuffixPair(platform
, product
);
289 *outBaseKey
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, fullKey
, baseKeyRange
);
291 if (outPlatformSuffix
) {
292 *outPlatformSuffix
= platform
;
294 if (platform
&& !(0)) CFRelease(platform
);
296 if (outProductSuffix
) {
297 *outProductSuffix
= product
;
299 if (product
&& !(0)) CFRelease(product
);
302 if (platform
&& !(0)) CFRelease(platform
);
303 if (product
&& !(0)) CFRelease(product
);
308 static Boolean
_isCurrentPlatformAndProduct(CFStringRef platform
, CFStringRef product
) {
309 if (!platform
&& !product
) return true;
311 return CFEqual(_CFGetProductName(), product
);
314 return CFEqual(_CFGetPlatformName(), platform
);
317 return CFEqual(_CFGetProductName(), product
) && CFEqual(_CFGetPlatformName(), platform
);
320 static CFArrayRef
_CopySortedOverridesForBaseKey(CFStringRef keyName
, CFDictionaryRef dict
) {
321 CFMutableArrayRef overrides
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
322 CFStringRef keyNameWithBoth
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@-%@~%@"), keyName
, _CFGetPlatformName(), _CFGetProductName());
323 CFStringRef keyNameWithProduct
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@~%@"), keyName
, _CFGetProductName());
324 CFStringRef keyNameWithPlatform
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@-%@"), keyName
, _CFGetPlatformName());
326 CFIndex count
= CFDictionaryGetCount(dict
);
329 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
330 CFTypeRef
*values
= &(keys
[count
]);
332 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
333 for (CFIndex idx
= 0; idx
< count
; idx
++) {
334 if (CFEqual(keys
[idx
], keyNameWithBoth
)) {
335 CFArrayAppendValue(overrides
, keys
[idx
]);
339 for (CFIndex idx
= 0; idx
< count
; idx
++) {
340 if (CFEqual(keys
[idx
], keyNameWithProduct
)) {
341 CFArrayAppendValue(overrides
, keys
[idx
]);
345 for (CFIndex idx
= 0; idx
< count
; idx
++) {
346 if (CFEqual(keys
[idx
], keyNameWithPlatform
)) {
347 CFArrayAppendValue(overrides
, keys
[idx
]);
351 for (CFIndex idx
= 0; idx
< count
; idx
++) {
352 if (CFEqual(keys
[idx
], keyName
)) {
353 CFArrayAppendValue(overrides
, keys
[idx
]);
358 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, keys
);
361 CFRelease(keyNameWithProduct
);
362 CFRelease(keyNameWithPlatform
);
363 CFRelease(keyNameWithBoth
);
368 CF_PRIVATE
void _CFBundleInfoPlistProcessInfoDictionary(CFMutableDictionaryRef dict
) {
369 // Defensive programming
372 CFIndex count
= CFDictionaryGetCount(dict
);
375 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2 * count
* sizeof(CFTypeRef
), 0);
376 CFTypeRef
*values
= &(keys
[count
]);
377 CFMutableArrayRef guard
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
379 CFDictionaryGetKeysAndValues(dict
, keys
, values
);
380 for (CFIndex idx
= 0; idx
< count
; idx
++) {
381 CFStringRef keyPlatformSuffix
, keyProductSuffix
, keyName
;
382 if (_isOverrideKey((CFStringRef
)keys
[idx
], &keyName
, &keyPlatformSuffix
, &keyProductSuffix
)) {
383 CFArrayRef keysForBaseKey
= NULL
;
384 if (_isCurrentPlatformAndProduct(keyPlatformSuffix
, keyProductSuffix
) && !_isBlacklistedKey(keyName
) && CFDictionaryContainsKey(dict
, keys
[idx
])) {
385 keysForBaseKey
= _CopySortedOverridesForBaseKey(keyName
, dict
);
386 CFIndex keysForBaseKeyCount
= CFArrayGetCount(keysForBaseKey
);
388 //make sure the other keys for this base key don't get released out from under us until we're done
389 CFArrayAppendValue(guard
, keysForBaseKey
);
391 //the winner for this base key will be sorted to the front, do the override with it
392 CFTypeRef highestPriorityKey
= CFArrayGetValueAtIndex(keysForBaseKey
, 0);
393 CFDictionarySetValue(dict
, keyName
, CFDictionaryGetValue(dict
, highestPriorityKey
));
395 //remove everything except the now-overridden key; this will cause them to fail the CFDictionaryContainsKey(dict, keys[idx]) check in the enclosing if() and not be reprocessed
396 for (CFIndex presentKeysIdx
= 0; presentKeysIdx
< keysForBaseKeyCount
; presentKeysIdx
++) {
397 CFStringRef currentKey
= (CFStringRef
)CFArrayGetValueAtIndex(keysForBaseKey
, presentKeysIdx
);
398 if (!CFEqual(currentKey
, keyName
))
399 CFDictionaryRemoveValue(dict
, currentKey
);
402 CFDictionaryRemoveValue(dict
, keys
[idx
]);
406 if (keyPlatformSuffix
) CFRelease(keyPlatformSuffix
);
407 if (keyProductSuffix
) CFRelease(keyProductSuffix
);
409 if (keysForBaseKey
) CFRelease(keysForBaseKey
);
413 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, keys
);
419 #pragma mark Info Plist Functions
421 CF_PRIVATE CFDictionaryRef
_CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc
, CFURLRef url
, uint8_t *version
) {
422 CFDictionaryRef dict
= NULL
;
423 unsigned char buff
[CFMaxPathSize
];
424 uint8_t localVersion
= 0;
426 if (CFURLGetFileSystemRepresentation(url
, true, buff
, CFMaxPathSize
)) {
427 CFURLRef newURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, buff
, strlen((char *)buff
), true);
428 if (!newURL
) newURL
= (CFURLRef
)CFRetain(url
);
430 localVersion
= _CFBundleGetBundleVersionForURL(newURL
);
432 dict
= _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc
, newURL
, localVersion
);
435 if (version
) *version
= localVersion
;
439 CF_PRIVATE CFDictionaryRef
_CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc
, CFURLRef url
, uint8_t version
) {
440 // We only return NULL for a bad URL, otherwise we create a dummy dictionary
441 if (!url
) return NULL
;
443 CFDictionaryRef result
= NULL
;
445 // We're going to search for two files here - Info.plist and Info-macos.plist (platform specific). The platform-specific one takes precedence.
446 // First, construct the URL to the directory we'll search by using the passed in URL as a base
447 CFStringRef platformInfoURLFromBase
= _CFBundlePlatformInfoURLFromBase0
;
448 CFStringRef infoURLFromBase
= _CFBundleInfoURLFromBase0
;
449 CFURLRef directoryURL
= NULL
;
452 directoryURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, _CFBundleResourcesURLFromBase0
, url
);
453 platformInfoURLFromBase
= _CFBundlePlatformInfoURLFromBase0
;
454 infoURLFromBase
= _CFBundleInfoURLFromBase0
;
455 } else if (1 == version
) {
456 directoryURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, _CFBundleSupportFilesURLFromBase1
, url
);
457 platformInfoURLFromBase
= _CFBundlePlatformInfoURLFromBase1
;
458 infoURLFromBase
= _CFBundleInfoURLFromBase1
;
459 } else if (2 == version
) {
460 directoryURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, _CFBundleSupportFilesURLFromBase2
, url
);
461 platformInfoURLFromBase
= _CFBundlePlatformInfoURLFromBase2
;
462 infoURLFromBase
= _CFBundleInfoURLFromBase2
;
463 } else if (3 == version
) {
464 CFStringRef path
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
465 // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle
467 if (!(CFStringHasSuffix(path
, _CFBundleSupportFilesDirectoryName1
) || CFStringHasSuffix(path
, _CFBundleSupportFilesDirectoryName2
) || CFStringHasSuffix(path
, _CFBundleResourcesDirectoryName
))) {
468 directoryURL
= (CFURLRef
)CFRetain(url
);
469 platformInfoURLFromBase
= _CFBundlePlatformInfoURLFromBase3
;
470 infoURLFromBase
= _CFBundleInfoURLFromBase3
;
476 CFURLRef absoluteURL
;
478 absoluteURL
= CFURLCopyAbsoluteURL(directoryURL
);
479 CFStringRef directoryPath
= CFURLCopyFileSystemPath(absoluteURL
, PLATFORM_PATH_STYLE
);
480 CFRelease(absoluteURL
);
482 __block CFURLRef infoPlistURL
= NULL
;
483 __block CFURLRef platformInfoPlistURL
= NULL
;
485 CFIndex infoPlistLength
= CFStringGetLength(_CFBundleInfoPlistName
);
486 CFIndex platformInfoPlistLength
= CFStringGetLength(_CFBundlePlatformInfoPlistName
);
488 // Look inside this directory for the platform-specific and global Info.plist
489 // For compatability reasons, we support case-insensitive versions of Info.plist. That means that we must do a search of all the file names in the directory so we can compare. Otherwise, perhaps a couple of stats would be more efficient than the readdir.
490 _CFIterateDirectory(directoryPath
, ^Boolean(CFStringRef fileName
, uint8_t fileType
) {
491 // Only do the platform check on platforms where the string is different than the normal one
492 if (_CFBundlePlatformInfoPlistName
!= _CFBundleInfoPlistName
) {
493 if (!platformInfoPlistURL
&& CFStringGetLength(fileName
) == platformInfoPlistLength
&& CFStringCompareWithOptions(fileName
, _CFBundlePlatformInfoPlistName
, CFRangeMake(0, platformInfoPlistLength
), kCFCompareCaseInsensitive
| kCFCompareAnchored
) == kCFCompareEqualTo
) {
494 // Make a URL out of this file
495 platformInfoPlistURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, platformInfoURLFromBase
, url
);
499 if (!infoPlistURL
&& CFStringGetLength(fileName
) == infoPlistLength
&& CFStringCompareWithOptions(fileName
, _CFBundleInfoPlistName
, CFRangeMake(0, infoPlistLength
), kCFCompareCaseInsensitive
| kCFCompareAnchored
) == kCFCompareEqualTo
) {
500 // Make a URL out of this file
501 infoPlistURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, infoURLFromBase
, url
);
504 // If by some chance we have both URLs, just bail early (or just the infoPlistURL on platforms that have no platform-specific name)
505 if (_CFBundlePlatformInfoPlistName
!= _CFBundleInfoPlistName
) {
506 if (infoPlistURL
&& platformInfoPlistURL
) return false;
508 if (infoPlistURL
) return false;
514 CFRelease(directoryPath
);
515 CFRelease(directoryURL
);
517 // Attempt to read in the data from the Info.plist we found - first the platform-specific one.
518 CFDataRef infoData
= NULL
;
519 CFURLRef finalInfoPlistURL
= NULL
;
520 if (platformInfoPlistURL
) {
521 #pragma GCC diagnostic push
522 #pragma GCC diagnostic ignored "-Wdeprecated"
523 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, platformInfoPlistURL
, &infoData
, NULL
, NULL
, NULL
);
524 #pragma GCC diagnostic pop
525 if (infoData
) finalInfoPlistURL
= platformInfoPlistURL
;
528 if (!infoData
&& infoPlistURL
) {
529 #pragma GCC diagnostic push
530 #pragma GCC diagnostic ignored "-Wdeprecated"
531 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, infoPlistURL
, &infoData
, NULL
, NULL
, NULL
);
532 #pragma GCC diagnostic pop
533 if (infoData
) finalInfoPlistURL
= infoPlistURL
;
537 CFErrorRef error
= NULL
;
538 result
= (CFDictionaryRef
)CFPropertyListCreateWithData(alloc
, infoData
, kCFPropertyListMutableContainers
, NULL
, &error
);
540 if (CFDictionaryGetTypeID() == CFGetTypeID(result
)) {
541 CFDictionarySetValue((CFMutableDictionaryRef
)result
, _kCFBundleInfoPlistURLKey
, finalInfoPlistURL
);
547 CFDictionaryRef userInfo
= CFErrorCopyUserInfo(error
);
548 CFLog(kCFLogLevelError
, CFSTR("There was an error parsing the Info.plist for the bundle at URL %@\n %@\n %@"), infoPlistURL
, error
, userInfo
);
549 if (userInfo
) CFRelease(userInfo
);
554 result
= CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
555 CFDictionarySetValue((CFMutableDictionaryRef
)result
, _kCFBundleRawInfoPlistURLKey
, finalInfoPlistURL
);
561 if (platformInfoPlistURL
) CFRelease(platformInfoPlistURL
);
562 if (infoPlistURL
) CFRelease(infoPlistURL
);
566 result
= CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
569 // process ~ipad, ~iphone, etc.
570 _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef
)result
);
575 CF_EXPORT CFDictionaryRef
CFBundleCopyInfoDictionaryForURL(CFURLRef url
) {
576 CFDictionaryRef result
= NULL
;
577 Boolean isDir
= false;
578 if (_CFIsResourceAtURL(url
, &isDir
)) {
580 result
= _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault
, url
, NULL
);
582 result
= _CFBundleCopyInfoDictionaryInExecutable(url
);
585 if (result
&& (0)) CFRetain(result
); // conditionally put on a retain for a Copy function
589 static Boolean
_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc
, CFURLRef url
, CFDictionaryRef infoDict
, UInt32
*packageType
, UInt32
*packageCreator
) {
590 Boolean retVal
= false, hasType
= false, hasCreator
= false, releaseInfoDict
= false;
592 CFDataRef pkgInfoData
= NULL
;
594 // Check for a "real" new bundle
595 tempURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, _CFBundlePkgInfoURLFromBase2
, url
);
596 #pragma GCC diagnostic push
597 #pragma GCC diagnostic ignored "-Wdeprecated"
598 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, tempURL
, &pkgInfoData
, NULL
, NULL
, NULL
);
599 #pragma GCC diagnostic pop
602 tempURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, _CFBundlePkgInfoURLFromBase1
, url
);
603 #pragma GCC diagnostic push
604 #pragma GCC diagnostic ignored "-Wdeprecated"
605 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, tempURL
, &pkgInfoData
, NULL
, NULL
, NULL
);
606 #pragma GCC diagnostic pop
610 // Check for a "pseudo" new bundle
611 tempURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, _CFBundlePseudoPkgInfoURLFromBase
, url
);
612 #pragma GCC diagnostic push
613 #pragma GCC diagnostic ignored "-Wdeprecated"
614 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, tempURL
, &pkgInfoData
, NULL
, NULL
, NULL
);
615 #pragma GCC diagnostic pop
619 // Now, either we have a pkgInfoData or not. If not, then is it because this is a new bundle without one (do we allow this?), or is it dbecause it is an old bundle.
620 // If we allow new bundles to not have a PkgInfo (because they already have the same data in the Info.plist), then we have to go read the info plist which makes failure expensive.
621 // drd: So we assume that a new bundle _must_ have a PkgInfo if they have this data at all, otherwise we manufacture it from the extension.
623 if (pkgInfoData
&& CFDataGetLength(pkgInfoData
) >= (int)(sizeof(UInt32
) * 2)) {
624 UInt32
*pkgInfo
= (UInt32
*)CFDataGetBytePtr(pkgInfoData
);
625 if (packageType
) *packageType
= CFSwapInt32BigToHost(pkgInfo
[0]);
626 if (packageCreator
) *packageCreator
= CFSwapInt32BigToHost(pkgInfo
[1]);
627 retVal
= hasType
= hasCreator
= true;
629 if (pkgInfoData
) CFRelease(pkgInfoData
);
632 infoDict
= _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault
, url
, NULL
);
633 releaseInfoDict
= true;
636 CFStringRef typeString
= (CFStringRef
)CFDictionaryGetValue(infoDict
, _kCFBundlePackageTypeKey
), creatorString
= (CFStringRef
)CFDictionaryGetValue(infoDict
, _kCFBundleSignatureKey
);
638 CFIndex usedBufLen
= 0;
639 if (typeString
&& CFGetTypeID(typeString
) == CFStringGetTypeID() && CFStringGetLength(typeString
) == 4 && 4 == CFStringGetBytes(typeString
, CFRangeMake(0, 4), kCFStringEncodingMacRoman
, 0, false, (UInt8
*)&tmp
, 4, &usedBufLen
) && 4 == usedBufLen
) {
640 if (packageType
) *packageType
= CFSwapInt32BigToHost(tmp
);
641 retVal
= hasType
= true;
643 if (creatorString
&& CFGetTypeID(creatorString
) == CFStringGetTypeID() && CFStringGetLength(creatorString
) == 4 && 4 == CFStringGetBytes(creatorString
, CFRangeMake(0, 4), kCFStringEncodingMacRoman
, 0, false, (UInt8
*)&tmp
, 4, &usedBufLen
) && 4 == usedBufLen
) {
644 if (packageCreator
) *packageCreator
= CFSwapInt32BigToHost(tmp
);
645 retVal
= hasCreator
= true;
647 if (releaseInfoDict
&& !(0)) CFRelease(infoDict
);
650 if (!hasType
|| !hasCreator
) {
651 // If this looks like a bundle then manufacture the type and creator.
652 if (retVal
|| _CFBundleURLLooksLikeBundle(url
)) {
653 if (packageCreator
&& !hasCreator
) *packageCreator
= 0x3f3f3f3f; // '????'
654 if (packageType
&& !hasType
) {
656 UniChar buff
[CFMaxPathSize
];
657 CFIndex strLen
, startOfExtension
;
658 CFURLRef absoluteURL
;
660 // Detect "app", "debug", "profile", or "framework" extensions
661 absoluteURL
= CFURLCopyAbsoluteURL(url
);
662 urlStr
= CFURLCopyFileSystemPath(absoluteURL
, PLATFORM_PATH_STYLE
);
663 CFRelease(absoluteURL
);
664 strLen
= CFStringGetLength(urlStr
);
665 if (strLen
> CFMaxPathSize
) strLen
= CFMaxPathSize
;
666 CFStringGetCharacters(urlStr
, CFRangeMake(0, strLen
), buff
);
668 startOfExtension
= _CFStartOfPathExtension(buff
, strLen
);
669 if ((strLen
- startOfExtension
== 4 || strLen
- startOfExtension
== 5) && buff
[startOfExtension
] == (UniChar
)'.' && buff
[startOfExtension
+1] == (UniChar
)'a' && buff
[startOfExtension
+2] == (UniChar
)'p' && buff
[startOfExtension
+3] == (UniChar
)'p' && (strLen
- startOfExtension
== 4 || buff
[startOfExtension
+4] == (UniChar
)PATH_SEP
)) {
671 *packageType
= 0x4150504c; // 'APPL'
672 } else if ((strLen
- startOfExtension
== 6 || strLen
- startOfExtension
== 7) && buff
[startOfExtension
] == (UniChar
)'.' && buff
[startOfExtension
+1] == (UniChar
)'d' && buff
[startOfExtension
+2] == (UniChar
)'e' && buff
[startOfExtension
+3] == (UniChar
)'b' && buff
[startOfExtension
+4] == (UniChar
)'u' && buff
[startOfExtension
+5] == (UniChar
)'g' && (strLen
- startOfExtension
== 6 || buff
[startOfExtension
+6] == (UniChar
)PATH_SEP
)) {
673 // This is an app (debug version)
674 *packageType
= 0x4150504c; // 'APPL'
675 } else if ((strLen
- startOfExtension
== 8 || strLen
- startOfExtension
== 9) && buff
[startOfExtension
] == (UniChar
)'.' && buff
[startOfExtension
+1] == (UniChar
)'p' && buff
[startOfExtension
+2] == (UniChar
)'r' && buff
[startOfExtension
+3] == (UniChar
)'o' && buff
[startOfExtension
+4] == (UniChar
)'f' && buff
[startOfExtension
+5] == (UniChar
)'i' && buff
[startOfExtension
+6] == (UniChar
)'l' && buff
[startOfExtension
+7] == (UniChar
)'e' && (strLen
- startOfExtension
== 8 || buff
[startOfExtension
+8] == (UniChar
)PATH_SEP
)) {
676 // This is an app (profile version)
677 *packageType
= 0x4150504c; // 'APPL'
678 } else if ((strLen
- startOfExtension
== 8 || strLen
- startOfExtension
== 9) && buff
[startOfExtension
] == (UniChar
)'.' && buff
[startOfExtension
+1] == (UniChar
)'s' && buff
[startOfExtension
+2] == (UniChar
)'e' && buff
[startOfExtension
+3] == (UniChar
)'r' && buff
[startOfExtension
+4] == (UniChar
)'v' && buff
[startOfExtension
+5] == (UniChar
)'i' && buff
[startOfExtension
+6] == (UniChar
)'c' && buff
[startOfExtension
+7] == (UniChar
)'e' && (strLen
- startOfExtension
== 8 || buff
[startOfExtension
+8] == (UniChar
)PATH_SEP
)) {
680 *packageType
= 0x4150504c; // 'APPL'
681 } else if ((strLen
- startOfExtension
== 10 || strLen
- startOfExtension
== 11) && buff
[startOfExtension
] == (UniChar
)'.' && buff
[startOfExtension
+1] == (UniChar
)'f' && buff
[startOfExtension
+2] == (UniChar
)'r' && buff
[startOfExtension
+3] == (UniChar
)'a' && buff
[startOfExtension
+4] == (UniChar
)'m' && buff
[startOfExtension
+5] == (UniChar
)'e' && buff
[startOfExtension
+6] == (UniChar
)'w' && buff
[startOfExtension
+7] == (UniChar
)'o' && buff
[startOfExtension
+8] == (UniChar
)'r' && buff
[startOfExtension
+9] == (UniChar
)'k' && (strLen
- startOfExtension
== 10 || buff
[startOfExtension
+10] == (UniChar
)PATH_SEP
)) {
682 // This is a framework
683 *packageType
= 0x464d574b; // 'FMWK'
685 // Default to BNDL for generic bundle
686 *packageType
= 0x424e444c; // 'BNDL'
695 CF_EXPORT Boolean
_CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc
, CFURLRef url
, UInt32
*packageType
, UInt32
*packageCreator
) {
696 return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc
, url
, NULL
, packageType
, packageCreator
);
699 CF_EXPORT
void CFBundleGetPackageInfo(CFBundleRef bundle
, UInt32
*packageType
, UInt32
*packageCreator
) {
700 CFURLRef bundleURL
= CFBundleCopyBundleURL(bundle
);
701 if (!_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(kCFAllocatorSystemDefault
, bundleURL
, CFBundleGetInfoDictionary(bundle
), packageType
, packageCreator
)) {
702 if (packageType
) *packageType
= 0x424e444c; // 'BNDL'
703 if (packageCreator
) *packageCreator
= 0x3f3f3f3f; // '????'
705 if (bundleURL
) CFRelease(bundleURL
);
708 CF_EXPORT Boolean
CFBundleGetPackageInfoInDirectory(CFURLRef url
, UInt32
*packageType
, UInt32
*packageCreator
) {
709 return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault
, url
, packageType
, packageCreator
);
712 CFDictionaryRef
CFBundleCopyInfoDictionaryInDirectory(CFURLRef url
) {
713 CFDictionaryRef dict
= _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault
, url
, NULL
);
717 // The Info.plist should NOT be mutated after being created. If there is any fixing up of the info dictionary to do, do it here.
718 // Call with bundle lock
719 static void _CFBundleInfoPlistFixupInfoDictionary(CFBundleRef bundle
, CFMutableDictionaryRef infoDict
) {
721 CFTypeRef unknownVersionValue
= CFDictionaryGetValue(infoDict
, _kCFBundleNumericVersionKey
);
725 if (!unknownVersionValue
) unknownVersionValue
= CFDictionaryGetValue(infoDict
, kCFBundleVersionKey
);
726 if (unknownVersionValue
) {
727 if (CFGetTypeID(unknownVersionValue
) == CFStringGetTypeID()) {
728 // Convert a string version number into a numeric one.
729 vers
= _CFVersionNumberFromString((CFStringRef
)unknownVersionValue
);
731 versNum
= CFNumberCreate(CFGetAllocator(bundle
), kCFNumberSInt32Type
, &vers
);
732 CFDictionarySetValue(infoDict
, _kCFBundleNumericVersionKey
, versNum
);
734 } else if (CFGetTypeID(unknownVersionValue
) == CFNumberGetTypeID()) {
735 // Nothing to do here
737 CFDictionaryRemoveValue((CFMutableDictionaryRef
)infoDict
, _kCFBundleNumericVersionKey
);
742 CFDictionaryRef
CFBundleGetInfoDictionary(CFBundleRef bundle
) {
743 __CFLock(&bundle
->_lock
);
744 if (!bundle
->_infoDict
) {
745 bundle
->_infoDict
= _CFBundleCopyInfoDictionaryInDirectoryWithVersion(kCFAllocatorSystemDefault
, bundle
->_url
, bundle
->_version
);
747 // Add or fixup any keys that will be expected later
748 if (bundle
->_infoDict
) _CFBundleInfoPlistFixupInfoDictionary(bundle
, (CFMutableDictionaryRef
)bundle
->_infoDict
);
750 __CFUnlock(&bundle
->_lock
);
752 return bundle
->_infoDict
;
755 CFDictionaryRef
_CFBundleGetLocalInfoDictionary(CFBundleRef bundle
) {
756 return CFBundleGetLocalInfoDictionary(bundle
);
759 CFDictionaryRef
CFBundleGetLocalInfoDictionary(CFBundleRef bundle
) {
760 CFDictionaryRef localInfoDict
= NULL
;
761 __CFLock(&bundle
->_lock
);
762 localInfoDict
= bundle
->_localInfoDict
;
763 if (!localInfoDict
) {
764 // To avoid keeping the spin lock for too long, let go of it here while we create a new dictionary. We'll relock later to set the value. If it turns out that we have already created another local info dictionary in the meantime, then we'll take care of it then.
765 __CFUnlock(&bundle
->_lock
);
766 CFURLRef url
= CFBundleCopyResourceURL(bundle
, _CFBundleLocalInfoName
, _CFBundleStringTableType
, NULL
);
770 CFStringRef errStr
= NULL
;
772 #pragma GCC diagnostic push
773 #pragma GCC diagnostic ignored "-Wdeprecated"
774 if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault
, url
, &data
, NULL
, NULL
, &errCode
)) {
775 localInfoDict
= (CFDictionaryRef
)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault
, data
, kCFPropertyListMutableContainers
, &errStr
);
776 if (errStr
) CFRelease(errStr
);
777 if (localInfoDict
&& CFDictionaryGetTypeID() != CFGetTypeID(localInfoDict
)) {
778 CFRelease(localInfoDict
);
779 localInfoDict
= NULL
;
783 #pragma GCC diagnostic pop
786 if (localInfoDict
) _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef
)localInfoDict
);
787 // remain locked here until we exit the if statement.
788 __CFLock(&bundle
->_lock
);
789 if (!bundle
->_localInfoDict
) {
790 // Still have no info dictionary, so set it
791 bundle
->_localInfoDict
= localInfoDict
;
793 // Oops, some other thread created an info dictionary too. We'll just release this one and use that one.
794 if (localInfoDict
) CFRelease(localInfoDict
);
795 localInfoDict
= bundle
->_localInfoDict
;
798 __CFUnlock(&bundle
->_lock
);
800 return localInfoDict
;
803 CFPropertyListRef
_CFBundleGetValueForInfoKey(CFBundleRef bundle
, CFStringRef key
) {
804 return (CFPropertyListRef
)CFBundleGetValueForInfoDictionaryKey(bundle
, key
);
807 CFTypeRef
CFBundleGetValueForInfoDictionaryKey(CFBundleRef bundle
, CFStringRef key
) {
808 // Look in InfoPlist.strings first. Then look in Info.plist
809 CFTypeRef result
= NULL
;
811 CFDictionaryRef dict
= CFBundleGetLocalInfoDictionary(bundle
);
812 if (dict
) result
= CFDictionaryGetValue(dict
, key
);
814 dict
= CFBundleGetInfoDictionary(bundle
);
815 if (dict
) result
= CFDictionaryGetValue(dict
, key
);
821 CFStringRef
CFBundleGetIdentifier(CFBundleRef bundle
) {
822 CFStringRef bundleID
= NULL
;
823 CFDictionaryRef infoDict
= CFBundleGetInfoDictionary(bundle
);
824 if (infoDict
) bundleID
= (CFStringRef
)CFDictionaryGetValue(infoDict
, kCFBundleIdentifierKey
);
829 static void __addPlatformAndProductNamesToKeys(const void *value
, void *context
) {
830 CFMutableSetRef newKeys
= (CFMutableSetRef
)context
;
831 CFStringRef key
= (CFStringRef
)value
;
832 CFStringRef firstPartOfKey
= NULL
;
833 CFStringRef restOfKey
= NULL
;
835 // Find the first ':'
837 Boolean success
= CFStringFindWithOptions(key
, CFSTR(":"), CFRangeMake(0, CFStringGetLength(key
)), 0, &range
);
839 firstPartOfKey
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, key
, CFRangeMake(0, range
.location
));
840 restOfKey
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, key
, CFRangeMake(range
.location
+ 1, CFStringGetLength(key
) - range
.location
- 1));
842 firstPartOfKey
= (CFStringRef
)CFRetain(key
);
845 // only apply product and platform to top-level key
846 CFStringRef newKeyWithPlatform
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@-%@%@%@"), firstPartOfKey
, _CFGetPlatformName(), restOfKey
? CFSTR(":") : CFSTR(""), restOfKey
? restOfKey
: CFSTR(""));
847 CFStringRef newKeyWithProduct
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@~%@%@%@"), firstPartOfKey
, _CFGetProductName(), restOfKey
? CFSTR(":") : CFSTR(""), restOfKey
? restOfKey
: CFSTR(""));
848 CFStringRef newKeyWithProductAndPlatform
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("%@-%@~%@%@%@"), firstPartOfKey
, _CFGetPlatformName(), _CFGetProductName(), restOfKey
? CFSTR(":") : CFSTR(""), restOfKey
? restOfKey
: CFSTR(""));
850 CFSetAddValue(newKeys
, key
);
851 CFSetAddValue(newKeys
, newKeyWithPlatform
);
852 CFSetAddValue(newKeys
, newKeyWithProduct
);
853 CFSetAddValue(newKeys
, newKeyWithProductAndPlatform
);
855 if (firstPartOfKey
) CFRelease(firstPartOfKey
);
856 if (restOfKey
) CFRelease(restOfKey
);
857 CFRelease(newKeyWithPlatform
);
858 CFRelease(newKeyWithProduct
);
859 CFRelease(newKeyWithProductAndPlatform
);
862 // from CFUtilities.c
863 CF_PRIVATE Boolean
_CFReadMappedFromFile(CFStringRef path
, Boolean map
, Boolean uncached
, void **outBytes
, CFIndex
*outLength
, CFErrorRef
*errorPtr
);
865 // implementation of below functions - takes URL as parameter
866 static CFPropertyListRef
_CFBundleCreateFilteredInfoPlistWithURL(CFURLRef infoPlistURL
, CFSetRef keyPaths
, _CFBundleFilteredPlistOptions options
) {
867 CFPropertyListRef result
= NULL
;
869 if (!infoPlistURL
) return CFDictionaryCreate(kCFAllocatorSystemDefault
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
871 CFURLRef absoluteURL
= CFURLCopyAbsoluteURL(infoPlistURL
);
872 CFStringRef filePath
= CFURLCopyFileSystemPath(absoluteURL
, PLATFORM_PATH_STYLE
);
873 CFRelease(absoluteURL
);
875 if (!filePath
) return CFDictionaryCreate(kCFAllocatorSystemDefault
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
879 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
880 Boolean mapped
= options
& _CFBundleFilteredPlistMemoryMapped
? true : false;
882 Boolean mapped
= false;
884 Boolean success
= _CFReadMappedFromFile(filePath
, mapped
, false, &bytes
, &length
, NULL
);
886 if (!success
) return CFDictionaryCreate(kCFAllocatorSystemDefault
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
888 CFDataRef infoPlistData
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, (const UInt8
*)bytes
, length
, kCFAllocatorNull
);
889 // We need to include all possible variants of the platform/product combo as possible keys.
890 CFMutableSetRef newKeyPaths
= CFSetCreateMutable(kCFAllocatorSystemDefault
, CFSetGetCount(keyPaths
), &kCFTypeSetCallBacks
);
891 CFSetApplyFunction(keyPaths
, __addPlatformAndProductNamesToKeys
, newKeyPaths
);
893 success
= _CFPropertyListCreateFiltered(kCFAllocatorSystemDefault
, infoPlistData
, kCFPropertyListMutableContainers
, newKeyPaths
, &result
, NULL
);
895 if (!success
|| !result
) {
896 result
= CFDictionaryCreate(kCFAllocatorSystemDefault
, NULL
, NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
898 _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef
)result
);
901 CFRelease(newKeyPaths
);
902 CFRelease(infoPlistData
);
904 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
905 munmap(bytes
, length
);
914 // Returns a subset of the bundle's property list, only including the keyPaths in the CFSet. If the top level object is not a dictionary, you will get back an empty dictionary as the result. If the Info.plist does not exist or could not be parsed, you will get back an empty dictionary.
915 CF_EXPORT CFPropertyListRef
_CFBundleCreateFilteredInfoPlist(CFBundleRef bundle
, CFSetRef keyPaths
, _CFBundleFilteredPlistOptions options
) {
916 CFURLRef infoPlistURL
= _CFBundleCopyInfoPlistURL(bundle
);
917 CFPropertyListRef result
= _CFBundleCreateFilteredInfoPlistWithURL(infoPlistURL
, keyPaths
, options
);
918 if (infoPlistURL
) CFRelease(infoPlistURL
);
922 CF_EXPORT CFPropertyListRef
_CFBundleCreateFilteredLocalizedInfoPlist(CFBundleRef bundle
, CFSetRef keyPaths
, CFStringRef localizationName
, _CFBundleFilteredPlistOptions options
) {
923 CFURLRef infoPlistURL
= CFBundleCopyResourceURLForLocalization(bundle
, _CFBundleLocalInfoName
, _CFBundleStringTableType
, NULL
, localizationName
);
924 CFPropertyListRef result
= _CFBundleCreateFilteredInfoPlistWithURL(infoPlistURL
, keyPaths
, options
);
925 if (infoPlistURL
) CFRelease(infoPlistURL
);
929 CF_EXPORT CFURLRef
_CFBundleCopyInfoPlistURL(CFBundleRef bundle
) {
930 CFDictionaryRef infoDict
= CFBundleGetInfoDictionary(bundle
);
931 CFURLRef url
= (CFURLRef
)CFDictionaryGetValue(infoDict
, _kCFBundleInfoPlistURLKey
);
932 if (!url
) url
= (CFURLRef
)CFDictionaryGetValue(infoDict
, _kCFBundleRawInfoPlistURLKey
);
933 return (url
? (CFURLRef
)CFRetain(url
) : NULL
);