/ CFBundle_InfoPlist.c
CFBundle_InfoPlist.c
1 /* 2 * Copyright (c) 2015 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 // 25 // CFBundle_InfoPlist.c 26 // CoreFoundation 27 // 28 // Created by Tony Parker on 5/30/12. 29 // 30 // 31 32 #include <CoreFoundation/CFBundle.h> 33 #include <CoreFoundation/CFNumber.h> 34 #include "CFBundle_Internal.h" 35 #include "CFByteOrder.h" 36 #include "CFURLAccess.h" 37 38 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_EMBEDDED_MINI 39 #include <dirent.h> 40 #include <sys/sysctl.h> 41 #include <sys/mman.h> 42 #endif 43 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__ 46 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" }; 50 51 #define _CFBundleNumberOfProducts 3 52 static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { NULL, NULL, NULL }; 53 static const char *_CFBundleSupportedProductStrings[_CFBundleNumberOfProducts] = { "iphone", "ipod", "ipad" }; 54 55 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3 56 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { NULL, NULL, NULL }; 57 static const char *_CFBundleSupportediPhoneOSPlatformProductStrings[_CFBundleNumberOfiPhoneOSPlatformProducts] = { "iphone", "ipod", "ipad" }; 58 59 CF_PRIVATE void _CFBundleResourcesInitialize() { 60 for (unsigned int i = 0; i < _CFBundleNumberOfPlatforms; i++) _CFBundleSupportedPlatforms[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedPlatformStrings[i], kCFStringEncodingUTF8); 61 62 for (unsigned int i = 0; i < _CFBundleNumberOfProducts; i++) _CFBundleSupportedProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedProductStrings[i], kCFStringEncodingUTF8); 63 64 for (unsigned int i = 0; i < _CFBundleNumberOfiPhoneOSPlatformProducts; i++) _CFBundleSupportediPhoneOSPlatformProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportediPhoneOSPlatformProductStrings[i], kCFStringEncodingUTF8); 65 } 66 67 #else 68 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") }; 73 #else 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") }; 77 #endif 78 79 #define _CFBundleNumberOfProducts 3 80 static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") }; 81 82 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3 83 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") }; 84 85 CF_PRIVATE void _CFBundleResourcesInitialize() { } 86 #endif 87 88 #pragma mark - 89 #pragma mark Product and Platform Getters - Exported 90 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. 101 } 102 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"); 109 } else { 110 char buffer[256]; 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"); 121 } else { 122 const char *env = __CFgetenv("IPHONE_SIMULATOR_DEVICE"); 123 if (env) { 124 if (0 == strcmp(env, "iPhone")) { 125 _cfBundlePlatform = CFSTR("iphone"); 126 } else if (0 == strcmp(env, "iPad")) { 127 _cfBundlePlatform = CFSTR("ipad"); 128 } else { 129 // fallback, unrecognized IPHONE_SIMULATOR_DEVICE 130 } 131 } else { 132 // fallback, unrecognized hw.machine and no IPHONE_SIMULATOR_DEVICE 133 } 134 } 135 } 136 } 137 if (!_cfBundlePlatform) _cfBundlePlatform = CFSTR("iphone"); // fallback 138 } 139 return _cfBundlePlatform; 140 #endif 141 return CFSTR(""); 142 } 143 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; 160 #else 161 #error Unknown or unspecified DEPLOYMENT_TARGET 162 #endif 163 } 164 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 171 return CFSTR(""); 172 #else 173 #error Unknown or unspecified DEPLOYMENT_TARGET 174 #endif 175 } 176 177 #pragma mark - 178 #pragma mark Product and Platform Suffix Processing - Internal 179 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; 184 } 185 return false; 186 } 187 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)) { 192 return true; 193 } 194 } 195 return false; 196 } 197 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; 202 } 203 return false; 204 } 205 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)) { 210 return true; 211 } 212 } 213 return false; 214 } 215 216 static Boolean _isValidiPhoneOSPlatformProductSuffix(CFStringRef suffix) { 217 for (CFIndex idx = 0; idx < _CFBundleNumberOfiPhoneOSPlatformProducts; idx++) { 218 if (CFEqual(suffix, _CFBundleSupportediPhoneOSPlatformProducts[idx])) return true; 219 } 220 return false; 221 } 222 223 static Boolean _isValidPlatformAndProductSuffixPair(CFStringRef platform, CFStringRef product) { 224 if (!platform && !product) return true; 225 if (!platform) { 226 return _isValidProductSuffix(product); 227 } 228 if (!product) { 229 return _isValidPlatformSuffix(platform); 230 } 231 if (CFEqual(platform, _CFBundleiPhoneOSPlatformName)) { 232 return _isValidiPhoneOSPlatformProductSuffix(product); 233 } 234 return false; 235 } 236 237 static Boolean _isBlacklistedKey(CFStringRef keyName) { 238 #if __CONSTANT_STRINGS__ 239 #define _CFBundleNumberOfBlacklistedInfoDictionaryKeys 2 240 static const CFStringRef _CFBundleBlacklistedInfoDictionaryKeys[_CFBundleNumberOfBlacklistedInfoDictionaryKeys] = { CFSTR("CFBundleExecutable"), CFSTR("CFBundleIdentifier") }; 241 242 for (CFIndex idx = 0; idx < _CFBundleNumberOfBlacklistedInfoDictionaryKeys; idx++) { 243 if (CFEqual(keyName, _CFBundleBlacklistedInfoDictionaryKeys[idx])) return true; 244 } 245 #endif 246 return false; 247 } 248 249 static Boolean _isOverrideKey(CFStringRef fullKey, CFStringRef *outBaseKey, CFStringRef *outPlatformSuffix, CFStringRef *outProductSuffix) { 250 if (outBaseKey) { 251 *outBaseKey = NULL; 252 } 253 if (outPlatformSuffix) { 254 *outPlatformSuffix = NULL; 255 } 256 if (outProductSuffix) { 257 *outProductSuffix = NULL; 258 } 259 if (!fullKey) 260 return false; 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; 266 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; 274 } 275 if (tildeRange.location != kCFNotFound) { 276 productRange.location = tildeRange.location + tildeRange.length; 277 productRange.length = strLen - productRange.location; 278 } 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; 282 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); 286 287 if (result) { 288 if (outBaseKey) { 289 *outBaseKey = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, fullKey, baseKeyRange); 290 } 291 if (outPlatformSuffix) { 292 *outPlatformSuffix = platform; 293 } else { 294 if (platform && !(0)) CFRelease(platform); 295 } 296 if (outProductSuffix) { 297 *outProductSuffix = product; 298 } else { 299 if (product && !(0)) CFRelease(product); 300 } 301 } else { 302 if (platform && !(0)) CFRelease(platform); 303 if (product && !(0)) CFRelease(product); 304 } 305 return result; 306 } 307 308 static Boolean _isCurrentPlatformAndProduct(CFStringRef platform, CFStringRef product) { 309 if (!platform && !product) return true; 310 if (!platform) { 311 return CFEqual(_CFGetProductName(), product); 312 } 313 if (!product) { 314 return CFEqual(_CFGetPlatformName(), platform); 315 } 316 317 return CFEqual(_CFGetProductName(), product) && CFEqual(_CFGetPlatformName(), platform); 318 } 319 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()); 325 326 CFIndex count = CFDictionaryGetCount(dict); 327 328 if (count > 0) { 329 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); 330 CFTypeRef *values = &(keys[count]); 331 332 CFDictionaryGetKeysAndValues(dict, keys, values); 333 for (CFIndex idx = 0; idx < count; idx++) { 334 if (CFEqual(keys[idx], keyNameWithBoth)) { 335 CFArrayAppendValue(overrides, keys[idx]); 336 break; 337 } 338 } 339 for (CFIndex idx = 0; idx < count; idx++) { 340 if (CFEqual(keys[idx], keyNameWithProduct)) { 341 CFArrayAppendValue(overrides, keys[idx]); 342 break; 343 } 344 } 345 for (CFIndex idx = 0; idx < count; idx++) { 346 if (CFEqual(keys[idx], keyNameWithPlatform)) { 347 CFArrayAppendValue(overrides, keys[idx]); 348 break; 349 } 350 } 351 for (CFIndex idx = 0; idx < count; idx++) { 352 if (CFEqual(keys[idx], keyName)) { 353 CFArrayAppendValue(overrides, keys[idx]); 354 break; 355 } 356 } 357 358 CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys); 359 } 360 361 CFRelease(keyNameWithProduct); 362 CFRelease(keyNameWithPlatform); 363 CFRelease(keyNameWithBoth); 364 365 return overrides; 366 } 367 368 CF_PRIVATE void _CFBundleInfoPlistProcessInfoDictionary(CFMutableDictionaryRef dict) { 369 // Defensive programming 370 if (!dict) return; 371 372 CFIndex count = CFDictionaryGetCount(dict); 373 374 if (count > 0) { 375 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0); 376 CFTypeRef *values = &(keys[count]); 377 CFMutableArrayRef guard = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); 378 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); 387 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); 390 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)); 394 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); 400 } 401 } else { 402 CFDictionaryRemoveValue(dict, keys[idx]); 403 } 404 405 406 if (keyPlatformSuffix) CFRelease(keyPlatformSuffix); 407 if (keyProductSuffix) CFRelease(keyProductSuffix); 408 CFRelease(keyName); 409 if (keysForBaseKey) CFRelease(keysForBaseKey); 410 } 411 } 412 413 CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys); 414 CFRelease(guard); 415 } 416 } 417 418 #pragma mark - 419 #pragma mark Info Plist Functions 420 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; 425 426 if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) { 427 CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true); 428 if (!newURL) newURL = (CFURLRef)CFRetain(url); 429 430 localVersion = _CFBundleGetBundleVersionForURL(newURL); 431 432 dict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc, newURL, localVersion); 433 CFRelease(newURL); 434 } 435 if (version) *version = localVersion; 436 return dict; 437 } 438 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; 442 443 CFDictionaryRef result = NULL; 444 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; 450 451 if (0 == version) { 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 466 if (path) { 467 if (!(CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(path, _CFBundleResourcesDirectoryName))) { 468 directoryURL = (CFURLRef)CFRetain(url); 469 platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase3; 470 infoURLFromBase = _CFBundleInfoURLFromBase3; 471 } 472 CFRelease(path); 473 } 474 } 475 476 CFURLRef absoluteURL; 477 if (directoryURL) { 478 absoluteURL = CFURLCopyAbsoluteURL(directoryURL); 479 CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 480 CFRelease(absoluteURL); 481 482 __block CFURLRef infoPlistURL = NULL; 483 __block CFURLRef platformInfoPlistURL = NULL; 484 485 CFIndex infoPlistLength = CFStringGetLength(_CFBundleInfoPlistName); 486 CFIndex platformInfoPlistLength = CFStringGetLength(_CFBundlePlatformInfoPlistName); 487 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); 496 } 497 } 498 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); 502 } 503 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; 507 } else { 508 if (infoPlistURL) return false; 509 } 510 511 return true; 512 }); 513 514 CFRelease(directoryPath); 515 CFRelease(directoryURL); 516 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; 526 } 527 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; 534 } 535 536 if (infoData) { 537 CFErrorRef error = NULL; 538 result = (CFDictionaryRef)CFPropertyListCreateWithData(alloc, infoData, kCFPropertyListMutableContainers, NULL, &error); 539 if (result) { 540 if (CFDictionaryGetTypeID() == CFGetTypeID(result)) { 541 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, finalInfoPlistURL); 542 } else { 543 CFRelease(result); 544 result = NULL; 545 } 546 } else if (error) { 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); 550 CFRelease(error); 551 } 552 553 if (!result) { 554 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 555 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleRawInfoPlistURLKey, finalInfoPlistURL); 556 } 557 558 CFRelease(infoData); 559 } 560 561 if (platformInfoPlistURL) CFRelease(platformInfoPlistURL); 562 if (infoPlistURL) CFRelease(infoPlistURL); 563 } 564 565 if (!result) { 566 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 567 } 568 569 // process ~ipad, ~iphone, etc. 570 _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef)result); 571 572 return result; 573 } 574 575 CF_EXPORT CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) { 576 CFDictionaryRef result = NULL; 577 Boolean isDir = false; 578 if (_CFIsResourceAtURL(url, &isDir)) { 579 if (isDir) { 580 result = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL); 581 } else { 582 result = _CFBundleCopyInfoDictionaryInExecutable(url); 583 } 584 } 585 if (result && (0)) CFRetain(result); // conditionally put on a retain for a Copy function 586 return result; 587 } 588 589 static Boolean _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc, CFURLRef url, CFDictionaryRef infoDict, UInt32 *packageType, UInt32 *packageCreator) { 590 Boolean retVal = false, hasType = false, hasCreator = false, releaseInfoDict = false; 591 CFURLRef tempURL; 592 CFDataRef pkgInfoData = NULL; 593 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 600 CFRelease(tempURL); 601 if (!pkgInfoData) { 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 607 CFRelease(tempURL); 608 } 609 if (!pkgInfoData) { 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 616 CFRelease(tempURL); 617 } 618 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. 622 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; 628 } 629 if (pkgInfoData) CFRelease(pkgInfoData); 630 if (!retVal) { 631 if (!infoDict) { 632 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL); 633 releaseInfoDict = true; 634 } 635 if (infoDict) { 636 CFStringRef typeString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleSignatureKey); 637 UInt32 tmp; 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; 642 } 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; 646 } 647 if (releaseInfoDict && !(0)) CFRelease(infoDict); 648 } 649 } 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) { 655 CFStringRef urlStr; 656 UniChar buff[CFMaxPathSize]; 657 CFIndex strLen, startOfExtension; 658 CFURLRef absoluteURL; 659 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); 667 CFRelease(urlStr); 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)) { 670 // This is an app 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)) { 679 // This is a service 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' 684 } else { 685 // Default to BNDL for generic bundle 686 *packageType = 0x424e444c; // 'BNDL' 687 } 688 } 689 retVal = true; 690 } 691 } 692 return retVal; 693 } 694 695 CF_EXPORT Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) { 696 return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc, url, NULL, packageType, packageCreator); 697 } 698 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; // '????' 704 } 705 if (bundleURL) CFRelease(bundleURL); 706 } 707 708 CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) { 709 return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault, url, packageType, packageCreator); 710 } 711 712 CFDictionaryRef CFBundleCopyInfoDictionaryInDirectory(CFURLRef url) { 713 CFDictionaryRef dict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL); 714 return dict; 715 } 716 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) { 720 // Version number 721 CFTypeRef unknownVersionValue = CFDictionaryGetValue(infoDict, _kCFBundleNumericVersionKey); 722 CFNumberRef versNum; 723 UInt32 vers = 0; 724 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); 730 731 versNum = CFNumberCreate(CFGetAllocator(bundle), kCFNumberSInt32Type, &vers); 732 CFDictionarySetValue(infoDict, _kCFBundleNumericVersionKey, versNum); 733 CFRelease(versNum); 734 } else if (CFGetTypeID(unknownVersionValue) == CFNumberGetTypeID()) { 735 // Nothing to do here 736 } else { 737 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleNumericVersionKey); 738 } 739 } 740 } 741 742 CFDictionaryRef CFBundleGetInfoDictionary(CFBundleRef bundle) { 743 __CFLock(&bundle->_lock); 744 if (!bundle->_infoDict) { 745 bundle->_infoDict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(kCFAllocatorSystemDefault, bundle->_url, bundle->_version); 746 747 // Add or fixup any keys that will be expected later 748 if (bundle->_infoDict) _CFBundleInfoPlistFixupInfoDictionary(bundle, (CFMutableDictionaryRef)bundle->_infoDict); 749 } 750 __CFUnlock(&bundle->_lock); 751 752 return bundle->_infoDict; 753 } 754 755 CFDictionaryRef _CFBundleGetLocalInfoDictionary(CFBundleRef bundle) { 756 return CFBundleGetLocalInfoDictionary(bundle); 757 } 758 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); 767 if (url) { 768 CFDataRef data; 769 SInt32 errCode; 770 CFStringRef errStr = NULL; 771 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; 780 } 781 CFRelease(data); 782 } 783 #pragma GCC diagnostic pop 784 CFRelease(url); 785 } 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; 792 } else { 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; 796 } 797 } 798 __CFUnlock(&bundle->_lock); 799 800 return localInfoDict; 801 } 802 803 CFPropertyListRef _CFBundleGetValueForInfoKey(CFBundleRef bundle, CFStringRef key) { 804 return (CFPropertyListRef)CFBundleGetValueForInfoDictionaryKey(bundle, key); 805 } 806 807 CFTypeRef CFBundleGetValueForInfoDictionaryKey(CFBundleRef bundle, CFStringRef key) { 808 // Look in InfoPlist.strings first. Then look in Info.plist 809 CFTypeRef result = NULL; 810 if (bundle && key) { 811 CFDictionaryRef dict = CFBundleGetLocalInfoDictionary(bundle); 812 if (dict) result = CFDictionaryGetValue(dict, key); 813 if (!result) { 814 dict = CFBundleGetInfoDictionary(bundle); 815 if (dict) result = CFDictionaryGetValue(dict, key); 816 } 817 } 818 return result; 819 } 820 821 CFStringRef CFBundleGetIdentifier(CFBundleRef bundle) { 822 CFStringRef bundleID = NULL; 823 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 824 if (infoDict) bundleID = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleIdentifierKey); 825 return bundleID; 826 } 827 828 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; 834 835 // Find the first ':' 836 CFRange range; 837 Boolean success = CFStringFindWithOptions(key, CFSTR(":"), CFRangeMake(0, CFStringGetLength(key)), 0, &range); 838 if (success) { 839 firstPartOfKey = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, key, CFRangeMake(0, range.location)); 840 restOfKey = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, key, CFRangeMake(range.location + 1, CFStringGetLength(key) - range.location - 1)); 841 } else { 842 firstPartOfKey = (CFStringRef)CFRetain(key); 843 } 844 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("")); 849 850 CFSetAddValue(newKeys, key); 851 CFSetAddValue(newKeys, newKeyWithPlatform); 852 CFSetAddValue(newKeys, newKeyWithProduct); 853 CFSetAddValue(newKeys, newKeyWithProductAndPlatform); 854 855 if (firstPartOfKey) CFRelease(firstPartOfKey); 856 if (restOfKey) CFRelease(restOfKey); 857 CFRelease(newKeyWithPlatform); 858 CFRelease(newKeyWithProduct); 859 CFRelease(newKeyWithProductAndPlatform); 860 } 861 862 // from CFUtilities.c 863 CF_PRIVATE Boolean _CFReadMappedFromFile(CFStringRef path, Boolean map, Boolean uncached, void **outBytes, CFIndex *outLength, CFErrorRef *errorPtr); 864 865 // implementation of below functions - takes URL as parameter 866 static CFPropertyListRef _CFBundleCreateFilteredInfoPlistWithURL(CFURLRef infoPlistURL, CFSetRef keyPaths, _CFBundleFilteredPlistOptions options) { 867 CFPropertyListRef result = NULL; 868 869 if (!infoPlistURL) return CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 870 871 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(infoPlistURL); 872 CFStringRef filePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 873 CFRelease(absoluteURL); 874 875 if (!filePath) return CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 876 877 void *bytes = NULL; 878 CFIndex length = 0; 879 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 880 Boolean mapped = options & _CFBundleFilteredPlistMemoryMapped ? true : false; 881 #else 882 Boolean mapped = false; 883 #endif 884 Boolean success = _CFReadMappedFromFile(filePath, mapped, false, &bytes, &length, NULL); 885 CFRelease(filePath); 886 if (!success) return CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 887 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); 892 893 success = _CFPropertyListCreateFiltered(kCFAllocatorSystemDefault, infoPlistData, kCFPropertyListMutableContainers, newKeyPaths, &result, NULL); 894 895 if (!success || !result) { 896 result = CFDictionaryCreate(kCFAllocatorSystemDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 897 } else { 898 _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef)result); 899 } 900 901 CFRelease(newKeyPaths); 902 CFRelease(infoPlistData); 903 if (mapped) { 904 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 905 munmap(bytes, length); 906 #endif 907 } else { 908 free(bytes); 909 } 910 911 return result; 912 } 913 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); 919 return result; 920 } 921 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); 926 return result; 927 } 928 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); 934 }