]> git.saurik.com Git - apple/cf.git/blob - CFBundle_InfoPlist.c
CF-1152.14.tar.gz
[apple/cf.git] / 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 }