/ 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  }