]> git.saurik.com Git - apple/cf.git/blame_incremental - CFBundle_Resources.c
CF-744.12.tar.gz
[apple/cf.git] / CFBundle_Resources.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2012 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/* CFBundle_Resources.c
25 Copyright (c) 1999-2012, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
27*/
28
29#define READ_DIRECTORIES_CACHE_CAPACITY 128
30
31#include "CFBundle_Internal.h"
32#include <CoreFoundation/CFURLAccess.h>
33#include <CoreFoundation/CFPropertyList.h>
34#include <CoreFoundation/CFByteOrder.h>
35#include <CoreFoundation/CFNumber.h>
36#include <CoreFoundation/CFLocale.h>
37#include <CoreFoundation/CFPreferences.h>
38#include <string.h>
39#include "CFInternal.h"
40#include <CoreFoundation/CFPriv.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43#include <stdio.h>
44#include <ctype.h>
45#include <errno.h>
46#include <sys/types.h>
47
48#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
49#include <unistd.h>
50#include <sys/sysctl.h>
51#include <sys/stat.h>
52#include <dirent.h>
53#endif
54
55#if DEPLOYMENT_TARGET_MACOSX
56
57#endif
58
59#if DEPLOYMENT_TARGET_WINDOWS
60#include <io.h>
61#include <fcntl.h>
62#include <sys/stat.h>
63#include <errno.h>
64
65#define close _close
66#define write _write
67#define read _read
68#define open _NS_open
69#define stat _NS_stat
70#define fstat _fstat
71#define mkdir(a,b) _NS_mkdir(a)
72#define rmdir _NS_rmdir
73#define unlink _NS_unlink
74
75#endif
76
77CF_EXPORT bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey);
78
79extern void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len);
80
81
82static inline Boolean _CFBundleSortedArrayContains(CFArrayRef arr, CFStringRef target) {
83 CFRange arrRange = CFRangeMake(0, CFArrayGetCount(arr));
84 CFIndex itemIdx = CFArrayBSearchValues(arr, arrRange, target, (CFComparatorFunction)CFStringCompare, NULL);
85 return itemIdx < arrRange.length && CFEqual(CFArrayGetValueAtIndex(arr, itemIdx), target);
86}
87
88// 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
89#if !__CONSTANT_STRINGS__
90
91#define _CFBundleNumberOfPlatforms 7
92static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
93static const char *_CFBundleSupportedPlatformStrings[_CFBundleNumberOfPlatforms] = { "iphoneos", "macos", "windows", "linux", "freebsd", "solaris", "hpux" };
94
95#define _CFBundleNumberOfProducts 3
96static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { NULL, NULL, NULL };
97static const char *_CFBundleSupportedProductStrings[_CFBundleNumberOfProducts] = { "iphone", "ipod", "ipad" };
98
99#define _CFBundleNumberOfiPhoneOSPlatformProducts 3
100static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { NULL, NULL, NULL };
101static const char *_CFBundleSupportediPhoneOSPlatformProductStrings[_CFBundleNumberOfiPhoneOSPlatformProducts] = { "iphone", "ipod", "ipad" };
102
103__private_extern__ void _CFBundleResourcesInitialize() {
104 for (unsigned int i = 0; i < _CFBundleNumberOfPlatforms; i++) _CFBundleSupportedPlatforms[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedPlatformStrings[i], kCFStringEncodingUTF8);
105
106 for (unsigned int i = 0; i < _CFBundleNumberOfProducts; i++) _CFBundleSupportedProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedProductStrings[i], kCFStringEncodingUTF8);
107
108 for (unsigned int i = 0; i < _CFBundleNumberOfiPhoneOSPlatformProducts; i++) _CFBundleSupportediPhoneOSPlatformProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportediPhoneOSPlatformProductStrings[i], kCFStringEncodingUTF8);
109}
110
111#else
112
113#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
114// On iOS, we only support one platform
115#define _CFBundleNumberOfPlatforms 1
116static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { CFSTR("iphoneos") };
117#else
118// On other platforms, we support the following platforms
119#define _CFBundleNumberOfPlatforms 7
120static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { CFSTR("iphoneos"), CFSTR("macos"), CFSTR("windows"), CFSTR("linux"), CFSTR("freebsd"), CFSTR("solaris"), CFSTR("hpux") };
121#endif
122
123#define _CFBundleNumberOfProducts 3
124static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") };
125
126#define _CFBundleNumberOfiPhoneOSPlatformProducts 3
127static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") };
128
129__private_extern__ void _CFBundleResourcesInitialize() { }
130#endif
131
132static CFStringRef platform = NULL;
133
134void _CFSetProductName(CFStringRef str) {
135 if (str) CFRetain(str);
136 platform = str;
137 // Note that the previous value is leaked, which is fine normally
138 // because the initial values would tend to be the constant strings
139 // below. That is required for thread-safety value due to the Get
140 // function [not being Copy]. It is also fine because people
141 // shouldn't be screwing around with this value casually.
142}
143
144CF_EXPORT CFStringRef _CFGetProductName(void) {
145#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
146 if (!platform) {
147 char buffer[256];
148 memset(buffer, 0, sizeof(buffer));
149 size_t buflen = sizeof(buffer);
150 int ret = sysctlbyname("hw.machine", buffer, &buflen, NULL, 0);
151 if (0 == ret || (-1 == ret && ENOMEM == errno)) {
152 if (6 <= buflen && 0 == memcmp(buffer, "iPhone", 6)) {
153 platform = CFSTR("iphone");
154 } else if (4 <= buflen && 0 == memcmp(buffer, "iPod", 4)) {
155 platform = CFSTR("ipod");
156 } else if (4 <= buflen && 0 == memcmp(buffer, "iPad", 4)) {
157 platform = CFSTR("ipad");
158 } else {
159 const char *env = __CFgetenv("IPHONE_SIMULATOR_DEVICE");
160 if (env) {
161 if (0 == strcmp(env, "iPhone")) {
162 platform = CFSTR("iphone");
163 } else if (0 == strcmp(env, "iPad")) {
164 platform = CFSTR("ipad");
165 } else {
166 // fallback, unrecognized IPHONE_SIMULATOR_DEVICE
167 }
168 } else {
169 // fallback, unrecognized hw.machine and no IPHONE_SIMULATOR_DEVICE
170 }
171 }
172 }
173 if (!platform) platform = CFSTR("iphone"); // fallback
174 }
175 return platform;
176#endif
177 return CFSTR("");
178}
179
180// All new-style bundles will have these extensions.
181CF_EXPORT CFStringRef _CFGetPlatformName(void) {
182#if DEPLOYMENT_TARGET_MACOSX
183 return _CFBundleMacOSXPlatformName;
184#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
185 return _CFBundleiPhoneOSPlatformName;
186#elif DEPLOYMENT_TARGET_WINDOWS
187 return _CFBundleWindowsPlatformName;
188#elif DEPLOYMENT_TARGET_SOLARIS
189 return _CFBundleSolarisPlatformName;
190#elif DEPLOYMENT_TARGET_HPUX
191 return _CFBundleHPUXPlatformName;
192#elif DEPLOYMENT_TARGET_LINUX
193 return _CFBundleLinuxPlatformName;
194#elif DEPLOYMENT_TARGET_FREEBSD
195 return _CFBundleFreeBSDPlatformName;
196#else
197#error Unknown or unspecified DEPLOYMENT_TARGET
198#endif
199}
200
201CF_EXPORT CFStringRef _CFGetAlternatePlatformName(void) {
202#if DEPLOYMENT_TARGET_MACOSX
203 return _CFBundleAlternateMacOSXPlatformName;
204#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
205 return _CFBundleMacOSXPlatformName;
206#elif DEPLOYMENT_TARGET_WINDOWS
207 return CFSTR("");
208#else
209#error Unknown or unspecified DEPLOYMENT_TARGET
210#endif
211}
212
213static UniChar *_AppSupportUniChars1 = NULL;
214static CFIndex _AppSupportLen1 = 0;
215static UniChar *_AppSupportUniChars2 = NULL;
216static CFIndex _AppSupportLen2 = 0;
217static UniChar *_ResourcesUniChars = NULL;
218static CFIndex _ResourcesLen = 0;
219static UniChar *_PlatformUniChars = NULL;
220static CFIndex _PlatformLen = 0;
221static UniChar *_AlternatePlatformUniChars = NULL;
222static CFIndex _AlternatePlatformLen = 0;
223static UniChar *_LprojUniChars = NULL;
224static CFIndex _LprojLen = 0;
225static UniChar *_BaseUniChars = NULL;
226static CFIndex _BaseLen;
227static UniChar *_GlobalResourcesUniChars = NULL;
228static CFIndex _GlobalResourcesLen = 0;
229static UniChar *_InfoExtensionUniChars = NULL;
230static CFIndex _InfoExtensionLen = 0;
231
232#if 0
233static UniChar _ResourceSuffix3[32];
234static CFIndex _ResourceSuffix3Len = 0;
235#endif
236static UniChar _ResourceSuffix2[16];
237static CFIndex _ResourceSuffix2Len = 0;
238static UniChar _ResourceSuffix1[16];
239static CFIndex _ResourceSuffix1Len = 0;
240
241static void _CFBundleInitStaticUniCharBuffers(void) {
242 CFStringRef appSupportStr1 = _CFBundleSupportFilesDirectoryName1;
243 CFStringRef appSupportStr2 = _CFBundleSupportFilesDirectoryName2;
244 CFStringRef resourcesStr = _CFBundleResourcesDirectoryName;
245 CFStringRef platformStr = _CFGetPlatformName();
246 CFStringRef alternatePlatformStr = _CFGetAlternatePlatformName();
247 CFStringRef lprojStr = _CFBundleLprojExtension;
248 CFStringRef globalResourcesStr = _CFBundleNonLocalizedResourcesDirectoryName;
249 CFStringRef infoExtensionStr = _CFBundleInfoExtension;
250 CFStringRef baseStr = _CFBundleBaseDirectory;
251
252 _AppSupportLen1 = CFStringGetLength(appSupportStr1);
253 _AppSupportLen2 = CFStringGetLength(appSupportStr2);
254 _ResourcesLen = CFStringGetLength(resourcesStr);
255 _PlatformLen = CFStringGetLength(platformStr);
256 _AlternatePlatformLen = CFStringGetLength(alternatePlatformStr);
257 _LprojLen = CFStringGetLength(lprojStr);
258 _GlobalResourcesLen = CFStringGetLength(globalResourcesStr);
259 _InfoExtensionLen = CFStringGetLength(infoExtensionStr);
260 _BaseLen = CFStringGetLength(baseStr);
261
262 _AppSupportUniChars1 = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (_AppSupportLen1 + _AppSupportLen2 + _ResourcesLen + _PlatformLen + _AlternatePlatformLen + _LprojLen + _GlobalResourcesLen + _InfoExtensionLen + _BaseLen), 0);
263 _AppSupportUniChars2 = _AppSupportUniChars1 + _AppSupportLen1;
264 _ResourcesUniChars = _AppSupportUniChars2 + _AppSupportLen2;
265 _PlatformUniChars = _ResourcesUniChars + _ResourcesLen;
266 _AlternatePlatformUniChars = _PlatformUniChars + _PlatformLen;
267 _LprojUniChars = _AlternatePlatformUniChars + _AlternatePlatformLen;
268 _GlobalResourcesUniChars = _LprojUniChars + _LprojLen;
269 _InfoExtensionUniChars = _GlobalResourcesUniChars + _GlobalResourcesLen;
270 _BaseUniChars = _InfoExtensionUniChars + _InfoExtensionLen;
271
272 if (_AppSupportLen1 > 0) CFStringGetCharacters(appSupportStr1, CFRangeMake(0, _AppSupportLen1), _AppSupportUniChars1);
273 if (_AppSupportLen2 > 0) CFStringGetCharacters(appSupportStr2, CFRangeMake(0, _AppSupportLen2), _AppSupportUniChars2);
274 if (_ResourcesLen > 0) CFStringGetCharacters(resourcesStr, CFRangeMake(0, _ResourcesLen), _ResourcesUniChars);
275 if (_PlatformLen > 0) CFStringGetCharacters(platformStr, CFRangeMake(0, _PlatformLen), _PlatformUniChars);
276 if (_AlternatePlatformLen > 0) CFStringGetCharacters(alternatePlatformStr, CFRangeMake(0, _AlternatePlatformLen), _AlternatePlatformUniChars);
277 if (_LprojLen > 0) CFStringGetCharacters(lprojStr, CFRangeMake(0, _LprojLen), _LprojUniChars);
278 if (_GlobalResourcesLen > 0) CFStringGetCharacters(globalResourcesStr, CFRangeMake(0, _GlobalResourcesLen), _GlobalResourcesUniChars);
279 if (_InfoExtensionLen > 0) CFStringGetCharacters(infoExtensionStr, CFRangeMake(0, _InfoExtensionLen), _InfoExtensionUniChars);
280 if (_BaseLen > 0) CFStringGetCharacters(baseStr, CFRangeMake(0, _BaseLen), _BaseUniChars);
281
282 _ResourceSuffix1Len = CFStringGetLength(platformStr);
283 if (_ResourceSuffix1Len > 0) _ResourceSuffix1[0] = '-';
284 if (_ResourceSuffix1Len > 0) CFStringGetCharacters(platformStr, CFRangeMake(0, _ResourceSuffix1Len), _ResourceSuffix1 + 1);
285 if (_ResourceSuffix1Len > 0) _ResourceSuffix1Len++;
286 CFStringRef productStr = _CFGetProductName();
287 if (CFEqual(productStr, CFSTR("ipod"))) { // For now, for resource lookups, hide ipod distinction and make it look for iphone resources
288 productStr = CFSTR("iphone");
289 }
290 _ResourceSuffix2Len = CFStringGetLength(productStr);
291 if (_ResourceSuffix2Len > 0) _ResourceSuffix2[0] = '~';
292 if (_ResourceSuffix2Len > 0) CFStringGetCharacters(productStr, CFRangeMake(0, _ResourceSuffix2Len), _ResourceSuffix2 + 1);
293 if (_ResourceSuffix2Len > 0) _ResourceSuffix2Len++;
294#if 0
295 if (_ResourceSuffix1Len > 1 && _ResourceSuffix2Len > 1) {
296 _ResourceSuffix3Len = _ResourceSuffix1Len + _ResourceSuffix2Len;
297 memmove(_ResourceSuffix3, _ResourceSuffix1, sizeof(UniChar) * _ResourceSuffix1Len);
298 memmove(_ResourceSuffix3 + _ResourceSuffix1Len, _ResourceSuffix2, sizeof(UniChar) * _ResourceSuffix2Len);
299 }
300#endif
301}
302
303CF_INLINE void _CFEnsureStaticBuffersInited(void) {
304 static dispatch_once_t once = 0;
305 dispatch_once(&once, ^{
306 _CFBundleInitStaticUniCharBuffers();
307 });
308}
309
310static CFSpinLock_t _cacheLock = CFSpinLockInit;
311static CFMutableDictionaryRef _contentsCache = NULL;
312static CFMutableDictionaryRef _directoryContentsCache = NULL;
313static CFMutableDictionaryRef _unknownContentsCache = NULL;
314
315typedef enum {
316 _CFBundleAllContents = 0,
317 _CFBundleDirectoryContents = 1,
318 _CFBundleUnknownContents = 2
319} _CFBundleDirectoryContentsType;
320
321extern void _CFArraySortValues(CFMutableArrayRef array, CFComparatorFunction comparator, void *context);
322
323static CFArrayRef _CFBundleCopySortedDirectoryContentsAtPath(CFStringRef path, _CFBundleDirectoryContentsType contentsType) {
324 CFArrayRef result = NULL;
325
326 if (!path) {
327 // Return an empty result. It's mutable because the other arrays returned from this function are mutable, so may as well go for maximum compatibility.
328 result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
329 return result;
330 }
331
332 __CFSpinLock(&_cacheLock);
333 if (contentsType == _CFBundleUnknownContents) {
334 if (_unknownContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(_unknownContentsCache, path);
335 } else if (contentsType == _CFBundleDirectoryContents) {
336 if (_directoryContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(_directoryContentsCache, path);
337 } else {
338 if (_contentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(_contentsCache, path);
339 }
340 if (result) CFRetain(result);
341 __CFSpinUnlock(&_cacheLock);
342
343 if (!result) {
344 Boolean tryToOpen = false, allDots = true;
345 char cpathBuff[CFMaxPathSize];
346 CFIndex cpathLen = 0, idx, lastSlashIdx = 0;
347 CFMutableArrayRef contents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), unknownContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
348 CFStringRef dirName, name;
349
350 cpathBuff[0] = '\0';
351 if (CFStringGetFileSystemRepresentation(path, cpathBuff, CFMaxPathSize)) {
352 tryToOpen = true;
353 cpathLen = strlen(cpathBuff);
354
355 // First see whether we already know that the directory doesn't exist
356 for (idx = cpathLen; lastSlashIdx == 0 && idx-- > 0;) {
357 if (cpathBuff[idx] == PATH_SEP) lastSlashIdx = idx;
358 else if (cpathBuff[idx] != '.') allDots = false;
359 }
360 if (lastSlashIdx > 0 && lastSlashIdx + 1 < cpathLen && !allDots) {
361 cpathBuff[lastSlashIdx] = '\0';
362 dirName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff);
363 if (dirName) {
364 name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff + lastSlashIdx + 1);
365 if (name) {
366 // ??? we might like to use _directoryContentsCache rather than _contentsCache here, but we cannot unless we resolve DT_LNKs below
367 CFArrayRef dirDirContents = NULL;
368
369 __CFSpinLock(&_cacheLock);
370 if (_contentsCache) dirDirContents = (CFArrayRef)CFDictionaryGetValue(_contentsCache, dirName);
371 if (dirDirContents) {
372 Boolean foundIt = false;
373 CFIndex dirDirIdx, dirDirLength = CFArrayGetCount(dirDirContents);
374 for (dirDirIdx = 0; !foundIt && dirDirIdx < dirDirLength; dirDirIdx++) if (kCFCompareEqualTo == CFStringCompare(name, (CFStringRef)CFArrayGetValueAtIndex(dirDirContents, dirDirIdx), kCFCompareCaseInsensitive)) foundIt = true;
375 if (!foundIt) tryToOpen = false;
376 }
377 __CFSpinUnlock(&_cacheLock);
378 CFRelease(name);
379 }
380 CFRelease(dirName);
381 }
382 cpathBuff[lastSlashIdx] = PATH_SEP;
383 }
384 }
385#if DEPLOYMENT_TARGET_WINDOWS
386 // Make sure there is room for the additional space we need in the win32 api
387 if (tryToOpen && cpathLen + 2 < CFMaxPathSize) {
388 WIN32_FIND_DATAW file;
389 HANDLE handle;
390
391 cpathBuff[cpathLen++] = '\\';
392 cpathBuff[cpathLen++] = '*';
393 cpathBuff[cpathLen] = '\0';
394
395 // Convert UTF8 buffer to windows appropriate UTF-16LE
396 // Get the real length of the string in UTF16 characters
397 CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, cpathBuff, kCFStringEncodingUTF8);
398 cpathLen = CFStringGetLength(cfStr);
399 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
400 wchar_t *wideBuf = (wchar_t *)malloc((cpathLen + 1) * sizeof(wchar_t));
401 // Copy the string into the buffer and terminate
402 CFStringGetCharacters(cfStr, CFRangeMake(0, cpathLen), (UniChar *)wideBuf);
403 wideBuf[cpathLen] = 0;
404 CFRelease(cfStr);
405
406 handle = FindFirstFileW(wideBuf, (LPWIN32_FIND_DATAW)&file);
407 if (handle != INVALID_HANDLE_VALUE) {
408 do {
409 CFIndex nameLen = wcslen(file.cFileName);
410 if (0 == nameLen || ('.' == file.cFileName[0] && (1 == nameLen || (2 == nameLen && '.' == file.cFileName[1]) || '_' == file.cFileName[1]))) continue;
411 name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)file.cFileName, nameLen * sizeof(wchar_t), kCFStringEncodingUTF16, NO);
412 if (name) {
413 CFArrayAppendValue(contents, name);
414 if (file.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) {
415 CFArrayAppendValue(directoryContents, name);
416 } /* else if (file.dwFileAttributes == DT_UNKNOWN) {
417 CFArrayAppendValue(unknownContents, name);
418 } */
419 CFRelease(name);
420 }
421 } while (FindNextFileW(handle, &file));
422
423 FindClose(handle);
424 }
425 free(wideBuf);
426 }
427#else
428 DIR *dirp = NULL;
429 struct dirent *dent;
430 if (tryToOpen && (dirp = opendir(cpathBuff))) {
431 while ((dent = readdir(dirp))) {
432 CFIndex nameLen = dent->d_namlen;
433 if (0 == nameLen || 0 == dent->d_fileno || ('.' == dent->d_name[0] && (1 == nameLen || (2 == nameLen && '.' == dent->d_name[1]) || '_' == dent->d_name[1]))) continue;
434 name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, dent->d_name);
435 if (name) {
436 // ??? should we follow links for DT_LNK? unless we do, results are approximate, but for performance reasons we do not
437 // ??? likewise for DT_UNKNOWN
438 // ??? the utility of distinguishing directories from other contents is somewhat doubtful anyway
439 CFArrayAppendValue(contents, name);
440 if (dent->d_type == DT_DIR) {
441 CFArrayAppendValue(directoryContents, name);
442 } else if (dent->d_type == DT_UNKNOWN) {
443 CFArrayAppendValue(unknownContents, name);
444 }
445 CFRelease(name);
446 }
447 }
448 (void)closedir(dirp);
449 }
450#endif
451
452 _CFArraySortValues(contents, (CFComparatorFunction)CFStringCompare, NULL);
453 _CFArraySortValues(directoryContents, (CFComparatorFunction)CFStringCompare, NULL);
454 _CFArraySortValues(unknownContents, (CFComparatorFunction)CFStringCompare, NULL);
455
456 __CFSpinLock(&_cacheLock);
457 if (!_contentsCache) _contentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
458 if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(_contentsCache)) CFDictionaryRemoveAllValues(_contentsCache);
459 CFDictionaryAddValue(_contentsCache, path, contents);
460
461 if (!_directoryContentsCache) _directoryContentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
462 if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(_directoryContentsCache)) CFDictionaryRemoveAllValues(_directoryContentsCache);
463 CFDictionaryAddValue(_directoryContentsCache, path, directoryContents);
464
465 if (!_unknownContentsCache) _unknownContentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
466 if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(_unknownContentsCache)) CFDictionaryRemoveAllValues(_unknownContentsCache);
467 CFDictionaryAddValue(_unknownContentsCache, path, unknownContents);
468
469 if (contentsType == _CFBundleUnknownContents) {
470 result = (CFArrayRef)CFRetain(unknownContents);
471 } else if (contentsType == _CFBundleDirectoryContents) {
472 result = (CFArrayRef)CFRetain(directoryContents);
473 } else {
474 result = (CFArrayRef)CFRetain(contents);
475 }
476
477 CFRelease(contents);
478 CFRelease(directoryContents);
479 CFRelease(unknownContents);
480 __CFSpinUnlock(&_cacheLock);
481 }
482 return result;
483}
484
485static void _CFBundleFlushContentsCaches(void) {
486 __CFSpinLock(&_cacheLock);
487 if (_contentsCache) CFDictionaryRemoveAllValues(_contentsCache);
488 if (_directoryContentsCache) CFDictionaryRemoveAllValues(_directoryContentsCache);
489 if (_unknownContentsCache) CFDictionaryRemoveAllValues(_unknownContentsCache);
490 __CFSpinUnlock(&_cacheLock);
491}
492
493static void _CFBundleFlushContentsCacheForPath(CFMutableDictionaryRef cache, CFStringRef path) {
494 CFStringRef keys[READ_DIRECTORIES_CACHE_CAPACITY];
495 unsigned i, count = CFDictionaryGetCount(cache);
496 if (count <= READ_DIRECTORIES_CACHE_CAPACITY) {
497 CFDictionaryGetKeysAndValues(cache, (const void **)keys, NULL);
498 for (i = 0; i < count; i++) {
499 if (CFStringFindWithOptions(keys[i], path, CFRangeMake(0, CFStringGetLength(keys[i])), kCFCompareAnchored|kCFCompareCaseInsensitive, NULL)) CFDictionaryRemoveValue(cache, keys[i]);
500 }
501 }
502}
503
504static void _CFBundleFlushContentsCachesForPath(CFStringRef path) {
505 __CFSpinLock(&_cacheLock);
506 if (_contentsCache) _CFBundleFlushContentsCacheForPath(_contentsCache, path);
507 if (_directoryContentsCache) _CFBundleFlushContentsCacheForPath(_directoryContentsCache, path);
508 if (_unknownContentsCache) _CFBundleFlushContentsCacheForPath(_unknownContentsCache, path);
509 __CFSpinUnlock(&_cacheLock);
510}
511
512CF_EXPORT void _CFBundleFlushCachesForURL(CFURLRef url) {
513 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url);
514 CFStringRef path = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
515 _CFBundleFlushContentsCachesForPath(path);
516 CFRelease(path);
517 CFRelease(absoluteURL);
518}
519
520CF_EXPORT void _CFBundleFlushCaches(void) {
521 _CFBundleFlushContentsCaches();
522}
523
524static inline Boolean _CFIsResourceCommon(char *path, Boolean *isDir) {
525 Boolean exists;
526 SInt32 mode;
527 if (_CFGetPathProperties(kCFAllocatorSystemDefault, path, &exists, &mode, NULL, NULL, NULL, NULL) == 0) {
528 if (isDir) *isDir = ((exists && ((mode & S_IFMT) == S_IFDIR)) ? true : false);
529 return (exists && (mode & 0444));
530 }
531 return false;
532}
533
534__private_extern__ Boolean _CFIsResourceAtURL(CFURLRef url, Boolean *isDir) {
535 char path[CFMaxPathSize];
536 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathLength)) return false;
537
538 return _CFIsResourceCommon(path, isDir);
539}
540
541__private_extern__ Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir) {
542 char pathBuf[CFMaxPathSize];
543 if (!CFStringGetFileSystemRepresentation(path, pathBuf, CFMaxPathSize)) return false;
544
545 return _CFIsResourceCommon(pathBuf, isDir);
546}
547
548__private_extern__ void _CFBundleSetResourceDir(UniChar *buffer, CFIndex *currLen, CFIndex maxLen, uint8_t version){
549 if (1 == version) {
550 _CFAppendPathComponent(buffer, currLen, maxLen, _AppSupportUniChars1, _AppSupportLen1);
551 } else if (2 == version) {
552 _CFAppendPathComponent(buffer, currLen, maxLen, _AppSupportUniChars2, _AppSupportLen2);
553 }
554 if (0 == version || 1 == version || 2 == version) _CFAppendPathComponent(buffer, currLen, maxLen, _ResourcesUniChars, _ResourcesLen);
555}
556
557static CFArrayRef _CFCopyTypesForSearchBundleDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, uint8_t version) {
558 CFMutableArrayRef result = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
559 CFArrayRef contents;
560 CFRange contentsRange, resultRange = CFRangeMake(0, 0);
561 CFIndex dirPathLen = pathLen, numResTypes = CFArrayGetCount(resTypes), i, j;
562
563 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
564 CFStringReplaceAll(cheapStr, tmpString);
565 //fprintf(stderr, "looking in ");CFShow(cheapStr);
566 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
567 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
568
569 CFStringSetExternalCharactersNoCopy(tmpString, nameUniChars, nameLen, nameLen);
570 CFStringReplaceAll(cheapStr, tmpString);
571 for (i = 0; i < contentsRange.length; i++) {
572 CFStringRef content = (CFStringRef)CFArrayGetValueAtIndex(contents, i);
573 if (CFStringHasPrefix(content, cheapStr)) {
574 //fprintf(stderr, "found ");CFShow(content);
575 for (j = 0; j < numResTypes; j++) {
576 CFStringRef resType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, j);
577 if (!CFArrayContainsValue(result, resultRange, resType) && CFStringHasSuffix(content, resType)) {
578 CFArrayAppendValue(result, resType);
579 resultRange.length = CFArrayGetCount(result);
580 }
581 }
582 }
583 }
584 //fprintf(stderr, "result ");CFShow(result);
585 CFRelease(contents);
586 return result;
587}
588
589#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
590static void _CFSearchBundleDirectory2(CFAllocatorRef alloc, CFMutableArrayRef result, UniChar *pathUniChars, CFIndex pathLen, UniChar *nameUniChars, CFIndex nameLen, UniChar *typeUniChars, CFIndex typeLen, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, uint8_t version) {
591 // pathUniChars is the full path to the directory we are searching.
592 // nameUniChars is what we are looking for.
593 // typeUniChars is the type we are looking for.
594 // platformUniChars is the platform name.
595 // cheapStr is available for our use for whatever we want.
596 // URLs for found resources get added to result.
597
598 Boolean appendSucceeded = true;
599 if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen);
600 if (! appendSucceeded) return;
601 CFIndex savedPathLen = pathLen;
602
603 // Try in order:
604 // NAME-PLATFORM~PRODUCT.TYPE (disabled for now)
605 // NAME~PRODUCT.TYPE
606 // NAME-PLATFORM.TYPE (disabled for now)
607 // NAME.TYPE
608
609#if 0
610 appendSucceeded = (pathLen + _ResourceSuffix3Len < CFMaxPathSize);
611 if (appendSucceeded) {
612 memmove(pathUniChars + pathLen, _ResourceSuffix3, _ResourceSuffix3Len * sizeof(UniChar));
613 pathLen += _ResourceSuffix3Len;
614 }
615 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
616 if (appendSucceeded) {
617 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
618 CFStringReplaceAll(cheapStr, tmpString);
619 Boolean Found = false, IsDir = false;
620 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
621 if (Found) {
622 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
623 CFArrayAppendValue(result, url);
624 CFRelease(url);
625 return;
626 }
627 }
628#endif
629
630 pathLen = savedPathLen;
631 appendSucceeded = (pathLen + _ResourceSuffix2Len < CFMaxPathSize);
632 if (appendSucceeded) {
633 memmove(pathUniChars + pathLen, _ResourceSuffix2, _ResourceSuffix2Len * sizeof(UniChar));
634 pathLen += _ResourceSuffix2Len;
635 }
636 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
637 if (appendSucceeded) {
638 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
639 CFStringReplaceAll(cheapStr, tmpString);
640 Boolean Found = false, IsDir = false;
641 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
642 if (Found) {
643 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
644 CFArrayAppendValue(result, url);
645 CFRelease(url);
646 return;
647 }
648 }
649
650#if 0
651 pathLen = savedPathLen;
652 appendSucceeded = (pathLen + _ResourceSuffix1Len < CFMaxPathSize);
653 if (appendSucceeded) {
654 memmove(pathUniChars + pathLen, _ResourceSuffix1, _ResourceSuffix1Len * sizeof(UniChar));
655 pathLen += _ResourceSuffix1Len;
656 }
657 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
658 if (appendSucceeded) {
659 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
660 CFStringReplaceAll(cheapStr, tmpString);
661 Boolean Found = false, IsDir = false;
662 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
663 if (Found) {
664 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
665 CFArrayAppendValue(result, url);
666 CFRelease(url);
667 return;
668 }
669 }
670#endif
671
672 pathLen = savedPathLen;
673 appendSucceeded = true;
674 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
675 if (appendSucceeded) {
676 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
677 CFStringReplaceAll(cheapStr, tmpString);
678 Boolean Found = false, IsDir = false;
679 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
680 if (Found) {
681 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
682 CFArrayAppendValue(result, url);
683 CFRelease(url);
684 return;
685 }
686 }
687}
688#endif
689
690static void _CFSearchBundleDirectory(CFAllocatorRef alloc, CFMutableArrayRef result, UniChar *pathUniChars, CFIndex pathLen, UniChar *nameUniChars, CFIndex nameLen, UniChar *typeUniChars, CFIndex typeLen, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, uint8_t version) {
691
692#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
693 _CFSearchBundleDirectory2(alloc, result, pathUniChars, pathLen, nameUniChars, nameLen, typeUniChars, typeLen, cheapStr, tmpString, version);
694#else
695 // pathUniChars is the full path to the directory we are searching.
696 // nameUniChars is what we are looking for.
697 // typeUniChars is the type we are looking for.
698 // platformUniChars is the platform name.
699 // cheapStr is available for our use for whatever we want.
700 // URLs for found resources get added to result.
701 CFIndex savedPathLen;
702 Boolean appendSucceeded = true, platformGenericFound = false, platformSpecificFound = false, platformGenericIsDir = false, platformSpecificIsDir = false;
703 Boolean platformGenericIsUnknown = false, platformSpecificIsUnknown = false;
704 CFStringRef platformGenericStr = NULL;
705
706 CFIndex dirPathLen = pathLen;
707 CFArrayRef contents, directoryContents, unknownContents;
708 CFRange contentsRange, directoryContentsRange, unknownContentsRange;
709
710 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
711 CFStringReplaceAll(cheapStr, tmpString);
712 //fprintf(stderr, "looking in ");CFShow(cheapStr);
713 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
714 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
715 directoryContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents);
716 directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents));
717 unknownContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents);
718 unknownContentsRange = CFRangeMake(0, CFArrayGetCount(unknownContents));
719
720 if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen);
721 savedPathLen = pathLen;
722 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
723 if (appendSucceeded) {
724 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1);
725 CFStringReplaceAll(cheapStr, tmpString);
726 platformGenericFound = _CFBundleSortedArrayContains(contents, cheapStr);
727 platformGenericIsDir = _CFBundleSortedArrayContains(directoryContents, cheapStr);
728 platformGenericIsUnknown = _CFBundleSortedArrayContains(unknownContents, cheapStr);
729 //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformGenericFound) fprintf(stderr, "found it\n"); if (platformGenericIsDir) fprintf(stderr, "a directory\n");
730 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
731 CFStringReplaceAll(cheapStr, tmpString);
732 if (platformGenericFound && platformGenericIsUnknown) {
733 (void)_CFIsResourceAtPath(cheapStr, &platformGenericIsDir);
734 //if (platformGenericIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n");
735 }
736 }
737
738 // Check for platform specific.
739 if (platformGenericFound) {
740 platformGenericStr = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, cheapStr);
741 if (!platformSpecificFound && (_PlatformLen > 0)) {
742 pathLen = savedPathLen;
743 pathUniChars[pathLen++] = (UniChar)'-';
744 memmove(pathUniChars + pathLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
745 pathLen += _PlatformLen;
746 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
747 if (appendSucceeded) {
748 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1);
749 CFStringReplaceAll(cheapStr, tmpString);
750 platformSpecificFound = _CFBundleSortedArrayContains(contents, cheapStr);
751 platformSpecificIsDir = _CFBundleSortedArrayContains(directoryContents, cheapStr);
752 platformSpecificIsUnknown = _CFBundleSortedArrayContains(unknownContents, cheapStr);
753 //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformSpecificFound) fprintf(stderr, "found it\n"); if (platformSpecificIsDir) fprintf(stderr, "a directory\n");
754 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
755 CFStringReplaceAll(cheapStr, tmpString);
756 if (platformSpecificFound && platformSpecificIsUnknown) {
757 (void)_CFIsResourceAtPath(cheapStr, &platformSpecificIsDir);
758 //if (platformSpecificIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n");
759 }
760 }
761 }
762 }
763 if (platformSpecificFound) {
764 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, platformSpecificIsDir);
765 CFArrayAppendValue(result, url);
766 CFRelease(url);
767 } else if (platformGenericFound) {
768 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, platformGenericStr ? platformGenericStr : cheapStr, PLATFORM_PATH_STYLE, platformGenericIsDir);
769 CFArrayAppendValue(result, url);
770 CFRelease(url);
771 }
772 if (platformGenericStr) CFRelease(platformGenericStr);
773 CFRelease(contents);
774 CFRelease(directoryContents);
775 CFRelease(unknownContents);
776#endif
777}
778
779static void _CFSearchBundleDirectoryWithPredicate(CFAllocatorRef alloc, CFMutableArrayRef result, UniChar *pathUniChars, CFIndex dirPathLen, Boolean (^predicate)(CFStringRef filename, Boolean *stop), CFMutableStringRef cheapStr, CFMutableStringRef tmpString, Boolean *stopLooking, uint8_t version) {
780
781 // pathUniChars is the full path to the directory we are searching.
782 // platformUniChars is the platform name.
783 // predicate is a block that evaluates a given filename to see if it's a match.
784 // cheapStr is available for our use for whatever we want.
785 // URLs for found resources get added to result.
786
787 // get the contents of the directory
788 CFArrayRef contents, directoryContents, unknownContents;
789 CFRange contentsRange;
790
791 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
792 CFStringReplaceAll(cheapStr, tmpString);
793
794 if (!_CFAppendTrailingPathSlash(pathUniChars, &dirPathLen, CFMaxPathSize)) {
795 return;
796 }
797
798 //fprintf(stderr, "looking in ");CFShow(cheapStr);
799 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
800 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
801 directoryContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents);
802 unknownContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents);
803
804 // scan directory contents for matches against predicate
805 for (int i = 0; i < contentsRange.length; i++) {
806 CFStringRef candidateFilename = (CFStringRef)CFArrayGetValueAtIndex(contents, i);
807 if (predicate(candidateFilename, stopLooking)) {
808 // we want this resource, though possibly a platform specific version of it
809 // unpack candidateFilename string into pathUniChars after verifying that we have enough space in the buffer
810 CFIndex candidateFilenameLength = CFStringGetLength(candidateFilename);
811 if ((dirPathLen + candidateFilenameLength < CFMaxPathSize)) {
812 CFStringGetCharacters(candidateFilename, CFRangeMake(0, candidateFilenameLength), pathUniChars + dirPathLen);
813
814 // is there a platform specific version available? if so update pathUniChars to contain it and candidateFilenameLength to describe its length.
815 static const int platformSeparatorLen = 1; // the length of '-', as appears in foo-macos.tiff. sugar to make the following easier to read.
816 if (_PlatformLen && (dirPathLen + candidateFilenameLength + platformSeparatorLen + _PlatformLen < CFMaxPathSize)) {
817 CFIndex candidateFilenameWithoutExtensionLen = _CFLengthAfterDeletingPathExtension(pathUniChars + dirPathLen, candidateFilenameLength);
818 CFIndex extensionLen = candidateFilenameLength - candidateFilenameWithoutExtensionLen;
819 // shift the extension over to make space for the platform
820 memmove(pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen + platformSeparatorLen + _PlatformLen, pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen, extensionLen * sizeof(UniChar));
821 // write the platform into the middle of the string
822 pathUniChars[dirPathLen + candidateFilenameWithoutExtensionLen] = (UniChar)'-';
823 memcpy(pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen + platformSeparatorLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
824 // pack it up as a CFStringRef
825 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen, candidateFilenameLength + platformSeparatorLen + _PlatformLen, candidateFilenameLength + _PlatformLen);
826 CFStringReplaceAll(cheapStr, tmpString);
827 // is the platform specialized version there?
828 if (_CFBundleSortedArrayContains(contents, cheapStr)) {
829 // woo. update the candidateFilenameLength. we'll update the candidateFilename too for consistency, but we don't actually use it again.
830 // the pathUniChars now contains the full path to the file
831 candidateFilename = cheapStr;
832 candidateFilenameLength = candidateFilenameLength + _PlatformLen + platformSeparatorLen;
833 } else {
834 // nope, no platform specific resource. Put the pathUniChars back how they were before, without the platform.
835 memmove(pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen, pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen + platformSeparatorLen + _PlatformLen, extensionLen * sizeof(UniChar));
836 }
837 }
838
839 // get the full path into cheapStr
840 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen + candidateFilenameLength, dirPathLen + candidateFilenameLength);
841 CFStringReplaceAll(cheapStr, tmpString);
842
843 // is the resource a directory? we need to know so that we can avoid file access when making a URL.
844 Boolean isDir = 0;
845 if (_CFBundleSortedArrayContains(directoryContents, cheapStr)) {
846 isDir = 1;
847 } else if (_CFBundleSortedArrayContains(unknownContents, cheapStr)) {
848 _CFIsResourceAtPath(cheapStr, &isDir);
849 }
850
851 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, isDir);
852 CFArrayAppendValue(result, url);
853 CFRelease(url);
854 }
855 }
856
857 if (*stopLooking) break;
858 }
859
860 CFRelease(contents);
861 CFRelease(directoryContents);
862 CFRelease(unknownContents);
863}
864
865
866static void _CFFindBundleResourcesInRawDir(CFAllocatorRef alloc, UniChar *workingUniChars, CFIndex workingLen, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFIndex limit, Boolean *stopLooking, Boolean (^predicate)(CFStringRef filename, Boolean *stop), uint8_t version, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, CFMutableArrayRef result) {
867 if (predicate) {
868 _CFSearchBundleDirectoryWithPredicate(alloc, result, workingUniChars, workingLen, predicate, cheapStr, tmpString, stopLooking, version);
869 return;
870 }
871 if (nameLen > 0) {
872 // If we have a resName, just call the search API. We may have to loop over the resTypes.
873 if (!resTypes) {
874 _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, NULL, 0, cheapStr, tmpString, version);
875 } else {
876 CFArrayRef subResTypes = resTypes;
877 Boolean releaseSubResTypes = false;
878 CFIndex i, c = CFArrayGetCount(resTypes);
879 if (c > 2) {
880 // this is an optimization we employ when searching for large numbers of types, if the directory contents are available
881 // we scan the directory contents and restrict the list of resTypes to the types that might actually occur with the specified name
882 subResTypes = _CFCopyTypesForSearchBundleDirectory(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, cheapStr, tmpString, version);
883 c = CFArrayGetCount(subResTypes);
884 releaseSubResTypes = true;
885 }
886 for (i = 0; i < c; i++) {
887 CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(subResTypes, i);
888 CFIndex typeLen = CFStringGetLength(curType);
889 STACK_BUFFER_DECL(UniChar, typeChars, typeLen);
890 CFStringGetCharacters(curType, CFRangeMake(0, typeLen), typeChars);
891 _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, typeChars, typeLen, cheapStr, tmpString, version);
892 if (limit <= CFArrayGetCount(result)) break;
893 }
894 if (releaseSubResTypes) CFRelease(subResTypes);
895 }
896 } else {
897 // If we have no resName, do it by hand. We may have to loop over the resTypes.
898 char cpathBuff[CFMaxPathSize];
899 CFIndex cpathLen;
900 CFMutableArrayRef children;
901
902 CFStringSetExternalCharactersNoCopy(tmpString, workingUniChars, workingLen, workingLen);
903 if (!CFStringGetFileSystemRepresentation(tmpString, cpathBuff, CFMaxPathSize)) return;
904 cpathLen = strlen(cpathBuff);
905
906 if (!resTypes) {
907 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
908 children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, NULL);
909 if (children) {
910 CFIndex childIndex, childCount = CFArrayGetCount(children);
911 for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex));
912 CFRelease(children);
913 }
914 } else {
915 CFIndex i, c = CFArrayGetCount(resTypes);
916 for (i = 0; i < c; i++) {
917 CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, i);
918
919 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
920 children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, curType);
921 if (children) {
922 CFIndex childIndex, childCount = CFArrayGetCount(children);
923 for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex));
924 CFRelease(children);
925 }
926 if (limit <= CFArrayGetCount(result)) break;
927 }
928 }
929 }
930}
931
932
933static void _CFFindBundleResourcesInResourcesDir(CFAllocatorRef alloc, UniChar *workingUniChars, CFIndex workingLen, UniChar *subDirUniChars, CFIndex subDirLen, CFArrayRef searchLanguages, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFIndex limit, Boolean (^predicate)(CFStringRef filename, Boolean *stop), uint8_t version, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, CFMutableArrayRef result) {
934 CFIndex savedWorkingLen = workingLen;
935 Boolean stopLooking = false; // for predicate based-queries, we set stopLooking instead of using a limit
936 // Look directly in the directory specified in workingUniChars. as if it is a Resources directory.
937 if (1 == version) {
938 // Add the non-localized resource directory.
939 Boolean appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _GlobalResourcesUniChars, _GlobalResourcesLen);
940 if (appendSucceeded && subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen);
941 if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
942 // Strip the non-localized resource directory.
943 workingLen = savedWorkingLen;
944 }
945
946 if (CFArrayGetCount(result) < limit && !stopLooking) {
947 Boolean appendSucceeded = true;
948 if (subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen);
949 if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
950 }
951
952 // Now search the first localized resources (user language).
953 CFIndex langCount = (searchLanguages ? CFArrayGetCount(searchLanguages) : 0);
954 UniChar curLangUniChars[255];
955
956 workingLen = savedWorkingLen;
957 if (CFArrayGetCount(result) < limit && !stopLooking && langCount >= 1) {
958 CFIndex numResults = CFArrayGetCount(result);
959 CFStringRef curLangStr = (CFStringRef)CFArrayGetValueAtIndex(searchLanguages, 0);
960 CFIndex curLangLen = MIN(CFStringGetLength(curLangStr), 255);
961 CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
962
963 savedWorkingLen = workingLen;
964 if (_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, curLangUniChars, curLangLen) &&
965 _CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, _LprojUniChars, _LprojLen) &&
966 (subDirLen == 0 || (subDirLen > 0 && _CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen)))) {
967
968 _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
969
970 if (CFArrayGetCount(result) != numResults) {
971 // We found resources in a language we already searched. Don't look any farther.
972 // We also don't need to check the limit, since if the count changed at all, we are bailing.
973 return;
974 }
975 }
976 }
977
978 workingLen = savedWorkingLen;
979 // Now search the Base.lproj directory
980 if (CFArrayGetCount(result) < limit && !stopLooking) {
981 CFIndex numResults = CFArrayGetCount(result);
982 savedWorkingLen = workingLen;
983 if (_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _BaseUniChars, _BaseLen) &&
984 _CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, _LprojUniChars, _LprojLen) &&
985 (subDirLen == 0 || (subDirLen > 0 && _CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen)))) {
986
987 _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
988
989 if (CFArrayGetCount(result) != numResults) {
990 // We found resources in a language we already searched. Don't look any farther.
991 // We also don't need to check the limit, since if the count changed at all, we are bailing.
992 return;
993 }
994 }
995 }
996
997 // Now search remaining localized resources (developer language)
998 workingLen = savedWorkingLen;
999 if (CFArrayGetCount(result) < limit && !stopLooking && langCount >= 2) {
1000 // MF:??? OK to hard-wire this length?
1001 CFIndex numResults = CFArrayGetCount(result);
1002
1003 // start after 1st language
1004 for (CFIndex langIndex = 1; langIndex < langCount; langIndex++) {
1005 CFStringRef curLangStr = (CFStringRef)CFArrayGetValueAtIndex(searchLanguages, langIndex);
1006 CFIndex curLangLen = MIN(CFStringGetLength(curLangStr), 255);
1007 CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1008 savedWorkingLen = workingLen;
1009 if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, curLangUniChars, curLangLen)) {
1010 workingLen = savedWorkingLen;
1011 continue;
1012 }
1013 if (!_CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1014 workingLen = savedWorkingLen;
1015 continue;
1016 }
1017 if (subDirLen > 0) {
1018 if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen)) {
1019 workingLen = savedWorkingLen;
1020 continue;
1021 }
1022 }
1023 _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
1024
1025 // Back off this lproj component
1026 workingLen = savedWorkingLen;
1027 if (CFArrayGetCount(result) != numResults) {
1028 // We found resources in a language we already searched. Don't look any farther.
1029 // We also don't need to check the limit, since if the count changed at all, we are bailing.
1030 break;
1031 }
1032 }
1033 }
1034}
1035
1036CFArrayRef _CFFindBundleResources(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef subDirName, CFArrayRef searchLanguages, CFStringRef resName, CFArrayRef resTypes, CFIndex limit, Boolean (^predicate)(CFStringRef filename, Boolean *stop), uint8_t version) {
1037 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
1038
1039 // Build an absolute path to the base directory.
1040 // If no URL was passed, we get it from the bundle.
1041 CFURLRef baseURL = bundleURL ? (CFURLRef)CFRetain(bundleURL) : (bundle ? CFBundleCopyBundleURL(bundle) : NULL);
1042 CFURLRef absoluteURL = baseURL ? CFURLCopyAbsoluteURL(baseURL) : NULL;
1043 CFStringRef basePath = absoluteURL ? CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE) : NULL;
1044 if (absoluteURL) CFRelease(absoluteURL);
1045 if (baseURL) CFRelease(baseURL);
1046 baseURL = absoluteURL = bundleURL = NULL;
1047 bundle = NULL;
1048 // bundle and bundleURL arguments are not used any further
1049
1050 if (!basePath) return result;
1051
1052
1053 UniChar *workingUniChars, *nameUniChars, *subDirUniChars;
1054 CFIndex nameLen = 0;
1055 CFIndex workingLen, savedWorkingLen;
1056 CFMutableStringRef cheapStr, tmpString;
1057
1058 if (resName) {
1059 char buff[CFMaxPathSize];
1060 CFStringRef newResName = NULL;
1061 if (CFStringGetFileSystemRepresentation(resName, buff, CFMaxPathSize)) newResName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff);
1062 resName = newResName ? newResName : (CFStringRef)CFRetain(resName);
1063 nameLen = CFStringGetLength(resName);
1064 }
1065
1066 // Init the one-time-only unichar buffers.
1067 _CFEnsureStaticBuffersInited();
1068
1069 // Build UniChar buffers for some of the string pieces we need.
1070 CFIndex subDirLen = (subDirName ? CFStringGetLength(subDirName) : 0);
1071 nameUniChars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (nameLen + subDirLen + CFMaxPathSize), 0);
1072 if (nameUniChars) {
1073 subDirUniChars = nameUniChars + nameLen;
1074 workingUniChars = subDirUniChars + subDirLen;
1075
1076 if (nameLen > 0) CFStringGetCharacters(resName, CFRangeMake(0, nameLen), nameUniChars);
1077 if (subDirLen > 0) CFStringGetCharacters(subDirName, CFRangeMake(0, subDirLen), subDirUniChars);
1078
1079 if ((workingLen = CFStringGetLength(basePath)) > 0) CFStringGetCharacters(basePath, CFRangeMake(0, workingLen), workingUniChars);
1080 savedWorkingLen = workingLen;
1081 _CFBundleSetResourceDir(workingUniChars, &workingLen, CFMaxPathSize, version);
1082
1083 // both of these used for temp string operations, for slightly different purposes, where each type is appropriate
1084 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
1085 _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize);
1086 tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
1087
1088 _CFFindBundleResourcesInResourcesDir(kCFAllocatorSystemDefault, workingUniChars, workingLen, subDirUniChars, subDirLen, searchLanguages, nameUniChars, nameLen, resTypes, limit, predicate, version, cheapStr, tmpString, result);
1089
1090 CFRelease(cheapStr);
1091 CFRelease(tmpString);
1092 CFAllocatorDeallocate(kCFAllocatorSystemDefault, nameUniChars);
1093 }
1094 if (resName) CFRelease(resName);
1095 if (basePath) CFRelease(basePath);
1096 return result;
1097}
1098
1099__private_extern__ CFArrayRef _CFFindBundleResourcesNoBlock(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef subDirName, CFArrayRef searchLanguages, CFStringRef resName, CFArrayRef resTypes, CFIndex limit, uint8_t version){
1100 return _CFFindBundleResources(bundle, bundleURL, subDirName, searchLanguages, resName, resTypes, limit, NULL, version);
1101}
1102
1103CF_EXPORT CFURLRef CFBundleCopyResourceURL(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) {
1104 if (!bundle) return NULL;
1105#ifdef CFBUNDLE_NEWLOOKUP
1106 CFURLRef result = (CFURLRef) _CFBundleCopyFindResources(bundle, NULL, NULL, resourceName, resourceType, subDirName, NULL, NO, NO, NULL);
1107 return result;
1108#else
1109 CFURLRef result = NULL;
1110 CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array;
1111 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1112 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, NULL, _CFBundleLayoutVersion(bundle));
1113 if (types) CFRelease(types);
1114 if (array) {
1115 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
1116 CFRelease(array);
1117 }
1118 return result;
1119#endif
1120}
1121
1122CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfType(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName) {
1123#ifdef CFBUNDLE_NEWLOOKUP
1124 if (!bundle) return CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
1125 CFArrayRef result = (CFArrayRef) _CFBundleCopyFindResources(bundle, NULL, NULL, NULL, resourceType, subDirName, NULL, YES, NO, NULL);
1126 return result;
1127#else
1128 CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array;
1129 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1130 // MF:!!! Better "limit" than 1,000,000?
1131 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, NULL, _CFBundleLayoutVersion(bundle));
1132 if (types) CFRelease(types);
1133
1134 return array;
1135#endif
1136}
1137
1138CF_EXPORT CFURLRef _CFBundleCopyResourceURLForLanguage(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {
1139 return CFBundleCopyResourceURLForLocalization(bundle, resourceName, resourceType, subDirName, language);
1140}
1141
1142CF_EXPORT CFURLRef CFBundleCopyResourceURLForLocalization(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) {
1143#ifdef CFBUNDLE_NEWLOOKUP
1144 if (!bundle) return NULL;
1145 CFURLRef result = (CFURLRef) _CFBundleCopyFindResources(bundle, NULL, NULL, resourceName, resourceType, subDirName, localizationName, NO, YES, NULL);
1146 return result;
1147#else
1148 CFURLRef result = NULL;
1149 CFArrayRef languages = NULL, types = NULL, array;
1150
1151 if (localizationName) languages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&localizationName, 1, &kCFTypeArrayCallBacks);
1152 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1153 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, NULL, _CFBundleLayoutVersion(bundle));
1154 if (array) {
1155 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
1156 CFRelease(array);
1157 }
1158 if (types) CFRelease(types);
1159 if (languages) CFRelease(languages);
1160 return result;
1161#endif
1162}
1163
1164CF_EXPORT CFArrayRef _CFBundleCopyResourceURLsOfTypeForLanguage(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {
1165 return CFBundleCopyResourceURLsOfTypeForLocalization(bundle, resourceType, subDirName, language);
1166}
1167
1168CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeForLocalization(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) {
1169#ifdef CFBUNDLE_NEWLOOKUP
1170 if (!bundle) return CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
1171 CFArrayRef result = (CFArrayRef) _CFBundleCopyFindResources(bundle, NULL, NULL, NULL, resourceType, subDirName, localizationName, YES, YES, NULL);
1172 return result;
1173#else
1174 CFArrayRef languages = NULL, types = NULL, array;
1175
1176 if (localizationName) languages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&localizationName, 1, &kCFTypeArrayCallBacks);
1177 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1178 // MF:!!! Better "limit" than 1,000,000?
1179 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, NULL, _CFBundleLayoutVersion(bundle));
1180 if (types) CFRelease(types);
1181 if (languages) CFRelease(languages);
1182 return array;
1183#endif
1184}
1185
1186
1187static Boolean CFBundleAllowMixedLocalizations(void);
1188
1189CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName) {
1190 CFStringRef result = NULL;
1191 CFDictionaryRef stringTable = NULL;
1192 static CFSpinLock_t CFBundleLocalizedStringLock = CFSpinLockInit;
1193
1194 if (!key) return (value ? (CFStringRef)CFRetain(value) : (CFStringRef)CFRetain(CFSTR("")));
1195
1196 // Make sure to check the mixed localizations key early -- if the main bundle has not yet been cached, then we need to create the cache of the Info.plist before we start asking for resources (11172381)
1197 (void)CFBundleAllowMixedLocalizations();
1198
1199 if (!tableName || CFEqual(tableName, CFSTR(""))) tableName = _CFBundleDefaultStringTableName;
1200
1201 __CFSpinLock(&CFBundleLocalizedStringLock);
1202 if (__CFBundleGetResourceData(bundle)->_stringTableCache) {
1203 stringTable = (CFDictionaryRef)CFDictionaryGetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName);
1204 if (stringTable) CFRetain(stringTable);
1205 }
1206 __CFSpinUnlock(&CFBundleLocalizedStringLock);
1207
1208 if (!stringTable) {
1209 // Go load the table.
1210 CFURLRef tableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringTableType, NULL);
1211 if (tableURL) {
1212 CFStringRef nameForSharing = NULL;
1213 if (!stringTable) {
1214 CFDataRef tableData = NULL;
1215 SInt32 errCode;
1216 CFStringRef errStr;
1217 if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tableURL, &tableData, NULL, NULL, &errCode)) {
1218 stringTable = (CFDictionaryRef)CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), tableData, kCFPropertyListImmutable, &errStr);
1219 if (errStr) {
1220 CFRelease(errStr);
1221 errStr = NULL;
1222 }
1223 if (stringTable && CFDictionaryGetTypeID() != CFGetTypeID(stringTable)) {
1224 CFRelease(stringTable);
1225 stringTable = NULL;
1226 }
1227 CFRelease(tableData);
1228
1229 }
1230 }
1231 if (nameForSharing) CFRelease(nameForSharing);
1232 if (tableURL) CFRelease(tableURL);
1233 }
1234 if (!stringTable) stringTable = CFDictionaryCreate(CFGetAllocator(bundle), NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1235
1236 if (!CFStringHasSuffix(tableName, CFSTR(".nocache")) || !_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) {
1237 __CFSpinLock(&CFBundleLocalizedStringLock);
1238 if (!__CFBundleGetResourceData(bundle)->_stringTableCache) __CFBundleGetResourceData(bundle)->_stringTableCache = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1239 CFDictionarySetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName, stringTable);
1240 __CFSpinUnlock(&CFBundleLocalizedStringLock);
1241 }
1242 }
1243
1244 result = (CFStringRef)CFDictionaryGetValue(stringTable, key);
1245 if (!result) {
1246 if (!value) {
1247 result = (CFStringRef)CFRetain(key);
1248 } else if (CFEqual(value, CFSTR(""))) {
1249 result = (CFStringRef)CFRetain(key);
1250 } else {
1251 result = (CFStringRef)CFRetain(value);
1252 }
1253 __block Boolean capitalize = false;
1254 if (capitalize) {
1255 CFMutableStringRef capitalizedResult = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, result);
1256 CFLog(__kCFLogBundle, CFSTR("Localizable string \"%@\" not found in strings table \"%@\" of bundle %@."), key, tableName, bundle);
1257 CFStringUppercase(capitalizedResult, NULL);
1258 CFRelease(result);
1259 result = capitalizedResult;
1260 }
1261 } else {
1262 CFRetain(result);
1263 }
1264 CFRelease(stringTable);
1265 return result;
1266}
1267
1268CF_EXPORT CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) {
1269 CFURLRef result = NULL;
1270 unsigned char buff[CFMaxPathSize];
1271 CFURLRef newURL = NULL;
1272
1273 if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL;
1274
1275 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
1276 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL);
1277 if (_CFBundleCouldBeBundle(newURL)) {
1278#ifdef CFBUNDLE_NEWLOOKUP
1279 result = (CFURLRef) _CFBundleCopyFindResources(NULL, bundleURL, NULL, resourceName, resourceType, subDirName, NULL, NO, NO, NULL);
1280#else
1281 uint8_t version = 0;
1282 CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL, array;
1283 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1284 array = _CFFindBundleResources(NULL, newURL, subDirName, languages, resourceName, types, 1, NULL, version);
1285 if (types) CFRelease(types);
1286 if (languages) CFRelease(languages);
1287 if (array) {
1288 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
1289 CFRelease(array);
1290 }
1291#endif
1292 }
1293 if (newURL) CFRelease(newURL);
1294 return result;
1295}
1296
1297CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleURL, CFStringRef resourceType, CFStringRef subDirName) {
1298 CFArrayRef array = NULL;
1299 unsigned char buff[CFMaxPathSize];
1300 CFURLRef newURL = NULL;
1301
1302 if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL;
1303
1304 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
1305 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL);
1306 if (_CFBundleCouldBeBundle(newURL)) {
1307#ifdef CFBUNDLE_NEWLOOKUP
1308 array = (CFArrayRef) _CFBundleCopyFindResources(NULL, bundleURL, NULL, NULL, resourceType, subDirName, NULL, YES, NO, NULL);
1309#else
1310 uint8_t version = 0;
1311 CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL;
1312 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1313 // MF:!!! Better "limit" than 1,000,000?
1314 array = _CFFindBundleResources(NULL, newURL, subDirName, languages, NULL, types, 1000000, NULL, version);
1315 if (types) CFRelease(types);
1316 if (languages) CFRelease(languages);
1317#endif
1318 }
1319 if (newURL) CFRelease(newURL);
1320 return array;
1321}
1322
1323// string, with groups of 6 characters being 1 element in the array of locale abbreviations
1324const char * __CFBundleLocaleAbbreviationsArray =
1325 "en_US\0" "fr_FR\0" "en_GB\0" "de_DE\0" "it_IT\0" "nl_NL\0" "nl_BE\0" "sv_SE\0"
1326 "es_ES\0" "da_DK\0" "pt_PT\0" "fr_CA\0" "nb_NO\0" "he_IL\0" "ja_JP\0" "en_AU\0"
1327 "ar\0\0\0\0" "fi_FI\0" "fr_CH\0" "de_CH\0" "el_GR\0" "is_IS\0" "mt_MT\0" "el_CY\0"
1328 "tr_TR\0" "hr_HR\0" "nl_NL\0" "nl_BE\0" "en_CA\0" "en_CA\0" "pt_PT\0" "nb_NO\0"
1329 "da_DK\0" "hi_IN\0" "ur_PK\0" "tr_TR\0" "it_CH\0" "en\0\0\0\0" "\0\0\0\0\0\0" "ro_RO\0"
1330 "grc\0\0\0" "lt_LT\0" "pl_PL\0" "hu_HU\0" "et_EE\0" "lv_LV\0" "se\0\0\0\0" "fo_FO\0"
1331 "fa_IR\0" "ru_RU\0" "ga_IE\0" "ko_KR\0" "zh_CN\0" "zh_TW\0" "th_TH\0" "\0\0\0\0\0\0"
1332 "cs_CZ\0" "sk_SK\0" "\0\0\0\0\0\0" "hu_HU\0" "bn\0\0\0\0" "be_BY\0" "uk_UA\0" "\0\0\0\0\0\0"
1333 "el_GR\0" "sr_CS\0" "sl_SI\0" "mk_MK\0" "hr_HR\0" "\0\0\0\0\0\0" "de_DE\0" "pt_BR\0"
1334 "bg_BG\0" "ca_ES\0" "\0\0\0\0\0\0" "gd\0\0\0\0" "gv\0\0\0\0" "br\0\0\0\0" "iu_CA\0" "cy\0\0\0\0"
1335 "en_CA\0" "ga_IE\0" "en_CA\0" "dz_BT\0" "hy_AM\0" "ka_GE\0" "es_XL\0" "es_ES\0"
1336 "to_TO\0" "pl_PL\0" "ca_ES\0" "fr\0\0\0\0" "de_AT\0" "es_XL\0" "gu_IN\0" "pa\0\0\0\0"
1337 "ur_IN\0" "vi_VN\0" "fr_BE\0" "uz_UZ\0" "en_SG\0" "nn_NO\0" "af_ZA\0" "eo\0\0\0\0"
1338 "mr_IN\0" "bo\0\0\0\0" "ne_NP\0" "kl\0\0\0\0" "en_IE\0";
1339
1340#define NUM_LOCALE_ABBREVIATIONS 109
1341#define LOCALE_ABBREVIATION_LENGTH 6
1342
1343static const char * const __CFBundleLanguageNamesArray[] = {
1344 "English", "French", "German", "Italian", "Dutch", "Swedish", "Spanish", "Danish",
1345 "Portuguese", "Norwegian", "Hebrew", "Japanese", "Arabic", "Finnish", "Greek", "Icelandic",
1346 "Maltese", "Turkish", "Croatian", "Chinese", "Urdu", "Hindi", "Thai", "Korean",
1347 "Lithuanian", "Polish", "Hungarian", "Estonian", "Latvian", "Sami", "Faroese", "Farsi",
1348 "Russian", "Chinese", "Dutch", "Irish", "Albanian", "Romanian", "Czech", "Slovak",
1349 "Slovenian", "Yiddish", "Serbian", "Macedonian", "Bulgarian", "Ukrainian", "Byelorussian", "Uzbek",
1350 "Kazakh", "Azerbaijani", "Azerbaijani", "Armenian", "Georgian", "Moldavian", "Kirghiz", "Tajiki",
1351 "Turkmen", "Mongolian", "Mongolian", "Pashto", "Kurdish", "Kashmiri", "Sindhi", "Tibetan",
1352 "Nepali", "Sanskrit", "Marathi", "Bengali", "Assamese", "Gujarati", "Punjabi", "Oriya",
1353 "Malayalam", "Kannada", "Tamil", "Telugu", "Sinhalese", "Burmese", "Khmer", "Lao",
1354 "Vietnamese", "Indonesian", "Tagalog", "Malay", "Malay", "Amharic", "Tigrinya", "Oromo",
1355 "Somali", "Swahili", "Kinyarwanda", "Rundi", "Nyanja", "Malagasy", "Esperanto", "",
1356 "", "", "", "", "", "", "", "",
1357 "", "", "", "", "", "", "", "",
1358 "", "", "", "", "", "", "", "",
1359 "", "", "", "", "", "", "", "",
1360 "Welsh", "Basque", "Catalan", "Latin", "Quechua", "Guarani", "Aymara", "Tatar",
1361 "Uighur", "Dzongkha", "Javanese", "Sundanese", "Galician", "Afrikaans", "Breton", "Inuktitut",
1362 "Scottish", "Manx", "Irish", "Tongan", "Greek", "Greenlandic", "Azerbaijani", "Nynorsk"
1363};
1364
1365#define NUM_LANGUAGE_NAMES 152
1366#define LANGUAGE_NAME_LENGTH 13
1367
1368// string, with groups of 3 characters being 1 element in the array of abbreviations
1369const char * __CFBundleLanguageAbbreviationsArray =
1370 "en\0" "fr\0" "de\0" "it\0" "nl\0" "sv\0" "es\0" "da\0"
1371 "pt\0" "nb\0" "he\0" "ja\0" "ar\0" "fi\0" "el\0" "is\0"
1372 "mt\0" "tr\0" "hr\0" "zh\0" "ur\0" "hi\0" "th\0" "ko\0"
1373 "lt\0" "pl\0" "hu\0" "et\0" "lv\0" "se\0" "fo\0" "fa\0"
1374 "ru\0" "zh\0" "nl\0" "ga\0" "sq\0" "ro\0" "cs\0" "sk\0"
1375 "sl\0" "yi\0" "sr\0" "mk\0" "bg\0" "uk\0" "be\0" "uz\0"
1376 "kk\0" "az\0" "az\0" "hy\0" "ka\0" "mo\0" "ky\0" "tg\0"
1377 "tk\0" "mn\0" "mn\0" "ps\0" "ku\0" "ks\0" "sd\0" "bo\0"
1378 "ne\0" "sa\0" "mr\0" "bn\0" "as\0" "gu\0" "pa\0" "or\0"
1379 "ml\0" "kn\0" "ta\0" "te\0" "si\0" "my\0" "km\0" "lo\0"
1380 "vi\0" "id\0" "tl\0" "ms\0" "ms\0" "am\0" "ti\0" "om\0"
1381 "so\0" "sw\0" "rw\0" "rn\0" "\0\0\0" "mg\0" "eo\0" "\0\0\0"
1382 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1383 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1384 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1385 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1386 "cy\0" "eu\0" "ca\0" "la\0" "qu\0" "gn\0" "ay\0" "tt\0"
1387 "ug\0" "dz\0" "jv\0" "su\0" "gl\0" "af\0" "br\0" "iu\0"
1388 "gd\0" "gv\0" "ga\0" "to\0" "el\0" "kl\0" "az\0" "nn\0";
1389
1390#define NUM_LANGUAGE_ABBREVIATIONS 152
1391#define LANGUAGE_ABBREVIATION_LENGTH 3
1392
1393#if defined(__CONSTANT_CFSTRINGS__)
1394
1395// These are not necessarily common localizations per se, but localizations for which the full language name is still in common use.
1396// These are used to provide a fast path for it (other localizations usually use the abbreviation, which is even faster).
1397static CFStringRef const __CFBundleCommonLanguageNamesArray[] = {CFSTR("English"), CFSTR("French"), CFSTR("German"), CFSTR("Italian"), CFSTR("Dutch"), CFSTR("Spanish"), CFSTR("Japanese")};
1398static CFStringRef const __CFBundleCommonLanguageAbbreviationsArray[] = {CFSTR("en"), CFSTR("fr"), CFSTR("de"), CFSTR("it"), CFSTR("nl"), CFSTR("es"), CFSTR("ja")};
1399
1400#define NUM_COMMON_LANGUAGE_NAMES 7
1401
1402#endif /* __CONSTANT_CFSTRINGS__ */
1403
1404static const SInt32 __CFBundleScriptCodesArray[] = {
1405 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 0,
1406 0, 0, 0, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 0, 4,
1407 7, 25, 0, 0, 0, 0, 29, 29, 0, 5, 7, 7, 7, 7, 7, 7,
1408 7, 7, 4, 24, 23, 7, 7, 7, 7, 27, 7, 4, 4, 4, 4, 26,
1409 9, 9, 9, 13, 13, 11, 10, 12, 17, 16, 14, 15, 18, 19, 20, 22,
1410 30, 0, 0, 0, 4, 28, 28, 28, 0, 0, 0, 0, 0, 0, 0, 0,
1411 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1412 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1413 0, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 0, 28,
1414 0, 0, 0, 0, 6, 0, 0, 0
1415};
1416
1417static const CFStringEncoding __CFBundleStringEncodingsArray[] = {
1418 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 37,
1419 0, 35, 36, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 37, 0x8C,
1420 7, 25, 0, 39, 0, 38, 29, 29, 36, 5, 7, 7, 7, 0x98, 7, 7,
1421 7, 7, 4, 24, 23, 7, 7, 7, 7, 27, 7, 4, 4, 4, 4, 26,
1422 9, 9, 9, 13, 13, 11, 10, 12, 17, 16, 14, 15, 18, 19, 20, 22,
1423 30, 0, 0, 0, 4, 28, 28, 28, 0, 0, 0, 0, 0, 0, 0, 0,
1424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1426 39, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 39, 0xEC,
1427 39, 39, 40, 0, 6, 0, 0, 0
1428};
1429
1430static SInt32 _CFBundleGetLanguageCodeForLocalization(CFStringRef localizationName) {
1431 SInt32 result = -1, i;
1432 char buff[256];
1433 CFIndex length = CFStringGetLength(localizationName);
1434 if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= 255 && CFStringGetCString(localizationName, buff, 255, kCFStringEncodingASCII)) {
1435 buff[255] = '\0';
1436 for (i = 0; -1 == result && i < NUM_LANGUAGE_NAMES; i++) {
1437 if (0 == strcmp(buff, __CFBundleLanguageNamesArray[i])) result = i;
1438 }
1439 if (0 == strcmp(buff, "zh_TW") || 0 == strcmp(buff, "zh-Hant")) result = 19; else if (0 == strcmp(buff, "zh_CN") || 0 == strcmp(buff, "zh-Hans")) result = 33; // hack for mixed-up Chinese language codes
1440 if (-1 == result && (length == LANGUAGE_ABBREVIATION_LENGTH - 1 || !isalpha(buff[LANGUAGE_ABBREVIATION_LENGTH - 1]))) {
1441 buff[LANGUAGE_ABBREVIATION_LENGTH - 1] = '\0';
1442 if ('n' == buff[0] && 'o' == buff[1]) result = 9; // hack for Norwegian
1443 for (i = 0; -1 == result && i < NUM_LANGUAGE_ABBREVIATIONS * LANGUAGE_ABBREVIATION_LENGTH; i += LANGUAGE_ABBREVIATION_LENGTH) {
1444 if (buff[0] == *(__CFBundleLanguageAbbreviationsArray + i + 0) && buff[1] == *(__CFBundleLanguageAbbreviationsArray + i + 1)) result = i / LANGUAGE_ABBREVIATION_LENGTH;
1445 }
1446 }
1447 }
1448 return result;
1449}
1450
1451static CFStringRef _CFBundleCopyLanguageAbbreviationForLanguageCode(SInt32 languageCode) {
1452 CFStringRef result = NULL;
1453 if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) {
1454 const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH;
1455 if (languageAbbreviation && *languageAbbreviation != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull);
1456 }
1457 return result;
1458}
1459
1460CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) {
1461 CFStringRef result = NULL;
1462 if (0 <= languageCode && languageCode < NUM_LANGUAGE_NAMES) {
1463 const char *languageName = __CFBundleLanguageNamesArray[languageCode];
1464 if (languageName && *languageName != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageName, kCFStringEncodingASCII, kCFAllocatorNull);
1465 }
1466 return result;
1467}
1468
1469CF_INLINE CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName) {
1470 CFStringRef result = NULL;
1471 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1472 if (languageCode >= 0) {
1473 result = _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode);
1474 } else {
1475 CFIndex length = CFStringGetLength(localizationName);
1476 if (length == LANGUAGE_ABBREVIATION_LENGTH - 1 || (length > LANGUAGE_ABBREVIATION_LENGTH - 1 && CFStringGetCharacterAtIndex(localizationName, LANGUAGE_ABBREVIATION_LENGTH - 1) == '_')) {
1477 result = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, localizationName, CFRangeMake(0, LANGUAGE_ABBREVIATION_LENGTH - 1));
1478 }
1479 }
1480 return result;
1481}
1482
1483CF_INLINE CFStringRef _CFBundleCopyModifiedLocalization(CFStringRef localizationName) {
1484 CFMutableStringRef result = NULL;
1485 CFIndex length = CFStringGetLength(localizationName);
1486 if (length >= 4) {
1487 UniChar c = CFStringGetCharacterAtIndex(localizationName, 2);
1488 if ('-' == c || '_' == c) {
1489 result = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, length, localizationName);
1490 CFStringReplace(result, CFRangeMake(2, 1), ('-' == c) ? CFSTR("_") : CFSTR("-"));
1491 }
1492 }
1493 return result;
1494}
1495
1496CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) {
1497 CFStringRef result = NULL;
1498 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1499 if (languageCode >= 0) {
1500 result = _CFBundleCopyLanguageNameForLanguageCode(languageCode);
1501 } else {
1502 result = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, localizationName);
1503 }
1504 return result;
1505}
1506
1507static SInt32 _CFBundleGetLanguageCodeForRegionCode(SInt32 regionCode) {
1508 SInt32 result = -1, i;
1509 if (52 == regionCode) { // hack for mixed-up Chinese language codes
1510 result = 33;
1511 } else if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) {
1512 const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH;
1513 if (localeAbbreviation && *localeAbbreviation != '\0') {
1514 for (i = 0; -1 == result && i < NUM_LANGUAGE_ABBREVIATIONS * LANGUAGE_ABBREVIATION_LENGTH; i += LANGUAGE_ABBREVIATION_LENGTH) {
1515 if (localeAbbreviation[0] == *(__CFBundleLanguageAbbreviationsArray + i + 0) && localeAbbreviation[1] == *(__CFBundleLanguageAbbreviationsArray + i + 1)) result = i / LANGUAGE_ABBREVIATION_LENGTH;
1516 }
1517 }
1518 }
1519 return result;
1520}
1521
1522static SInt32 _CFBundleGetRegionCodeForLanguageCode(SInt32 languageCode) {
1523 SInt32 result = -1, i;
1524 if (19 == languageCode) { // hack for mixed-up Chinese language codes
1525 result = 53;
1526 } else if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) {
1527 const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH;
1528 if (languageAbbreviation && *languageAbbreviation != '\0') {
1529 for (i = 0; -1 == result && i < NUM_LOCALE_ABBREVIATIONS * LOCALE_ABBREVIATION_LENGTH; i += LOCALE_ABBREVIATION_LENGTH) {
1530 if (*(__CFBundleLocaleAbbreviationsArray + i + 0) == languageAbbreviation[0] && *(__CFBundleLocaleAbbreviationsArray + i + 1) == languageAbbreviation[1]) result = i / LOCALE_ABBREVIATION_LENGTH;
1531 }
1532 }
1533 }
1534 if (25 == result) result = 68;
1535 if (28 == result) result = 82;
1536 return result;
1537}
1538
1539static SInt32 _CFBundleGetRegionCodeForLocalization(CFStringRef localizationName) {
1540 SInt32 result = -1, i;
1541 char buff[LOCALE_ABBREVIATION_LENGTH];
1542 CFIndex length = CFStringGetLength(localizationName);
1543 if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= LOCALE_ABBREVIATION_LENGTH - 1 && CFStringGetCString(localizationName, buff, LOCALE_ABBREVIATION_LENGTH, kCFStringEncodingASCII)) {
1544 buff[LOCALE_ABBREVIATION_LENGTH - 1] = '\0';
1545 for (i = 0; -1 == result && i < NUM_LOCALE_ABBREVIATIONS * LOCALE_ABBREVIATION_LENGTH; i += LOCALE_ABBREVIATION_LENGTH) {
1546 if (0 == strcmp(buff, __CFBundleLocaleAbbreviationsArray + i)) result = i / LOCALE_ABBREVIATION_LENGTH;
1547 }
1548 }
1549 if (25 == result) result = 68;
1550 if (28 == result) result = 82;
1551 if (37 == result) result = 0;
1552 if (-1 == result) {
1553 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1554 result = _CFBundleGetRegionCodeForLanguageCode(languageCode);
1555 }
1556 return result;
1557}
1558
1559static CFStringRef _CFBundleCopyLocaleAbbreviationForRegionCode(SInt32 regionCode) {
1560 CFStringRef result = NULL;
1561 if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) {
1562 const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH;
1563 if (localeAbbreviation && *localeAbbreviation != '\0') {
1564 result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, localeAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull);
1565 }
1566 }
1567 return result;
1568}
1569
1570Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding) {
1571 Boolean retval = false;
1572 SInt32 language = -1, region = -1, script = 0;
1573 CFStringEncoding encoding = kCFStringEncodingMacRoman;
1574 if (!localizationName) {
1575 CFBundleRef mainBundle = CFBundleGetMainBundle();
1576 CFArrayRef languages = NULL;
1577 if (mainBundle) {
1578 languages = _CFBundleGetLanguageSearchList(mainBundle);
1579 if (languages) CFRetain(languages);
1580 }
1581 if (!languages) languages = _CFBundleCopyUserLanguages(false);
1582 if (languages && CFArrayGetCount(languages) > 0) localizationName = (CFStringRef)CFArrayGetValueAtIndex(languages, 0);
1583 }
1584 if (localizationName) {
1585 LangCode langCode = -1;
1586 RegionCode regCode = -1;
1587 ScriptCode scrCode = 0;
1588 CFStringEncoding enc = kCFStringEncodingMacRoman;
1589 retval = CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(localizationName, &langCode, &regCode, &scrCode, &enc);
1590 if (retval) {
1591 language = langCode;
1592 region = regCode;
1593 script = scrCode;
1594 encoding = enc;
1595 }
1596 }
1597 if (!retval) {
1598 if (localizationName) {
1599 language = _CFBundleGetLanguageCodeForLocalization(localizationName);
1600 region = _CFBundleGetRegionCodeForLocalization(localizationName);
1601 } else {
1602 _CFBundleGetLanguageAndRegionCodes(&language, &region);
1603 }
1604 if ((language < 0 || language > (int)(sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32))) && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region);
1605 if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language);
1606 if (language >= 0 && language < (int)(sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32))) {
1607 script = __CFBundleScriptCodesArray[language];
1608 }
1609 if (language >= 0 && language < (int)(sizeof(__CFBundleStringEncodingsArray)/sizeof(CFStringEncoding))) {
1610 encoding = __CFBundleStringEncodingsArray[language];
1611 }
1612 retval = (language != -1 || region != -1);
1613 }
1614 if (languageCode) *languageCode = language;
1615 if (regionCode) *regionCode = region;
1616 if (scriptCode) *scriptCode = script;
1617 if (stringEncoding) *stringEncoding = encoding;
1618 return retval;
1619}
1620
1621CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding) {
1622 CFStringRef localizationName = NULL;
1623 if (!localizationName) localizationName = _CFBundleCopyLocaleAbbreviationForRegionCode(regionCode);
1624#if DEPLOYMENT_TARGET_MACOSX
1625 if (!localizationName && 0 <= languageCode && languageCode < SHRT_MAX) localizationName = CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(kCFAllocatorSystemDefault, (LangCode)languageCode, (RegionCode)-1);
1626#endif
1627 if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode);
1628 if (!localizationName) {
1629 SInt32 language = -1, scriptLanguage = -1, encodingLanguage = -1;
1630 unsigned int i;
1631 for (i = 0; language == -1 && i < (sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32)); i++) {
1632 if (__CFBundleScriptCodesArray[i] == scriptCode && __CFBundleStringEncodingsArray[i] == stringEncoding) language = i;
1633 }
1634 for (i = 0; scriptLanguage == -1 && i < (sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32)); i++) {
1635 if (__CFBundleScriptCodesArray[i] == scriptCode) scriptLanguage = i;
1636 }
1637 for (i = 0; encodingLanguage == -1 && i < (sizeof(__CFBundleStringEncodingsArray)/sizeof(CFStringEncoding)); i++) {
1638 if (__CFBundleStringEncodingsArray[i] == stringEncoding) encodingLanguage = i;
1639 }
1640 localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(language);
1641 if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(encodingLanguage);
1642 if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(scriptLanguage);
1643 }
1644 return localizationName;
1645}
1646
1647extern void *__CFAppleLanguages;
1648
1649
1650__private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops) {
1651 static CFArrayRef _CFBundleUserLanguages = NULL;
1652 static dispatch_once_t once = 0;
1653 dispatch_once(&once, ^{
1654 CFArrayRef preferencesArray = NULL;
1655 if (__CFAppleLanguages) {
1656 CFDataRef data;
1657 CFIndex length = strlen((const char *)__CFAppleLanguages);
1658 if (length > 0) {
1659 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8 *)__CFAppleLanguages, length, kCFAllocatorNull);
1660 if (data) {
1661 _CFBundleUserLanguages = (CFArrayRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL);
1662 CFRelease(data);
1663 }
1664 }
1665 }
1666 if (!_CFBundleUserLanguages && preferencesArray) _CFBundleUserLanguages = (CFArrayRef)CFRetain(preferencesArray);
1667 Boolean useEnglishAsBackstop = true;
1668 // could perhaps read out of LANG environment variable
1669 if (useEnglishAsBackstop && !_CFBundleUserLanguages) {
1670 CFStringRef english = CFSTR("en");
1671 _CFBundleUserLanguages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&english, 1, &kCFTypeArrayCallBacks);
1672 }
1673 if (_CFBundleUserLanguages && CFGetTypeID(_CFBundleUserLanguages) != CFArrayGetTypeID()) {
1674 CFRelease(_CFBundleUserLanguages);
1675 _CFBundleUserLanguages = NULL;
1676 }
1677 if (preferencesArray) CFRelease(preferencesArray);
1678 });
1679
1680 if (_CFBundleUserLanguages) {
1681 CFRetain(_CFBundleUserLanguages);
1682 return _CFBundleUserLanguages;
1683 } else {
1684 return NULL;
1685 }
1686}
1687
1688CF_EXPORT void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 *regionCode) {
1689 // an attempt to answer the question, "what language are we running in?"
1690 // note that the question cannot be answered fully since it may depend on the bundle
1691 SInt32 language = -1, region = -1;
1692 CFBundleRef mainBundle = CFBundleGetMainBundle();
1693 CFArrayRef languages = NULL;
1694 if (mainBundle) {
1695 languages = _CFBundleGetLanguageSearchList(mainBundle);
1696 if (languages) CFRetain(languages);
1697 }
1698 if (!languages) languages = _CFBundleCopyUserLanguages(false);
1699 if (languages && CFArrayGetCount(languages) > 0) {
1700 CFStringRef localizationName = (CFStringRef)CFArrayGetValueAtIndex(languages, 0);
1701 Boolean retval = false;
1702 LangCode langCode = -1;
1703 RegionCode regCode = -1;
1704 retval = CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(localizationName, &langCode, &regCode, NULL, NULL);
1705 if (retval) {
1706 language = langCode;
1707 region = regCode;
1708 }
1709 if (!retval) {
1710 language = _CFBundleGetLanguageCodeForLocalization(localizationName);
1711 region = _CFBundleGetRegionCodeForLocalization(localizationName);
1712 }
1713 } else {
1714 language = 0;
1715 region = 0;
1716 }
1717 if (language == -1 && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region);
1718 if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language);
1719 if (languages) CFRelease(languages);
1720 if (languageCode) *languageCode = language;
1721 if (regionCode) *regionCode = region;
1722}
1723
1724
1725static Boolean _CFBundleTryOnePreferredLprojNameInDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, uint8_t version, CFDictionaryRef infoDict, CFStringRef curLangStr, CFMutableArrayRef lprojNames, Boolean fallBackToLanguage) {
1726 CFIndex curLangLen = CFStringGetLength(curLangStr), savedPathLen;
1727 UniChar curLangUniChars[255];
1728 CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL, canonicalLanguageAbbreviation = NULL;
1729 CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL, predefinedCanonicalLanguageIdentifiers = NULL;
1730 Boolean foundOne = false, specifiesScript = false;
1731 CFArrayRef predefinedLocalizations = NULL;
1732 CFRange predefinedLocalizationsRange;
1733 CFMutableStringRef cheapStr, tmpString;
1734 CFArrayRef contents;
1735 CFRange contentsRange;
1736
1737 // both of these used for temp string operations, for slightly
1738 // different purposes, where each type is appropriate
1739 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
1740 _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize);
1741 tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
1742
1743 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1744 CFStringReplaceAll(cheapStr, tmpString);
1745 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
1746 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
1747
1748 if (infoDict) {
1749 predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
1750 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) {
1751 predefinedLocalizations = NULL;
1752 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey);
1753 }
1754 }
1755 predefinedLocalizationsRange = CFRangeMake(0, predefinedLocalizations ? CFArrayGetCount(predefinedLocalizations) : 0);
1756
1757 if (curLangLen > 255) curLangLen = 255;
1758 CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1759 savedPathLen = pathLen;
1760 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1761 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1762 CFStringReplaceAll(cheapStr, tmpString);
1763 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1764 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr);
1765 foundOne = true;
1766 if (CFStringGetLength(curLangStr) <= 2) {
1767 CFRelease(cheapStr);
1768 CFRelease(tmpString);
1769 CFRelease(contents);
1770 return foundOne;
1771 }
1772 }
1773 }
1774#if defined(__CONSTANT_CFSTRINGS__)
1775 if (!altLangStr) {
1776 CFIndex idx;
1777 for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) {
1778 if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx];
1779 else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx];
1780 }
1781 }
1782#endif /* __CONSTANT_CFSTRINGS__ */
1783 if (foundOne && altLangStr) {
1784 CFRelease(cheapStr);
1785 CFRelease(tmpString);
1786 CFRelease(contents);
1787 return foundOne;
1788 }
1789 if (altLangStr) {
1790 curLangLen = CFStringGetLength(altLangStr);
1791 if (curLangLen > 255) curLangLen = 255;
1792 CFStringGetCharacters(altLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1793 pathLen = savedPathLen;
1794 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1795 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1796 CFStringReplaceAll(cheapStr, tmpString);
1797 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1798 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr);
1799 foundOne = true;
1800 CFRelease(cheapStr);
1801 CFRelease(tmpString);
1802 CFRelease(contents);
1803 return foundOne;
1804 }
1805 }
1806 }
1807 if (!foundOne && (!predefinedLocalizations || CFArrayGetCount(predefinedLocalizations) == 0)) {
1808 Boolean hasLocalizations = false;
1809 CFIndex idx;
1810 for (idx = 0; !hasLocalizations && idx < contentsRange.length; idx++) {
1811 CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(contents, idx);
1812 if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) hasLocalizations = true;
1813 }
1814 if (!hasLocalizations) {
1815 CFRelease(cheapStr);
1816 CFRelease(tmpString);
1817 CFRelease(contents);
1818 return foundOne;
1819 }
1820 }
1821 if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) {
1822 curLangLen = CFStringGetLength(modifiedLangStr);
1823 if (curLangLen > 255) curLangLen = 255;
1824 CFStringGetCharacters(modifiedLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1825 pathLen = savedPathLen;
1826 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1827 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1828 CFStringReplaceAll(cheapStr, tmpString);
1829 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1830 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr);
1831 foundOne = true;
1832 }
1833 }
1834 }
1835 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) {
1836 curLangLen = CFStringGetLength(languageAbbreviation);
1837 if (curLangLen > 255) curLangLen = 255;
1838 CFStringGetCharacters(languageAbbreviation, CFRangeMake(0, curLangLen), curLangUniChars);
1839 pathLen = savedPathLen;
1840 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1841 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1842 CFStringReplaceAll(cheapStr, tmpString);
1843 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1844 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation);
1845 foundOne = true;
1846 }
1847 }
1848 }
1849 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) {
1850 curLangLen = CFStringGetLength(languageName);
1851 if (curLangLen > 255) curLangLen = 255;
1852 CFStringGetCharacters(languageName, CFRangeMake(0, curLangLen), curLangUniChars);
1853 pathLen = savedPathLen;
1854 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1855 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1856 CFStringReplaceAll(cheapStr, tmpString);
1857 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1858 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName);
1859 foundOne = true;
1860 }
1861 }
1862 }
1863 if (modifiedLangStr) CFRelease(modifiedLangStr);
1864 if (languageAbbreviation) CFRelease(languageAbbreviation);
1865 if (languageName) CFRelease(languageName);
1866 if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier);
1867 if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers);
1868 if (predefinedCanonicalLanguageIdentifiers) CFRelease(predefinedCanonicalLanguageIdentifiers);
1869 if (canonicalLanguageAbbreviation) CFRelease(canonicalLanguageAbbreviation);
1870 CFRelease(cheapStr);
1871 CFRelease(tmpString);
1872 CFRelease(contents);
1873 return foundOne;
1874}
1875
1876static Boolean CFBundleAllowMixedLocalizations(void) {
1877 static Boolean allowMixed = false;
1878 static dispatch_once_t once = 0;
1879 dispatch_once(&once, ^{
1880 CFBundleRef mainBundle = CFBundleGetMainBundle();
1881 CFDictionaryRef infoDict = mainBundle ? CFBundleGetInfoDictionary(mainBundle) : NULL;
1882 CFTypeRef allowMixedValue = infoDict ? CFDictionaryGetValue(infoDict, _kCFBundleAllowMixedLocalizationsKey) : NULL;
1883 if (allowMixedValue) {
1884 CFTypeID typeID = CFGetTypeID(allowMixedValue);
1885 if (typeID == CFBooleanGetTypeID()) {
1886 allowMixed = CFBooleanGetValue((CFBooleanRef)allowMixedValue);
1887 } else if (typeID == CFStringGetTypeID()) {
1888 allowMixed = (CFStringCompare((CFStringRef)allowMixedValue, CFSTR("true"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)allowMixedValue, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo);
1889 } else if (typeID == CFNumberGetTypeID()) {
1890 SInt32 val = 0;
1891 if (CFNumberGetValue((CFNumberRef)allowMixedValue, kCFNumberSInt32Type, &val)) allowMixed = (val != 0);
1892 }
1893 }
1894 });
1895 return allowMixed;
1896}
1897
1898static Boolean _CFBundleLocalizationsHaveCommonPrefix(CFStringRef loc1, CFStringRef loc2) {
1899 Boolean result = false;
1900 CFIndex length1 = CFStringGetLength(loc1), length2 = CFStringGetLength(loc2), idx;
1901 if (length1 > 3 && length2 > 3) {
1902 for (idx = 0; idx < length1 && idx < length2; idx++) {
1903 UniChar c1 = CFStringGetCharacterAtIndex(loc1, idx), c2 = CFStringGetCharacterAtIndex(loc2, idx);
1904 if (idx >= 2 && (c1 == '-' || c1 == '_') && (c2 == '-' || c2 == '_')) {
1905 result = true;
1906 break;
1907 } else if (c1 != c2) {
1908 break;
1909 }
1910 }
1911 }
1912 return result;
1913}
1914
1915__private_extern__ void _CFBundleAddPreferredLprojNamesInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, uint8_t version, CFDictionaryRef infoDict, CFMutableArrayRef lprojNames, CFStringRef devLang) {
1916 // This function will add zero, one or two elements to the lprojNames array.
1917 // It examines the users preferred language list and the lproj directories inside the bundle directory. It picks the lproj directory that is highest on the users list.
1918 // The users list can contain region names (like "en_US" for US English). In this case, if the region lproj exists, it will be added, and, if the region's associated language lproj exists that will be added.
1919 CFURLRef resourcesURL = _CFBundleCopyResourcesDirectoryURLInDirectory(bundleURL, version);
1920 CFURLRef absoluteURL;
1921 CFIndex idx, startIdx;
1922 CFIndex count;
1923 CFStringRef resourcesPath;
1924 UniChar pathUniChars[CFMaxPathSize];
1925 CFIndex pathLen;
1926 CFStringRef curLangStr, nextLangStr;
1927 Boolean foundOne = false;
1928 CFArrayRef userLanguages;
1929
1930 // Init the one-time-only unichar buffers.
1931 _CFEnsureStaticBuffersInited();
1932
1933 // Get the path to the resources and extract into a buffer.
1934 absoluteURL = CFURLCopyAbsoluteURL(resourcesURL);
1935 resourcesPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
1936 CFRelease(absoluteURL);
1937 pathLen = CFStringGetLength(resourcesPath);
1938 if (pathLen > CFMaxPathSize) pathLen = CFMaxPathSize;
1939 CFStringGetCharacters(resourcesPath, CFRangeMake(0, pathLen), pathUniChars);
1940 CFRelease(resourcesURL);
1941 CFRelease(resourcesPath);
1942
1943 // First check the main bundle.
1944 if (!CFBundleAllowMixedLocalizations()) {
1945 CFBundleRef mainBundle = CFBundleGetMainBundle();
1946 if (mainBundle) {
1947 CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
1948 if (mainBundleURL) {
1949 if (!CFEqual(bundleURL, mainBundleURL)) {
1950 // If there is a main bundle, and it isn't this one, try to use the language it prefers.
1951 CFArrayRef mainBundleLangs = _CFBundleGetLanguageSearchList(mainBundle);
1952 if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) {
1953 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0);
1954 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
1955 }
1956 }
1957 CFRelease(mainBundleURL);
1958 }
1959 }
1960 }
1961
1962 if (!foundOne) {
1963 // If we didn't find the main bundle's preferred language, look at the users' prefs again and find the best one.
1964 userLanguages = _CFBundleCopyUserLanguages(true);
1965 count = (userLanguages ? CFArrayGetCount(userLanguages) : 0);
1966 for (idx = 0, startIdx = -1; !foundOne && idx < count; idx++) {
1967 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx);
1968 nextLangStr = (idx + 1 < count) ? (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx + 1) : NULL;
1969 if (nextLangStr && _CFBundleLocalizationsHaveCommonPrefix(curLangStr, nextLangStr)) {
1970 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, false);
1971 if (startIdx < 0) startIdx = idx;
1972 } else if (startIdx >= 0 && startIdx <= idx) {
1973 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, false);
1974 for (; !foundOne && startIdx <= idx; startIdx++) {
1975 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, startIdx);
1976 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
1977 }
1978 startIdx = -1;
1979 } else {
1980 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
1981 startIdx = -1;
1982 }
1983 }
1984 // use development region and U.S. English as backstops
1985 if (!foundOne && devLang) foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, devLang, lprojNames, true);
1986 if (!foundOne) foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, CFSTR("en_US"), lprojNames, true);
1987 if (userLanguages) CFRelease(userLanguages);
1988 }
1989}
1990
1991static Boolean _CFBundleTryOnePreferredLprojNameInArray(CFArrayRef array, CFStringRef curLangStr, CFMutableArrayRef lprojNames, Boolean fallBackToLanguage) {
1992 Boolean foundOne = false, specifiesScript = false;
1993 CFRange range = CFRangeMake(0, CFArrayGetCount(array));
1994 CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL, canonicalLanguageAbbreviation = NULL;
1995 CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL;
1996
1997 if (range.length == 0) return foundOne;
1998 if (CFArrayContainsValue(array, range, curLangStr)) {
1999 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr);
2000 foundOne = true;
2001 if (range.length == 1 || CFStringGetLength(curLangStr) <= 2) return foundOne;
2002 }
2003 if (range.length == 1 && CFArrayContainsValue(array, range, CFSTR("default"))) return foundOne;
2004#if defined(__CONSTANT_CFSTRINGS__)
2005 if (!altLangStr) {
2006 CFIndex idx;
2007 for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) {
2008 if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx];
2009 else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx];
2010 }
2011 }
2012#endif /* __CONSTANT_CFSTRINGS__ */
2013 if (foundOne && altLangStr) return foundOne;
2014 if (altLangStr && CFArrayContainsValue(array, range, altLangStr)) {
2015 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr);
2016 foundOne = true;
2017 return foundOne;
2018 }
2019 if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) {
2020 if (CFArrayContainsValue(array, range, modifiedLangStr)) {
2021 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr);
2022 foundOne = true;
2023 }
2024 }
2025 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) {
2026 if (CFArrayContainsValue(array, range, languageAbbreviation)) {
2027 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation);
2028 foundOne = true;
2029 }
2030 }
2031 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) {
2032 if (CFArrayContainsValue(array, range, languageName)) {
2033 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName);
2034 foundOne = true;
2035 }
2036 }
2037 if (modifiedLangStr) CFRelease(modifiedLangStr);
2038 if (languageAbbreviation) CFRelease(languageAbbreviation);
2039 if (languageName) CFRelease(languageName);
2040 if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier);
2041 if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers);
2042 if (canonicalLanguageAbbreviation) CFRelease(canonicalLanguageAbbreviation);
2043 return foundOne;
2044}
2045
2046static CFArrayRef _CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray, Boolean considerMain) {
2047 CFMutableArrayRef lprojNames = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
2048 Boolean foundOne = false, releasePrefArray = false;
2049 CFIndex idx, count, startIdx;
2050
2051 if (considerMain && !CFBundleAllowMixedLocalizations()) {
2052 CFBundleRef mainBundle = CFBundleGetMainBundle();
2053 if (mainBundle) {
2054 // If there is a main bundle, try to use the language it prefers.
2055 CFArrayRef mainBundleLangs = _CFBundleGetLanguageSearchList(mainBundle);
2056 if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0), lprojNames, true);
2057 }
2058 }
2059
2060 if (!foundOne) {
2061 CFStringRef curLangStr, nextLangStr;
2062 if (!prefArray) {
2063 prefArray = _CFBundleCopyUserLanguages(true);
2064 if (prefArray) releasePrefArray = true;
2065 }
2066 count = (prefArray ? CFArrayGetCount(prefArray) : 0);
2067 for (idx = 0, startIdx = -1; !foundOne && idx < count; idx++) {
2068 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(prefArray, idx);
2069 nextLangStr = (idx + 1 < count) ? (CFStringRef)CFArrayGetValueAtIndex(prefArray, idx + 1) : NULL;
2070 if (nextLangStr && _CFBundleLocalizationsHaveCommonPrefix(curLangStr, nextLangStr)) {
2071 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, false);
2072 if (startIdx < 0) startIdx = idx;
2073 } else if (startIdx >= 0 && startIdx <= idx) {
2074 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, false);
2075 for (; !foundOne && startIdx <= idx; startIdx++) {
2076 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(prefArray, startIdx);
2077 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, true);
2078 }
2079 startIdx = -1;
2080 } else {
2081 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, true);
2082 startIdx = -1;
2083 }
2084 }
2085 // use U.S. English as backstop
2086 if (!foundOne) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, CFSTR("en_US"), lprojNames, true);
2087 // use random entry as backstop
2088 if (!foundOne && CFArrayGetCount(locArray) > 0) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(locArray, 0), lprojNames, true);
2089 }
2090 if (CFArrayGetCount(lprojNames) == 0) {
2091 // Total backstop behavior to avoid having an empty array.
2092 CFArrayAppendValue(lprojNames, CFSTR("en"));
2093 }
2094 if (releasePrefArray) {
2095 CFRelease(prefArray);
2096 }
2097 return lprojNames;
2098}
2099
2100CF_EXPORT CFArrayRef CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray) {
2101 return _CFBundleCopyLocalizationsForPreferences(locArray, prefArray, false);
2102}
2103
2104CF_EXPORT CFArrayRef CFBundleCopyPreferredLocalizationsFromArray(CFArrayRef locArray) {
2105 return _CFBundleCopyLocalizationsForPreferences(locArray, NULL, true);
2106}
2107
2108__private_extern__ CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) {
2109 CFMutableArrayRef langs = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
2110 uint8_t localVersion = 0;
2111 CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, &localVersion);
2112 CFStringRef devLang = NULL;
2113 if (infoDict) devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey);
2114 if (devLang && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) devLang = NULL;
2115
2116 _CFBundleAddPreferredLprojNamesInDirectory(alloc, url, localVersion, infoDict, langs, devLang);
2117
2118 if (devLang && CFArrayGetFirstIndexOfValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), devLang) < 0) CFArrayAppendValue(langs, devLang);
2119
2120 // Total backstop behavior to avoid having an empty array.
2121 if (CFArrayGetCount(langs) == 0) CFArrayAppendValue(langs, CFSTR("en"));
2122
2123 if (infoDict && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
2124 if (version) *version = localVersion;
2125 return langs;
2126}
2127
2128CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) {
2129 Boolean result = false;
2130 CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault, url);
2131 if (bundle) {
2132 result = true;
2133 CFRelease(bundle);
2134 }
2135 return result;
2136}
2137
2138// Note that subDirName is expected to be the string for a URL
2139CF_INLINE Boolean _CFBundleURLHasSubDir(CFURLRef url, CFStringRef subDirName) {
2140 Boolean isDir = false, result = false;
2141 CFURLRef dirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, subDirName, url);
2142 if (dirURL) {
2143 if (_CFIsResourceAtURL(dirURL, &isDir) && isDir) result = true;
2144 CFRelease(dirURL);
2145 }
2146 return result;
2147}
2148
2149__private_extern__ Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, uint8_t *version) {
2150 // check for existence of "Resources" or "Contents" or "Support Files"
2151 // but check for the most likely one first
2152 // version 0: old-style "Resources" bundles
2153 // version 1: obsolete "Support Files" bundles
2154 // version 2: modern "Contents" bundles
2155 // version 3: none of the above (see below)
2156 // version 4: not a bundle (for main bundle only)
2157 uint8_t localVersion = 3;
2158 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url);
2159 CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2160 CFArrayRef contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
2161 Boolean hasFrameworkSuffix = CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"));
2162#if DEPLOYMENT_TARGET_WINDOWS
2163 hasFrameworkSuffix = hasFrameworkSuffix || CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework\\"));
2164#endif
2165
2166 if (hasFrameworkSuffix) {
2167 if (_CFBundleSortedArrayContains(contents, _CFBundleResourcesDirectoryName)) localVersion = 0;
2168 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName2)) localVersion = 2;
2169 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName1)) localVersion = 1;
2170 } else {
2171 if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName2)) localVersion = 2;
2172 else if (_CFBundleSortedArrayContains(contents, _CFBundleResourcesDirectoryName)) localVersion = 0;
2173 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName1)) localVersion = 1;
2174 }
2175 if (contents) CFRelease(contents);
2176 if (directoryPath) CFRelease(directoryPath);
2177 if (absoluteURL) CFRelease(absoluteURL);
2178#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
2179 if (localVersion == 3) {
2180 if (hasFrameworkSuffix) {
2181 if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0;
2182 else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2;
2183 else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1;
2184 } else {
2185 if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2;
2186 else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0;
2187 else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1;
2188 }
2189 }
2190#endif
2191 if (version) *version = localVersion;
2192 return (localVersion != 3);
2193}
2194
2195static Boolean _isValidPlatformSuffix(CFStringRef suffix) {
2196 for (CFIndex idx = 0; idx < _CFBundleNumberOfPlatforms; idx++) {
2197 if (CFEqual(suffix, _CFBundleSupportedPlatforms[idx])) return true;
2198 }
2199 return false;
2200}
2201
2202static Boolean _isValidProductSuffix(CFStringRef suffix) {
2203 for (CFIndex idx = 0; idx < _CFBundleNumberOfProducts; idx++) {
2204 if (CFEqual(suffix, _CFBundleSupportedProducts[idx])) return true;
2205 }
2206 return false;
2207}
2208
2209static Boolean _isValidiPhoneOSPlatformProductSuffix(CFStringRef suffix) {
2210 for (CFIndex idx = 0; idx < _CFBundleNumberOfiPhoneOSPlatformProducts; idx++) {
2211 if (CFEqual(suffix, _CFBundleSupportediPhoneOSPlatformProducts[idx])) return true;
2212 }
2213 return false;
2214}
2215
2216static Boolean _isValidPlatformAndProductSuffixPair(CFStringRef platform, CFStringRef product) {
2217 if (!platform && !product) return true;
2218 if (!platform) {
2219 return _isValidProductSuffix(product);
2220 }
2221 if (!product) {
2222 return _isValidPlatformSuffix(platform);
2223 }
2224 if (CFEqual(platform, _CFBundleiPhoneOSPlatformName)) {
2225 return _isValidiPhoneOSPlatformProductSuffix(product);
2226 }
2227 return false;
2228}
2229
2230static Boolean _isBlacklistedKey(CFStringRef keyName) {
2231#if __CONSTANT_STRINGS__
2232#define _CFBundleNumberOfBlacklistedInfoDictionaryKeys 2
2233 static const CFStringRef _CFBundleBlacklistedInfoDictionaryKeys[_CFBundleNumberOfBlacklistedInfoDictionaryKeys] = { CFSTR("CFBundleExecutable"), CFSTR("CFBundleIdentifier") };
2234
2235 for (CFIndex idx = 0; idx < _CFBundleNumberOfBlacklistedInfoDictionaryKeys; idx++) {
2236 if (CFEqual(keyName, _CFBundleBlacklistedInfoDictionaryKeys[idx])) return true;
2237 }
2238#endif
2239 return false;
2240}
2241
2242static Boolean _isOverrideKey(CFStringRef fullKey, CFStringRef *outBaseKey, CFStringRef *outPlatformSuffix, CFStringRef *outProductSuffix) {
2243 if (outBaseKey) {
2244 *outBaseKey = NULL;
2245 }
2246 if (outPlatformSuffix) {
2247 *outPlatformSuffix = NULL;
2248 }
2249 if (outProductSuffix) {
2250 *outProductSuffix = NULL;
2251 }
2252 if (!fullKey)
2253 return false;
2254 CFRange minusRange = CFStringFind(fullKey, CFSTR("-"), kCFCompareBackwards);
2255 CFRange tildeRange = CFStringFind(fullKey, CFSTR("~"), kCFCompareBackwards);
2256 if (minusRange.location == kCFNotFound && tildeRange.location == kCFNotFound) return false;
2257 // minus must come before tilde if both are present
2258 if (minusRange.location != kCFNotFound && tildeRange.location != kCFNotFound && tildeRange.location <= minusRange.location) return false;
2259
2260 CFIndex strLen = CFStringGetLength(fullKey);
2261 CFRange baseKeyRange = (minusRange.location != kCFNotFound) ? CFRangeMake(0, minusRange.location) : CFRangeMake(0, tildeRange.location);
2262 CFRange platformRange = CFRangeMake(kCFNotFound, 0);
2263 CFRange productRange = CFRangeMake(kCFNotFound, 0);
2264 if (minusRange.location != kCFNotFound) {
2265 platformRange.location = minusRange.location + minusRange.length;
2266 platformRange.length = ((tildeRange.location != kCFNotFound) ? tildeRange.location : strLen) - platformRange.location;
2267 }
2268 if (tildeRange.location != kCFNotFound) {
2269 productRange.location = tildeRange.location + tildeRange.length;
2270 productRange.length = strLen - productRange.location;
2271 }
2272 if (baseKeyRange.length < 1) return false;
2273 if (platformRange.location != kCFNotFound && platformRange.length < 1) return false;
2274 if (productRange.location != kCFNotFound && productRange.length < 1) return false;
2275
2276 CFStringRef platform = (platformRange.location != kCFNotFound) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefaultGCRefZero, fullKey, platformRange) : NULL;
2277 CFStringRef product = (productRange.location != kCFNotFound) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefaultGCRefZero, fullKey, productRange) : NULL;
2278 Boolean result = _isValidPlatformAndProductSuffixPair(platform, product);
2279
2280 if (result) {
2281 if (outBaseKey) {
2282 *outBaseKey = CFStringCreateWithSubstring(kCFAllocatorSystemDefaultGCRefZero, fullKey, baseKeyRange);
2283 }
2284 if (outPlatformSuffix) {
2285 *outPlatformSuffix = platform;
2286 } else {
2287 if (platform && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(platform);
2288 }
2289 if (outProductSuffix) {
2290 *outProductSuffix = product;
2291 } else {
2292 if (product && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(product);
2293 }
2294 } else {
2295 if (platform && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(platform);
2296 if (product && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(product);
2297 }
2298 return result;
2299}
2300
2301static Boolean _isCurrentPlatformAndProduct(CFStringRef platform, CFStringRef product) {
2302 if (!platform && !product) return true;
2303 if (!platform) {
2304 return CFEqual(_CFGetProductName(), product);
2305 }
2306 if (!product) {
2307 return CFEqual(_CFGetPlatformName(), platform);
2308 }
2309
2310 return CFEqual(_CFGetProductName(), product) && CFEqual(_CFGetPlatformName(), platform);
2311}
2312
2313static CFArrayRef _CopySortedOverridesForBaseKey(CFStringRef keyName, CFDictionaryRef dict) {
2314 CFMutableArrayRef overrides = CFArrayCreateMutable(kCFAllocatorSystemDefaultGCRefZero, 0, &kCFTypeArrayCallBacks);
2315 CFStringRef keyNameWithBoth = CFStringCreateWithFormat(kCFAllocatorSystemDefaultGCRefZero, NULL, CFSTR("%@-%@~%@"), keyName, _CFGetPlatformName(), _CFGetProductName());
2316 CFStringRef keyNameWithProduct = CFStringCreateWithFormat(kCFAllocatorSystemDefaultGCRefZero, NULL, CFSTR("%@~%@"), keyName, _CFGetProductName());
2317 CFStringRef keyNameWithPlatform = CFStringCreateWithFormat(kCFAllocatorSystemDefaultGCRefZero, NULL, CFSTR("%@-%@"), keyName, _CFGetPlatformName());
2318
2319 CFIndex count = CFDictionaryGetCount(dict);
2320
2321 if (count > 0) {
2322 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), 0);
2323 CFTypeRef *values = &(keys[count]);
2324
2325 CFDictionaryGetKeysAndValues(dict, keys, values);
2326 for (CFIndex idx = 0; idx < count; idx++) {
2327 if (CFEqual(keys[idx], keyNameWithBoth)) {
2328 CFArrayAppendValue(overrides, keys[idx]);
2329 break;
2330 }
2331 }
2332 for (CFIndex idx = 0; idx < count; idx++) {
2333 if (CFEqual(keys[idx], keyNameWithProduct)) {
2334 CFArrayAppendValue(overrides, keys[idx]);
2335 break;
2336 }
2337 }
2338 for (CFIndex idx = 0; idx < count; idx++) {
2339 if (CFEqual(keys[idx], keyNameWithPlatform)) {
2340 CFArrayAppendValue(overrides, keys[idx]);
2341 break;
2342 }
2343 }
2344 for (CFIndex idx = 0; idx < count; idx++) {
2345 if (CFEqual(keys[idx], keyName)) {
2346 CFArrayAppendValue(overrides, keys[idx]);
2347 break;
2348 }
2349 }
2350
2351 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2352 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, keys);
2353 }
2354 }
2355
2356 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2357 CFRelease(keyNameWithProduct);
2358 CFRelease(keyNameWithPlatform);
2359 CFRelease(keyNameWithBoth);
2360 }
2361
2362 return overrides;
2363}
2364
2365__private_extern__ void _processInfoDictionary(CFMutableDictionaryRef dict, CFStringRef platformSuffix, CFStringRef productSuffix) {
2366 CFIndex count = CFDictionaryGetCount(dict);
2367
2368 if (count > 0) {
2369 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), 0);
2370 CFTypeRef *values = &(keys[count]);
2371 CFMutableArrayRef guard = CFArrayCreateMutable(kCFAllocatorSystemDefaultGCRefZero, 0, &kCFTypeArrayCallBacks);
2372
2373 CFDictionaryGetKeysAndValues(dict, keys, values);
2374 for (CFIndex idx = 0; idx < count; idx++) {
2375 CFStringRef keyPlatformSuffix, keyProductSuffix, keyName;
2376 if (_isOverrideKey((CFStringRef)keys[idx], &keyName, &keyPlatformSuffix, &keyProductSuffix)) {
2377 CFArrayRef keysForBaseKey = NULL;
2378 if (_isCurrentPlatformAndProduct(keyPlatformSuffix, keyProductSuffix) && !_isBlacklistedKey(keyName) && CFDictionaryContainsKey(dict, keys[idx])) {
2379 keysForBaseKey = _CopySortedOverridesForBaseKey(keyName, dict);
2380 CFIndex keysForBaseKeyCount = CFArrayGetCount(keysForBaseKey);
2381
2382 //make sure the other keys for this base key don't get released out from under us until we're done
2383 CFArrayAppendValue(guard, keysForBaseKey);
2384
2385 //the winner for this base key will be sorted to the front, do the override with it
2386 CFTypeRef highestPriorityKey = CFArrayGetValueAtIndex(keysForBaseKey, 0);
2387 CFDictionarySetValue(dict, keyName, CFDictionaryGetValue(dict, highestPriorityKey));
2388
2389 //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
2390 for (CFIndex presentKeysIdx = 0; presentKeysIdx < keysForBaseKeyCount; presentKeysIdx++) {
2391 CFStringRef currentKey = (CFStringRef)CFArrayGetValueAtIndex(keysForBaseKey, presentKeysIdx);
2392 if (!CFEqual(currentKey, keyName))
2393 CFDictionaryRemoveValue(dict, currentKey);
2394 }
2395 } else {
2396 CFDictionaryRemoveValue(dict, keys[idx]);
2397 }
2398
2399
2400 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2401 if (keyPlatformSuffix) CFRelease(keyPlatformSuffix);
2402 if (keyProductSuffix) CFRelease(keyProductSuffix);
2403 CFRelease(keyName);
2404 if (keysForBaseKey) CFRelease(keysForBaseKey);
2405 }
2406 }
2407 }
2408
2409 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2410 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, keys);
2411 CFRelease(guard);
2412 }
2413 }
2414}
2415
2416// returns zero-ref dictionary under GC if given kCFAllocatorSystemDefaultGCRefZero
2417__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) {
2418 CFDictionaryRef dict = NULL;
2419 unsigned char buff[CFMaxPathSize];
2420 uint8_t localVersion = 0;
2421
2422 if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) {
2423 CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
2424 if (!newURL) newURL = (CFURLRef)CFRetain(url);
2425
2426 // version 3 is for flattened pseudo-bundles with no Contents, Support Files, or Resources directories
2427 if (!_CFBundleURLLooksLikeBundleVersion(newURL, &localVersion)) localVersion = 3;
2428
2429 dict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc, newURL, localVersion);
2430 CFRelease(newURL);
2431 }
2432 if (version) *version = localVersion;
2433 return dict;
2434}
2435
2436// returns zero-ref dictionary under GC if given kCFAllocatorSystemDefaultGCRefZero
2437__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, uint8_t version) {
2438 CFDictionaryRef result = NULL;
2439 if (url) {
2440 CFURLRef infoURL = NULL, rawInfoURL = NULL;
2441 CFDataRef infoData = NULL;
2442 UniChar buff[CFMaxPathSize];
2443 CFIndex len;
2444 CFMutableStringRef cheapStr;
2445 CFStringRef infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0, infoURLFromBase = _CFBundleInfoURLFromBase0;
2446 Boolean tryPlatformSpecific = true, tryGlobal = true;
2447 CFURLRef directoryURL = NULL, absoluteURL;
2448 CFStringRef directoryPath;
2449 CFArrayRef contents = NULL;
2450 CFRange contentsRange = CFRangeMake(0, 0);
2451
2452 _CFEnsureStaticBuffersInited();
2453
2454 if (0 == version) {
2455 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, url);
2456 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0;
2457 infoURLFromBase = _CFBundleInfoURLFromBase0;
2458 } else if (1 == version) {
2459 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, url);
2460 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension1;
2461 infoURLFromBase = _CFBundleInfoURLFromBase1;
2462 } else if (2 == version) {
2463 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, url);
2464 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension2;
2465 infoURLFromBase = _CFBundleInfoURLFromBase2;
2466 } else if (3 == version) {
2467 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
2468 // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle
2469 if (path) {
2470 if (!(CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(path, _CFBundleResourcesDirectoryName))) {
2471 directoryURL = (CFURLRef)CFRetain(url);
2472 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3;
2473 infoURLFromBase = _CFBundleInfoURLFromBase3;
2474 }
2475 CFRelease(path);
2476 }
2477 }
2478 if (directoryURL) {
2479 absoluteURL = CFURLCopyAbsoluteURL(directoryURL);
2480 directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2481 contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
2482 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
2483 if (directoryPath) CFRelease(directoryPath);
2484 if (absoluteURL) CFRelease(absoluteURL);
2485 if (directoryURL) CFRelease(directoryURL);
2486 }
2487
2488 len = CFStringGetLength(infoURLFromBaseNoExtension);
2489 CFStringGetCharacters(infoURLFromBaseNoExtension, CFRangeMake(0, len), buff);
2490 buff[len++] = (UniChar)'-';
2491 memmove(buff + len, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
2492 len += _PlatformLen;
2493 _CFAppendPathExtension(buff, &len, CFMaxPathSize, _InfoExtensionUniChars, _InfoExtensionLen);
2494 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
2495 CFStringAppendCharacters(cheapStr, buff, len);
2496 infoURL = CFURLCreateWithString(kCFAllocatorSystemDefault, cheapStr, url);
2497 if (contents) {
2498 CFIndex resourcesLen, idx;
2499 for (resourcesLen = len; resourcesLen > 0; resourcesLen--) if (buff[resourcesLen - 1] == PATH_SEP) break;
2500 CFStringDelete(cheapStr, CFRangeMake(0, CFStringGetLength(cheapStr)));
2501 CFStringAppendCharacters(cheapStr, buff + resourcesLen, len - resourcesLen);
2502 for (tryPlatformSpecific = false, idx = 0; !tryPlatformSpecific && idx < contentsRange.length; idx++) {
2503 // Need to do this case-insensitive to accommodate Palm
2504 if (kCFCompareEqualTo == CFStringCompare(cheapStr, (CFStringRef)CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryPlatformSpecific = true;
2505 }
2506 }
2507 if (tryPlatformSpecific) CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoURL, &infoData, NULL, NULL, NULL);
2508 //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryPlatformSpecific ? "missed it\n" : "skipped it\n"));
2509 CFRelease(cheapStr);
2510 if (!infoData) {
2511 // Check for global Info.plist
2512 CFRelease(infoURL);
2513 infoURL = CFURLCreateWithString(kCFAllocatorSystemDefault, infoURLFromBase, url);
2514 if (contents) {
2515 CFIndex idx;
2516 for (tryGlobal = false, idx = 0; !tryGlobal && idx < contentsRange.length; idx++) {
2517 // Need to do this case-insensitive to accommodate Palm
2518 if (kCFCompareEqualTo == CFStringCompare(_CFBundleInfoFileName, (CFStringRef)CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryGlobal = true;
2519 }
2520 }
2521 if (tryGlobal) CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoURL, &infoData, NULL, NULL, NULL);
2522 //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryGlobal ? "missed it\n" : "skipped it\n"));
2523 }
2524
2525 if (infoData) {
2526 result = (CFDictionaryRef)CFPropertyListCreateFromXMLData(alloc, infoData, kCFPropertyListMutableContainers, NULL);
2527 if (result) {
2528 if (CFDictionaryGetTypeID() == CFGetTypeID(result)) {
2529 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, infoURL);
2530 } else {
2531 if (!_CFAllocatorIsGCRefZero(alloc)) CFRelease(result);
2532 result = NULL;
2533 }
2534 }
2535 if (!result) rawInfoURL = infoURL;
2536 CFRelease(infoData);
2537 }
2538 if (!result) {
2539 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2540 if (rawInfoURL) CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleRawInfoPlistURLKey, rawInfoURL);
2541 }
2542
2543 CFRelease(infoURL);
2544 if (contents) CFRelease(contents);
2545 }
2546 _processInfoDictionary((CFMutableDictionaryRef)result, _CFGetPlatformName(), _CFGetProductName());
2547 return result;
2548}
2549
2550static Boolean _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc, CFURLRef url, CFDictionaryRef infoDict, UInt32 *packageType, UInt32 *packageCreator) {
2551 Boolean retVal = false, hasType = false, hasCreator = false, releaseInfoDict = false;
2552 CFURLRef tempURL;
2553 CFDataRef pkgInfoData = NULL;
2554
2555 // Check for a "real" new bundle
2556 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase2, url);
2557 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
2558 CFRelease(tempURL);
2559 if (!pkgInfoData) {
2560 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase1, url);
2561 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
2562 CFRelease(tempURL);
2563 }
2564 if (!pkgInfoData) {
2565 // Check for a "pseudo" new bundle
2566 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePseudoPkgInfoURLFromBase, url);
2567 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
2568 CFRelease(tempURL);
2569 }
2570
2571 // 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.
2572 // 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.
2573 // 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.
2574
2575 if (pkgInfoData && CFDataGetLength(pkgInfoData) >= (int)(sizeof(UInt32) * 2)) {
2576 UInt32 *pkgInfo = (UInt32 *)CFDataGetBytePtr(pkgInfoData);
2577 if (packageType) *packageType = CFSwapInt32BigToHost(pkgInfo[0]);
2578 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(pkgInfo[1]);
2579 retVal = hasType = hasCreator = true;
2580 }
2581 if (pkgInfoData) CFRelease(pkgInfoData);
2582 if (!retVal) {
2583 if (!infoDict) {
2584 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, NULL);
2585 releaseInfoDict = true;
2586 }
2587 if (infoDict) {
2588 CFStringRef typeString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleSignatureKey);
2589 UInt32 tmp;
2590 CFIndex usedBufLen = 0;
2591 if (typeString && CFGetTypeID(typeString) == CFStringGetTypeID() && CFStringGetLength(typeString) == 4 && 4 == CFStringGetBytes(typeString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) {
2592 if (packageType) *packageType = CFSwapInt32BigToHost(tmp);
2593 retVal = hasType = true;
2594 }
2595 if (creatorString && CFGetTypeID(creatorString) == CFStringGetTypeID() && CFStringGetLength(creatorString) == 4 && 4 == CFStringGetBytes(creatorString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) {
2596 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(tmp);
2597 retVal = hasCreator = true;
2598 }
2599 if (releaseInfoDict && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
2600 }
2601 }
2602 if (!hasType || !hasCreator) {
2603 // If this looks like a bundle then manufacture the type and creator.
2604 if (retVal || _CFBundleURLLooksLikeBundle(url)) {
2605 if (packageCreator && !hasCreator) *packageCreator = 0x3f3f3f3f; // '????'
2606 if (packageType && !hasType) {
2607 CFStringRef urlStr;
2608 UniChar buff[CFMaxPathSize];
2609 CFIndex strLen, startOfExtension;
2610 CFURLRef absoluteURL;
2611
2612 // Detect "app", "debug", "profile", or "framework" extensions
2613 absoluteURL = CFURLCopyAbsoluteURL(url);
2614 urlStr = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2615 CFRelease(absoluteURL);
2616 strLen = CFStringGetLength(urlStr);
2617 if (strLen > CFMaxPathSize) strLen = CFMaxPathSize;
2618 CFStringGetCharacters(urlStr, CFRangeMake(0, strLen), buff);
2619 CFRelease(urlStr);
2620 startOfExtension = _CFStartOfPathExtension(buff, strLen);
2621 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)) {
2622 // This is an app
2623 *packageType = 0x4150504c; // 'APPL'
2624 } 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)) {
2625 // This is an app (debug version)
2626 *packageType = 0x4150504c; // 'APPL'
2627 } 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)) {
2628 // This is an app (profile version)
2629 *packageType = 0x4150504c; // 'APPL'
2630 } 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)) {
2631 // This is a service
2632 *packageType = 0x4150504c; // 'APPL'
2633 } 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)) {
2634 // This is a framework
2635 *packageType = 0x464d574b; // 'FMWK'
2636 } else {
2637 // Default to BNDL for generic bundle
2638 *packageType = 0x424e444c; // 'BNDL'
2639 }
2640 }
2641 retVal = true;
2642 }
2643 }
2644 return retVal;
2645}
2646
2647CF_EXPORT Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
2648 return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc, url, NULL, packageType, packageCreator);
2649}
2650
2651CF_EXPORT void CFBundleGetPackageInfo(CFBundleRef bundle, UInt32 *packageType, UInt32 *packageCreator) {
2652 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle);
2653 if (!_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(kCFAllocatorSystemDefault, bundleURL, CFBundleGetInfoDictionary(bundle), packageType, packageCreator)) {
2654 if (packageType) *packageType = 0x424e444c; // 'BNDL'
2655 if (packageCreator) *packageCreator = 0x3f3f3f3f; // '????'
2656 }
2657 if (bundleURL) CFRelease(bundleURL);
2658}
2659
2660CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
2661 return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault, url, packageType, packageCreator);
2662}
2663
2664static void _CFBundleCheckSupportedPlatform(CFMutableArrayRef mutableArray, UniChar *buff, CFIndex startLen, CFStringRef platformName, CFStringRef platformIdentifier) {
2665 CFIndex buffLen = startLen, platformLen = CFStringGetLength(platformName), extLen = CFStringGetLength(_CFBundleInfoExtension);
2666 CFMutableStringRef str;
2667 Boolean isDir;
2668 if (buffLen + platformLen + extLen < CFMaxPathSize) {
2669 CFStringGetCharacters(platformName, CFRangeMake(0, platformLen), buff + buffLen);
2670 buffLen += platformLen;
2671 buff[buffLen++] = (UniChar)'.';
2672 CFStringGetCharacters(_CFBundleInfoExtension, CFRangeMake(0, extLen), buff + buffLen);
2673 buffLen += extLen;
2674 str = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
2675 CFStringAppendCharacters(str, buff, buffLen);
2676 if (_CFIsResourceAtPath(str, &isDir) && !isDir && CFArrayGetFirstIndexOfValue(mutableArray, CFRangeMake(0, CFArrayGetCount(mutableArray)), platformIdentifier) < 0) CFArrayAppendValue(mutableArray, platformIdentifier);
2677 CFRelease(str);
2678 }
2679}
2680
2681CF_EXPORT CFArrayRef _CFBundleGetSupportedPlatforms(CFBundleRef bundle) {
2682 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
2683 CFArrayRef platformArray = infoDict ? (CFArrayRef)CFDictionaryGetValue(infoDict, _kCFBundleSupportedPlatformsKey) : NULL;
2684 if (platformArray && CFGetTypeID(platformArray) != CFArrayGetTypeID()) {
2685 platformArray = NULL;
2686 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleSupportedPlatformsKey);
2687 }
2688 if (!platformArray) {
2689 CFURLRef infoPlistURL = infoDict ? (CFURLRef)CFDictionaryGetValue(infoDict, _kCFBundleInfoPlistURLKey) : NULL, absoluteURL;
2690 CFStringRef infoPlistPath;
2691 UniChar buff[CFMaxPathSize];
2692 CFIndex buffLen, infoLen = CFStringGetLength(_CFBundleInfoURLFromBaseNoExtension3), startLen, extLen = CFStringGetLength(_CFBundleInfoExtension);
2693 if (infoPlistURL) {
2694 CFMutableArrayRef mutableArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
2695 absoluteURL = CFURLCopyAbsoluteURL(infoPlistURL);
2696 infoPlistPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2697 CFRelease(absoluteURL);
2698 buffLen = CFStringGetLength(infoPlistPath);
2699 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize;
2700 CFStringGetCharacters(infoPlistPath, CFRangeMake(0, buffLen), buff);
2701 CFRelease(infoPlistPath);
2702 if (buffLen > 0) {
2703 buffLen = _CFStartOfLastPathComponent(buff, buffLen);
2704 if (buffLen > 0 && buffLen + infoLen + extLen < CFMaxPathSize) {
2705 CFStringGetCharacters(_CFBundleInfoURLFromBaseNoExtension3, CFRangeMake(0, infoLen), buff + buffLen);
2706 buffLen += infoLen;
2707 buff[buffLen++] = (UniChar)'-';
2708 startLen = buffLen;
2709 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("macos"), CFSTR("MacOS"));
2710 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("macosx"), CFSTR("MacOS"));
2711 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("iphoneos"), CFSTR("iPhoneOS"));
2712 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("windows"), CFSTR("Windows"));
2713 }
2714 }
2715 if (CFArrayGetCount(mutableArray) > 0) {
2716 platformArray = (CFArrayRef)mutableArray;
2717 CFDictionarySetValue((CFMutableDictionaryRef)infoDict, _kCFBundleSupportedPlatformsKey, platformArray);
2718 }
2719 CFRelease(mutableArray);
2720 }
2721 }
2722 return platformArray;
2723}
2724
2725CF_EXPORT CFStringRef _CFBundleGetCurrentPlatform(void) {
2726#if DEPLOYMENT_TARGET_MACOSX
2727 return CFSTR("MacOS");
2728#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
2729 return CFSTR("iPhoneOS");
2730#elif DEPLOYMENT_TARGET_WINDOWS
2731 return CFSTR("Windows");
2732#elif DEPLOYMENT_TARGET_SOLARIS
2733 return CFSTR("Solaris");
2734#elif DEPLOYMENT_TARGET_HPUX
2735 return CFSTR("HPUX");
2736#elif DEPLOYMENT_TARGET_LINUX
2737 return CFSTR("Linux");
2738#elif DEPLOYMENT_TARGET_FREEBSD
2739 return CFSTR("FreeBSD");
2740#else
2741#error Unknown or unspecified DEPLOYMENT_TARGET
2742#endif
2743}
2744
2745__private_extern__ CFStringRef _CFBundleGetPlatformExecutablesSubdirectoryName(void) {
2746#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
2747 return CFSTR("MacOS");
2748#elif DEPLOYMENT_TARGET_WINDOWS
2749 return CFSTR("Windows");
2750#elif DEPLOYMENT_TARGET_SOLARIS
2751 return CFSTR("Solaris");
2752#elif DEPLOYMENT_TARGET_HPUX
2753 return CFSTR("HPUX");
2754#elif DEPLOYMENT_TARGET_LINUX
2755 return CFSTR("Linux");
2756#elif DEPLOYMENT_TARGET_FREEBSD
2757 return CFSTR("FreeBSD");
2758#else
2759#error Unknown or unspecified DEPLOYMENT_TARGET
2760#endif
2761}
2762
2763__private_extern__ CFStringRef _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(void) {
2764#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
2765 return CFSTR("Mac OS X");
2766#elif DEPLOYMENT_TARGET_WINDOWS
2767 return CFSTR("WinNT");
2768#elif DEPLOYMENT_TARGET_SOLARIS
2769 return CFSTR("Solaris");
2770#elif DEPLOYMENT_TARGET_HPUX
2771 return CFSTR("HP-UX");
2772#elif DEPLOYMENT_TARGET_LINUX
2773 return CFSTR("Linux");
2774#elif DEPLOYMENT_TARGET_FREEBSD
2775 return CFSTR("FreeBSD");
2776#else
2777#error Unknown or unspecified DEPLOYMENT_TARGET
2778#endif
2779}
2780
2781__private_extern__ CFStringRef _CFBundleGetOtherPlatformExecutablesSubdirectoryName(void) {
2782#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
2783 return CFSTR("MacOSClassic");
2784#elif DEPLOYMENT_TARGET_WINDOWS
2785 return CFSTR("Other");
2786#elif DEPLOYMENT_TARGET_HPUX
2787 return CFSTR("Other");
2788#elif DEPLOYMENT_TARGET_SOLARIS
2789 return CFSTR("Other");
2790#elif DEPLOYMENT_TARGET_LINUX
2791 return CFSTR("Other");
2792#elif DEPLOYMENT_TARGET_FREEBSD
2793 return CFSTR("Other");
2794#else
2795#error Unknown or unspecified DEPLOYMENT_TARGET
2796#endif
2797}
2798
2799__private_extern__ CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(void) {
2800#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
2801 return CFSTR("Mac OS 8");
2802#elif DEPLOYMENT_TARGET_WINDOWS
2803 return CFSTR("Other");
2804#elif DEPLOYMENT_TARGET_HPUX
2805 return CFSTR("Other");
2806#elif DEPLOYMENT_TARGET_SOLARIS
2807 return CFSTR("Other");
2808#elif DEPLOYMENT_TARGET_LINUX
2809 return CFSTR("Other");
2810#elif DEPLOYMENT_TARGET_FREEBSD
2811 return CFSTR("Other");
2812#else
2813#error Unknown or unspecified DEPLOYMENT_TARGET
2814#endif
2815}
2816
2817__private_extern__ CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle) {
2818 return CFBundleCopyBundleLocalizations(bundle);
2819}
2820
2821CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) {
2822 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
2823 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
2824 CFURLRef absoluteURL;
2825 CFStringRef directoryPath;
2826 CFArrayRef contents;
2827 CFRange contentsRange;
2828 CFIndex idx;
2829 CFArrayRef predefinedLocalizations = NULL;
2830 CFMutableArrayRef result = NULL;
2831
2832 if (infoDict) {
2833 predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
2834 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) {
2835 predefinedLocalizations = NULL;
2836 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey);
2837 }
2838 if (predefinedLocalizations) {
2839 CFIndex i, c = CFArrayGetCount(predefinedLocalizations);
2840 if (c > 0 && !result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2841 for (i = 0; i < c; i++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(predefinedLocalizations, i));
2842 }
2843 }
2844
2845 if (resourcesURL) {
2846 absoluteURL = CFURLCopyAbsoluteURL(resourcesURL);
2847 directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2848 contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
2849 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
2850 for (idx = 0; idx < contentsRange.length; idx++) {
2851 CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(contents, idx);
2852 if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) {
2853 CFStringRef localization = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, name, CFRangeMake(0, CFStringGetLength(name) - 6));
2854 if (!result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2855 CFArrayAppendValue(result, localization);
2856 CFRelease(localization);
2857 }
2858 }
2859 if (contents) CFRelease(contents);
2860 if (directoryPath) CFRelease(directoryPath);
2861 if (absoluteURL) CFRelease(absoluteURL);
2862 }
2863
2864 if (!result) {
2865 CFStringRef developmentLocalization = CFBundleGetDevelopmentRegion(bundle);
2866 if (developmentLocalization) {
2867 result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2868 CFArrayAppendValue(result, developmentLocalization);
2869 }
2870 }
2871 if (resourcesURL) CFRelease(resourcesURL);
2872 return result;
2873}
2874
2875
2876CF_EXPORT CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) {
2877 CFDictionaryRef result = NULL;
2878 Boolean isDir = false;
2879 if (_CFIsResourceAtURL(url, &isDir)) {
2880 if (isDir) {
2881 result = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, NULL);
2882 } else {
2883 result = _CFBundleCopyInfoDictionaryInExecutable(url); // return zero-ref dictionary under GC
2884 }
2885 }
2886 if (result && _CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRetain(result); // conditionally put on a retain for a Copy function
2887 return result;
2888}
2889
2890CFArrayRef CFBundleCopyExecutableArchitecturesForURL(CFURLRef url) {
2891 CFArrayRef result = NULL;
2892 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
2893 if (bundle) {
2894 result = CFBundleCopyExecutableArchitectures(bundle);
2895 CFRelease(bundle);
2896 } else {
2897 result = _CFBundleCopyArchitecturesForExecutable(url);
2898 }
2899 return result;
2900}
2901
2902CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) {
2903 CFArrayRef result = NULL;
2904 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
2905 CFStringRef devLang = NULL;
2906 if (bundle) {
2907 result = CFBundleCopyBundleLocalizations(bundle);
2908 CFRelease(bundle);
2909 } else {
2910 CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInExecutable(url); // return zero-ref dictionary under GC
2911 if (infoDict) {
2912 CFArrayRef predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
2913 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) == CFArrayGetTypeID()) result = (CFArrayRef)CFRetain(predefinedLocalizations);
2914 if (!result) {
2915 devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey);
2916 if (devLang && (CFGetTypeID(devLang) == CFStringGetTypeID() && CFStringGetLength(devLang) > 0)) result = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&devLang, 1, &kCFTypeArrayCallBacks);
2917 }
2918 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
2919 }
2920 }
2921 return result;
2922}
2923
2924
2925
2926CF_INLINE Boolean _CFBundleFindCharacterInStr(const UniChar *str, UniChar c, Boolean backward, CFIndex start, CFIndex length, CFRange *result){
2927 *result = CFRangeMake(kCFNotFound, 0);
2928 Boolean found = false;
2929 if (backward) {
2930 for (CFIndex i = start; i > start-length; i--) {
2931 if (c == str[i]) {
2932 result->location = i;
2933 found = true;
2934 break;
2935 }
2936 }
2937 } else {
2938 for (CFIndex i = start; i < start+length; i++) {
2939 if (c == str[i]) {
2940 result->location = i;
2941 found = true;
2942 break;
2943 }
2944 }
2945 }
2946 return found;
2947}
2948
2949
2950typedef enum {
2951 _CFBundleFileVersionNoProductNoPlatform = 1,
2952 _CFBundleFileVersionWithProductNoPlatform,
2953 _CFBundleFileVersionNoProductWithPlatform,
2954 _CFBundleFileVersionWithProductWithPlatform,
2955 _CFBundleFileVersionUnmatched
2956} _CFBundleFileVersion;
2957
2958static _CFBundleFileVersion _CFBundleCheckFileProductAndPlatform(CFStringRef file, UniChar *fileBuffer1, CFIndex fileLen, CFRange searchRange, CFStringRef product, CFStringRef platform, CFRange* prodp, CFRange* platp, CFIndex prodLen)
2959{
2960 _CFBundleFileVersion version;
2961 CFRange found;
2962 Boolean foundprod, foundplat;
2963 foundplat = foundprod = NO;
2964 UniChar fileBuffer2[CFMaxPathSize];
2965 UniChar *fileBuffer;
2966 Boolean wrong = false;
2967
2968 if (fileBuffer1) {
2969 fileBuffer = fileBuffer1;
2970 }else{
2971 fileLen = CFStringGetLength(file);
2972 if (fileLen > CFMaxPathSize) fileLen = CFMaxPathSize;
2973 CFStringGetCharacters(file, CFRangeMake(0, fileLen), fileBuffer2);
2974 fileBuffer = fileBuffer2;
2975 }
2976
2977 if (_CFBundleFindCharacterInStr(fileBuffer, '~', NO, searchRange.location, searchRange.length, &found)) {
2978 if (prodLen != 1) {
2979 if (CFStringFindWithOptions(file, product, searchRange, kCFCompareEqualTo, prodp)) {
2980 foundprod = YES;
2981 }
2982 }
2983 if (!foundprod) {
2984 for (CFIndex i = 0; i < _CFBundleNumberOfProducts; i++) {
2985 if (CFStringFindWithOptions(file, _CFBundleSupportedProducts[i], searchRange, kCFCompareEqualTo, &found)) {
2986 wrong = true;
2987 break;
2988 }
2989 }
2990 }
2991 }
2992
2993 if (!wrong && _CFBundleFindCharacterInStr(fileBuffer, '-', NO, searchRange.location, searchRange.length, &found)) {
2994 if (CFStringFindWithOptions(file, platform, searchRange, kCFCompareEqualTo, platp)) {
2995 foundplat = YES;
2996 }
2997 if (!foundplat) {
2998 for (CFIndex i = 0; i < _CFBundleNumberOfPlatforms; i++) {
2999 if (CFStringFindWithOptions(file, _CFBundleSupportedPlatforms[i], searchRange, kCFCompareEqualTo, &found)) {
3000 wrong = true;
3001 break;
3002 }
3003 }
3004 }
3005 }
3006
3007 if (wrong) {
3008 version = _CFBundleFileVersionUnmatched;
3009 } else if (foundplat && foundprod) {
3010 version = _CFBundleFileVersionWithProductWithPlatform;
3011 } else if (foundplat) {
3012 version = _CFBundleFileVersionNoProductWithPlatform;
3013 } else if (foundprod) {
3014 version = _CFBundleFileVersionWithProductNoPlatform;
3015 } else {
3016 version = _CFBundleFileVersionNoProductNoPlatform;
3017 }
3018 return version;
3019}
3020
3021
3022// ZFH
3023
3024
3025static void _CFBundleAddValueForType(CFMutableStringRef type, UniChar* fileNameBuffer, CFMutableDictionaryRef queryTable, CFRange dotPosition, CFIndex fileLen, CFMutableDictionaryRef typeDir, CFTypeRef value, CFMutableDictionaryRef addedTypes, Boolean firstLproj){
3026 CFIndex typeLen = fileLen - dotPosition.location - 1;
3027 CFStringSetExternalCharactersNoCopy(type, fileNameBuffer+dotPosition.location+1, typeLen, typeLen);
3028 CFMutableArrayRef tFiles = (CFMutableArrayRef) CFDictionaryGetValue(typeDir, type);
3029 if (!tFiles) {
3030 CFStringRef key = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@.%@"), _CFBundleTypeIndicator, type);
3031 tFiles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
3032 CFDictionarySetValue(queryTable, key, tFiles);
3033 CFDictionarySetValue(typeDir, type, tFiles);
3034 CFRelease(tFiles);
3035 CFRelease(key);
3036 }
3037 if (!addedTypes) {
3038 CFArrayAppendValue(tFiles, value);
3039 } else if (firstLproj) {
3040 CFDictionarySetValue(addedTypes, type, type);
3041 CFArrayAppendValue(tFiles, value);
3042 } else if (!(CFDictionaryGetValue(addedTypes, type))) {
3043 CFArrayAppendValue(tFiles, value);
3044 }
3045}
3046
3047static Boolean _CFBundleReadDirectory(CFStringRef pathOfDir, CFBundleRef bundle, CFURLRef bundleURL, UniChar *resDir, UniChar *subDir, CFIndex subDirLen, CFMutableArrayRef allFiles, Boolean hasFileAdded, CFMutableStringRef type, CFMutableDictionaryRef queryTable, CFMutableDictionaryRef typeDir, CFMutableDictionaryRef addedTypes, Boolean firstLproj, CFStringRef product, CFStringRef platform, CFStringRef lprojName, Boolean appendLprojCharacters) {
3048
3049 Boolean result = true;
3050
3051 const CFIndex cPathBuffLen = CFStringGetMaximumSizeOfFileSystemRepresentation(pathOfDir) + 1;
3052 const CFIndex valueBufferLen = cPathBuffLen + 1 + CFMaxPathSize;
3053 UniChar *valueBuff = (UniChar *) CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * valueBufferLen, 0);
3054 CFMutableStringRef valueStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
3055 CFIndex pathOfDirWithSlashLen = 0;
3056
3057 CFIndex productLen = CFStringGetLength(product);
3058 CFIndex platformLen = CFStringGetLength(platform);
3059
3060 if (lprojName) {
3061 // valueBuff is allocated with the actual length of lprojTarget
3062 CFRange lprojRange = CFRangeMake(0, CFStringGetLength(lprojName));
3063 CFStringGetCharacters(lprojName, lprojRange, valueBuff);
3064 pathOfDirWithSlashLen += lprojRange.length;
3065 if (appendLprojCharacters) _CFAppendPathExtension(valueBuff, &pathOfDirWithSlashLen, valueBufferLen, _LprojUniChars, _LprojLen);
3066 _CFAppendTrailingPathSlash(valueBuff, &pathOfDirWithSlashLen, valueBufferLen);
3067 }
3068
3069 if (subDirLen) {
3070 memmove(valueBuff+pathOfDirWithSlashLen, subDir, subDirLen*sizeof(UniChar));
3071 pathOfDirWithSlashLen += subDirLen;
3072 if (subDir[subDirLen-1] != _CFGetSlash()) {
3073 _CFAppendTrailingPathSlash(valueBuff, &pathOfDirWithSlashLen, valueBufferLen);
3074 }
3075 }
3076
3077 UniChar *fileNameBuffer = valueBuff + pathOfDirWithSlashLen;
3078 char *cPathBuff = (char *)malloc(sizeof(char) * cPathBuffLen);
3079
3080 if (CFStringGetFileSystemRepresentation(pathOfDir, cPathBuff, cPathBuffLen)) {
3081// this is a fix for traversing ouside of a bundle security issue: 8302591
3082// it will be enabled after the bug 10956699 gets fixed
3083#ifdef CFBUNDLE_NO_TRAVERSE_OUTSIDE
3084#endif // CFBUNDLE_NO_TRAVERSE_OUTSIDE
3085
3086#if DEPLOYMENT_TARGET_WINDOWS
3087 wchar_t pathBuf[CFMaxPathSize];
3088 CFStringRef pathInUTF8 = CFStringCreateWithCString(kCFAllocatorSystemDefault, cPathBuff, kCFStringEncodingUTF8);
3089 CFIndex pathInUTF8Len = CFStringGetLength(pathInUTF8);
3090 if (pathInUTF8Len > CFMaxPathSize) pathInUTF8Len = CFMaxPathSize;
3091
3092 CFStringGetCharacters(pathInUTF8, CFRangeMake(0, pathInUTF8Len), (UniChar *)pathBuf);
3093 pathBuf[pathInUTF8Len] = 0;
3094 CFRelease(pathInUTF8);
3095 WIN32_FIND_DATAW filePt;
3096 HANDLE handle;
3097
3098 if (pathInUTF8Len + 2 >= CFMaxPathLength) {
3099 result = false;
3100 }
3101
3102 pathBuf[pathInUTF8Len] = '\\';
3103 pathBuf[pathInUTF8Len + 1] = '*';
3104 pathBuf[pathInUTF8Len + 2] = '\0';
3105 handle = FindFirstFileW(pathBuf, (LPWIN32_FIND_DATAW)&filePt);
3106 if (INVALID_HANDLE_VALUE == handle) {
3107 pathBuf[pathInUTF8Len] = '\0';
3108 result = false;
3109 }
3110 if (!result) {
3111 free(cPathBuff);
3112 CFAllocatorDeallocate(kCFAllocatorSystemDefault, valueBuff);
3113 CFRelease(valueStr);
3114 return result;
3115 }
3116
3117 do {
3118 CFIndex nameLen = wcslen(filePt.cFileName);
3119 if (filePt.cFileName[0] == '.' && (nameLen == 1 || (nameLen == 2 && filePt.cFileName[1] == '.'))) {
3120 continue;
3121 }
3122 CFStringRef file = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (const uint8_t *)filePt.cFileName, nameLen * sizeof(wchar_t), kCFStringEncodingUTF16, NO);
3123#elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
3124 DIR *dirp = NULL;
3125 struct dirent* dent;
3126 if ( result && (dirp = opendir(cPathBuff))) {
3127
3128 while ((dent = readdir(dirp))) {
3129
3130#if DEPLOYMENT_TARGET_LINUX
3131 CFIndex nameLen = strlen(dent->d_name);
3132#else
3133 CFIndex nameLen = dent->d_namlen;
3134#endif
3135 if (0 == nameLen || 0 == dent->d_fileno || ('.' == dent->d_name[0] && (1 == nameLen || (2 == nameLen && '.' == dent->d_name[1]) || '_' == dent->d_name[1])))
3136 continue;
3137
3138 CFStringRef file = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, dent->d_name);
3139#else
3140#error unknown architecture, not implemented
3141#endif
3142 if (file) {
3143
3144 CFIndex fileNameLen = CFStringGetLength(file);
3145 if (fileNameLen > CFMaxPathSize) fileNameLen = CFMaxPathSize;
3146 CFStringGetCharacters(file, CFRangeMake(0, fileNameLen), fileNameBuffer);
3147 CFIndex valueTotalLen = pathOfDirWithSlashLen + fileNameLen;
3148
3149#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
3150 // construct the path for a file, which is the value in the query table
3151 // if it is a dir
3152 if (dent->d_type == DT_DIR) {
3153 _CFAppendTrailingPathSlash(valueBuff, &valueTotalLen, valueBufferLen);
3154 } else if (dent->d_type == DT_UNKNOWN) {
3155 Boolean isDir = false;
3156 char subdirPath[CFMaxPathLength];
3157 struct stat statBuf;
3158 strlcpy(subdirPath, cPathBuff, sizeof(subdirPath));
3159 strlcat(subdirPath, "/", sizeof(subdirPath));
3160 strlcat(subdirPath, dent->d_name, sizeof(subdirPath));
3161 if (stat(subdirPath, &statBuf) == 0) {
3162 isDir = ((statBuf.st_mode & S_IFMT) == S_IFDIR);
3163 }
3164 if (isDir) {
3165 _CFAppendTrailingPathSlash(valueBuff, &valueTotalLen, valueBufferLen);
3166 }
3167 }
3168#elif DEPLOYMENT_TARGET_WINDOWS
3169 if ((filePt.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
3170 _CFAppendTrailingPathSlash(valueBuff, &valueTotalLen, valueBufferLen);
3171 }
3172#endif
3173 CFStringSetExternalCharactersNoCopy(valueStr, valueBuff, valueTotalLen, valueBufferLen);
3174 CFTypeRef value = CFStringCreateCopy(kCFAllocatorSystemDefault, valueStr);
3175
3176 // put it into all file array
3177 if (!hasFileAdded) {
3178 CFArrayAppendValue(allFiles, value);
3179 }
3180
3181 // put it into type array
3182 // search the type from the end
3183 CFRange backDotPosition, dotPosition;
3184 Boolean foundDot = _CFBundleFindCharacterInStr(fileNameBuffer, '.', YES, fileNameLen-1, fileNameLen, &backDotPosition);
3185
3186 if (foundDot && backDotPosition.location != (fileNameLen-1)) {
3187 _CFBundleAddValueForType(type, fileNameBuffer, queryTable, backDotPosition, fileNameLen, typeDir, value, addedTypes, firstLproj);
3188 }
3189
3190 // search the type from the beginning
3191 //CFRange dotPosition = CFStringFind(file, _CFBundleDot, kCFCompareEqualTo);
3192 foundDot = _CFBundleFindCharacterInStr(fileNameBuffer, '.', NO, 0, fileNameLen, &dotPosition);
3193 if (dotPosition.location != backDotPosition.location && foundDot) {
3194 _CFBundleAddValueForType(type, fileNameBuffer, queryTable, dotPosition, fileNameLen, typeDir, value, addedTypes, firstLproj);
3195 }
3196
3197 // check if the file is product and platform specific
3198 CFRange productRange, platformRange;
3199 _CFBundleFileVersion fileVersion = _CFBundleCheckFileProductAndPlatform(file, fileNameBuffer, fileNameLen, CFRangeMake(0, fileNameLen), product, platform, &productRange, &platformRange, productLen);
3200
3201 if (fileVersion == _CFBundleFileVersionNoProductNoPlatform || fileVersion == _CFBundleFileVersionUnmatched) {
3202 // No product/no platform, or unmatched files get added directly to the query table.
3203 CFStringRef prevPath = (CFStringRef)CFDictionaryGetValue(queryTable, file);
3204 if (!prevPath) {
3205 CFDictionarySetValue(queryTable, file, value);
3206 }
3207 } else {
3208 // If the file has a product or platform extension, we add the full name to the query table so that it may be found using that name.
3209 // Then we add the more specific name as well.
3210 CFDictionarySetValue(queryTable, file, value);
3211
3212 CFIndex searchOffset = platformLen;
3213 CFStringRef key = NULL;
3214
3215 // set the key accordining to the version of the file (product and platform)
3216 switch (fileVersion) {
3217 case _CFBundleFileVersionWithProductNoPlatform:
3218 platformRange = productRange;
3219 searchOffset = productLen;
3220 case _CFBundleFileVersionNoProductWithPlatform:
3221 case _CFBundleFileVersionWithProductWithPlatform:
3222 foundDot = _CFBundleFindCharacterInStr(fileNameBuffer, '.', NO, platformRange.location+searchOffset, fileNameLen-platformRange.location-searchOffset, &dotPosition);
3223 if (foundDot) {
3224 CFMutableStringRef mutableKey = CFStringCreateMutable(kCFAllocatorSystemDefault, platformRange.location + (fileNameLen - dotPosition.location));
3225 CFStringAppendCharacters(mutableKey, fileNameBuffer, platformRange.location);
3226 CFStringAppendCharacters(mutableKey, fileNameBuffer+dotPosition.location, fileNameLen - dotPosition.location);
3227 key = (CFStringRef)mutableKey;
3228 } else {
3229 key = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, fileNameBuffer, platformRange.location);
3230 }
3231 break;
3232 default:
3233 CFLog(kCFLogLevelError, CFSTR("CFBundle: Unknown kind of file (%d) when creating CFBundle: %@"), pathOfDir);
3234 break;
3235 }
3236
3237 if (key) {
3238 // add the path of the key into the query table
3239 CFStringRef prevPath = (CFStringRef) CFDictionaryGetValue(queryTable, key);
3240 if (!prevPath) {
3241 CFDictionarySetValue(queryTable, key, value);
3242 } else {
3243 if (!lprojName || CFStringHasPrefix(prevPath, lprojName)) {
3244 // we need to know the version of exisiting path to see if we can replace it by the current path
3245 CFRange searchRange;
3246 if (lprojName) {
3247 searchRange.location = CFStringGetLength(lprojName);
3248 searchRange.length = CFStringGetLength(prevPath) - searchRange.location;
3249 } else {
3250 searchRange.location = 0;
3251 searchRange.length = CFStringGetLength(prevPath);
3252 }
3253 _CFBundleFileVersion prevFileVersion = _CFBundleCheckFileProductAndPlatform(prevPath, NULL, 0, searchRange, product, platform, &productRange, &platformRange, productLen);
3254 switch (prevFileVersion) {
3255 case _CFBundleFileVersionNoProductNoPlatform:
3256 CFDictionarySetValue(queryTable, key, value);
3257 break;
3258 case _CFBundleFileVersionWithProductNoPlatform:
3259 if (fileVersion == _CFBundleFileVersionWithProductWithPlatform) CFDictionarySetValue(queryTable, key, value);
3260 break;
3261 case _CFBundleFileVersionNoProductWithPlatform:
3262 CFDictionarySetValue(queryTable, key, value);
3263 break;
3264 default:
3265 break;
3266 }
3267 }
3268 }
3269
3270 CFRelease(key);
3271 }
3272 }
3273
3274 CFRelease(value);
3275 CFRelease(file);
3276 }
3277
3278
3279#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
3280 }
3281 closedir(dirp);
3282 } else { // opendir
3283 result = false;
3284 }
3285#elif DEPLOYMENT_TARGET_WINDOWS
3286 } while ((FindNextFileW(handle, &filePt)));
3287 FindClose(handle);
3288 pathBuf[pathInUTF8Len] = '\0';
3289#endif
3290
3291 } else { // the path counld not be resolved to be a file system representation
3292 result = false;
3293 }
3294
3295 free(cPathBuff);
3296 CFAllocatorDeallocate(kCFAllocatorSystemDefault, valueBuff);
3297 CFRelease(valueStr);
3298 return result;
3299}
3300
3301__private_extern__ CFDictionaryRef _CFBundleCreateQueryTableAtPath(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef languages, UniChar *resDir, CFIndex resDirLen, UniChar *subDir, CFIndex subDirLen)
3302{
3303 const CFIndex pathBufferSize = 2*CFMaxPathSize+resDirLen+subDirLen+2;
3304 UniChar *pathBuffer = (UniChar *) CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * pathBufferSize, 0);
3305
3306 CFMutableDictionaryRef queryTable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3307 CFMutableArrayRef allFiles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
3308 CFMutableDictionaryRef typeDir = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3309 CFMutableStringRef type = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
3310
3311 CFStringRef productName = _CFGetProductName();//CFSTR("iphone");
3312 CFStringRef platformName = _CFGetPlatformName();//CFSTR("iphoneos");
3313 if (CFEqual(productName, CFSTR("ipod"))) {
3314 productName = CFSTR("iphone");
3315 }
3316 CFStringRef product = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("~%@"), productName);
3317 CFStringRef platform = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("-%@"), platformName);
3318
3319 CFStringRef bundlePath = NULL;
3320 if (bundle) {
3321 bundlePath = _CFBundleGetBundlePath(bundle);
3322 CFRetain(bundlePath);
3323 } else {
3324 CFURLRef url = CFURLCopyAbsoluteURL(bundleURL);
3325 bundlePath = CFURLCopyFileSystemPath(url, PLATFORM_PATH_STYLE);
3326 CFRelease(url);
3327 }
3328 // bundlePath is an actual path, so it should not have a length greater than CFMaxPathSize
3329 CFIndex pathLen = CFStringGetLength(bundlePath);
3330 CFStringGetCharacters(bundlePath, CFRangeMake(0, pathLen), pathBuffer);
3331 CFRelease(bundlePath);
3332
3333 Boolean appendSucc = true;
3334 if (resDirLen > 0) { // should not fail, buffer has enought space
3335 appendSucc = _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, resDir, resDirLen);
3336 }
3337
3338 CFStringRef basePath = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathBuffer, pathLen);
3339
3340 if (subDirLen > 0) { // should not fail, buffer has enought space
3341 appendSucc = _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, subDir, subDirLen);
3342 }
3343
3344 CFStringRef pathToRead = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathBuffer, pathLen);
3345
3346 // read the content in sub dir and put them into query table
3347 _CFBundleReadDirectory(pathToRead, bundle, bundleURL, resDir, subDir, subDirLen, allFiles, false, type, queryTable, typeDir, NULL, false, product, platform, NULL, false);
3348
3349 CFRelease(pathToRead);
3350
3351 CFIndex numOfAllFiles = CFArrayGetCount(allFiles);
3352
3353 if (bundle && !languages) {
3354 languages = _CFBundleGetLanguageSearchList(bundle);
3355 }
3356 CFIndex numLprojs = languages ? CFArrayGetCount(languages) : 0;
3357 CFMutableDictionaryRef addedTypes = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
3358
3359 CFIndex basePathLen = CFStringGetLength(basePath);
3360 Boolean hasFileAdded = false;
3361 Boolean firstLproj = true;
3362
3363 // First, search lproj for user's chosen language
3364 if (numLprojs >= 1) {
3365 CFStringRef lprojTarget = (CFStringRef)CFArrayGetValueAtIndex(languages, 0);
3366 // lprojTarget is from _CFBundleGetLanguageSearchList, so it should not have a length greater than CFMaxPathSize
3367 UniChar lprojBuffer[CFMaxPathSize];
3368 CFIndex lprojLen = CFStringGetLength(lprojTarget);
3369 CFStringGetCharacters(lprojTarget, CFRangeMake(0, lprojLen), lprojBuffer);
3370
3371 pathLen = basePathLen;
3372 _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, lprojBuffer, lprojLen);
3373 _CFAppendPathExtension(pathBuffer, &pathLen, pathBufferSize, _LprojUniChars, _LprojLen);
3374 if (subDirLen > 0) {
3375 _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, subDir, subDirLen);
3376 }
3377 pathToRead = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathBuffer, pathLen);
3378 _CFBundleReadDirectory(pathToRead, bundle, bundleURL, resDir, subDir, subDirLen, allFiles, hasFileAdded, type, queryTable, typeDir, addedTypes, firstLproj, product, platform, lprojTarget, true);
3379 CFRelease(pathToRead);
3380
3381 if (!hasFileAdded && numOfAllFiles < CFArrayGetCount(allFiles)) {
3382 hasFileAdded = true;
3383 }
3384 firstLproj = false;
3385 }
3386
3387 // Next, search Base.lproj folder
3388 pathLen = basePathLen;
3389 _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, _BaseUniChars, _BaseLen);
3390 _CFAppendPathExtension(pathBuffer, &pathLen, pathBufferSize, _LprojUniChars, _LprojLen);
3391 if (subDirLen > 0) {
3392 _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, subDir, subDirLen);
3393 }
3394 pathToRead = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathBuffer, pathLen);
3395 _CFBundleReadDirectory(pathToRead, bundle, bundleURL, resDir, subDir, subDirLen, allFiles, hasFileAdded, type, queryTable, typeDir, addedTypes, YES, product, platform, _CFBundleBaseDirectory, true);
3396 CFRelease(pathToRead);
3397
3398 if (!hasFileAdded && numOfAllFiles < CFArrayGetCount(allFiles)) {
3399 hasFileAdded = true;
3400 }
3401
3402 // Finally, search remaining languages (development language first)
3403 if (numLprojs >= 2) {
3404 // for each lproj we are interested in, read the content and put them into query table
3405 for (CFIndex i = 1; i < CFArrayGetCount(languages); i++) {
3406 CFStringRef lprojTarget = (CFStringRef) CFArrayGetValueAtIndex(languages, i);
3407 // lprojTarget is from _CFBundleGetLanguageSearchList, so it should not have a length greater than CFMaxPathSize
3408 UniChar lprojBuffer[CFMaxPathSize];
3409 CFIndex lprojLen = CFStringGetLength(lprojTarget);
3410 CFStringGetCharacters(lprojTarget, CFRangeMake(0, lprojLen), lprojBuffer);
3411
3412 pathLen = basePathLen;
3413 _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, lprojBuffer, lprojLen);
3414 _CFAppendPathExtension(pathBuffer, &pathLen, pathBufferSize, _LprojUniChars, _LprojLen);
3415 if (subDirLen > 0) {
3416 _CFAppendPathComponent(pathBuffer, &pathLen, pathBufferSize, subDir, subDirLen);
3417 }
3418 pathToRead = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathBuffer, pathLen);
3419 _CFBundleReadDirectory(pathToRead, bundle, bundleURL, resDir, subDir, subDirLen, allFiles, hasFileAdded, type, queryTable, typeDir, addedTypes, false, product, platform, lprojTarget, true);
3420 CFRelease(pathToRead);
3421
3422 if (!hasFileAdded && numOfAllFiles < CFArrayGetCount(allFiles)) {
3423 hasFileAdded = true;
3424 }
3425 }
3426 }
3427
3428 CFRelease(addedTypes);
3429
3430 // put the array of all files in sub dir to the query table
3431 if (CFArrayGetCount(allFiles) > 0) {
3432 CFDictionarySetValue(queryTable, _CFBundleAllFiles, allFiles);
3433 }
3434
3435 CFRelease(platform);
3436 CFRelease(product);
3437 CFRelease(allFiles);
3438 CFRelease(typeDir);
3439 CFRelease(type);
3440 CFRelease(basePath);
3441
3442 CFAllocatorDeallocate(kCFAllocatorSystemDefault, pathBuffer);
3443 return queryTable;
3444}
3445
3446static CFURLRef _CFBundleCreateURLFromPath(CFStringRef path, UniChar slash, UniChar *urlBuffer, CFIndex urlBufferLen, CFMutableStringRef urlStr)
3447{
3448 CFURLRef url = NULL;
3449 // path is a part of an actual path in the query table, so it should not have a length greater than the buffer size
3450 CFIndex pathLen = CFStringGetLength(path);
3451 CFStringGetCharacters(path, CFRangeMake(0, pathLen), urlBuffer+urlBufferLen);
3452 CFStringSetExternalCharactersNoCopy(urlStr, urlBuffer, urlBufferLen+pathLen, CFMaxPathSize);
3453 if (CFStringGetCharacterAtIndex(path, pathLen-1) == slash) {
3454 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, urlStr, PLATFORM_PATH_STYLE, YES);
3455 } else {
3456 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, urlStr, PLATFORM_PATH_STYLE, NO);
3457 }
3458
3459 return url;
3460}
3461
3462static CFURLRef _CFBundleCreateRelativeURLFromBaseAndPath(CFStringRef path, CFURLRef base, UniChar slash, CFStringRef slashStr)
3463{
3464 CFURLRef url = NULL;
3465 CFRange resultRange;
3466 Boolean needToRelease = false;
3467 if (CFStringFindWithOptions(path, slashStr, CFRangeMake(0, CFStringGetLength(path)-1), kCFCompareBackwards, &resultRange)) {
3468 CFStringRef subPathCom = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, path, CFRangeMake(0, resultRange.location));
3469 base = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, base, subPathCom, YES);
3470 path = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, path, CFRangeMake(resultRange.location+1, CFStringGetLength(path)-resultRange.location-1));
3471 CFRelease(subPathCom);
3472 needToRelease = true;
3473 }
3474 if (CFStringGetCharacterAtIndex(path, CFStringGetLength(path)-1) == slash) {
3475 url = (CFURLRef)CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, path, PLATFORM_PATH_STYLE, YES, base);
3476 } else {
3477 url = (CFURLRef)CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, path, PLATFORM_PATH_STYLE, NO, base);
3478 }
3479 if (needToRelease) {
3480 CFRelease(base);
3481 CFRelease(path);
3482 }
3483 return url;
3484}
3485
3486static void _CFBundleFindResourcesWithPredicate(CFMutableArrayRef interResult, CFDictionaryRef queryTable, Boolean (^predicate)(CFStringRef filename, Boolean *stop), Boolean *stop)
3487{
3488 CFIndex dictSize = CFDictionaryGetCount(queryTable);
3489 if (dictSize == 0) {
3490 return;
3491 }
3492 STACK_BUFFER_DECL(CFTypeRef, keys, dictSize);
3493 STACK_BUFFER_DECL(CFTypeRef, values, dictSize);
3494 CFDictionaryGetKeysAndValues(queryTable, keys, values);
3495 for (CFIndex i = 0; i < dictSize; i++) {
3496 if (predicate((CFStringRef)keys[i], stop)) {
3497 if (CFGetTypeID(values[i]) == CFStringGetTypeID()) {
3498 CFArrayAppendValue(interResult, values[i]);
3499 } else {
3500 CFArrayAppendArray(interResult, (CFArrayRef)values[i], CFRangeMake(0, CFArrayGetCount((CFArrayRef)values[i])));
3501 }
3502 }
3503
3504 if (*stop) break;
3505 }
3506}
3507
3508static CFTypeRef _CFBundleCopyURLsOfKey(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef languages, UniChar *resDir, CFIndex resDirLen, UniChar *subDirBuffer, CFIndex subDirLen, CFStringRef subDir, CFStringRef key, CFStringRef lproj, UniChar *lprojBuff, Boolean returnArray, Boolean localized, uint8_t bundleVersion, Boolean (^predicate)(CFStringRef filename, Boolean *stop))
3509{
3510 CFTypeRef value = NULL;
3511 Boolean stop = false; // for predicate
3512 CFMutableArrayRef interResult = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
3513 CFDictionaryRef subTable = NULL;
3514
3515 if (1 == bundleVersion) {
3516 CFIndex savedResDirLen = resDirLen;
3517 // add the non-localized resource dir
3518 Boolean appendSucc = _CFAppendPathComponent(resDir, &resDirLen, CFMaxPathSize, _GlobalResourcesUniChars, _GlobalResourcesLen);
3519 if (appendSucc) {
3520 subTable = _CFBundleCopyQueryTable(bundle, bundleURL, languages, resDir, resDirLen, subDirBuffer, subDirLen);
3521 if (predicate) {
3522 _CFBundleFindResourcesWithPredicate(interResult, subTable, predicate, &stop);
3523 } else {
3524 value = CFDictionaryGetValue(subTable, key);
3525 }
3526 }
3527 resDirLen = savedResDirLen;
3528 }
3529
3530 if (!value && !stop) {
3531 if (subTable) CFRelease(subTable);
3532 subTable = _CFBundleCopyQueryTable(bundle, bundleURL, languages, resDir, resDirLen, subDirBuffer, subDirLen);
3533 if (predicate) {
3534 _CFBundleFindResourcesWithPredicate(interResult, subTable, predicate, &stop);
3535 } else {
3536 // get the path or paths for the given key
3537 value = CFDictionaryGetValue(subTable, key);
3538 }
3539 }
3540
3541 // if localization is needed, we filter out the paths for the localization and put the valid ones in the interResult
3542 Boolean checkLP = true;
3543 CFIndex lpLen = lproj ? CFStringGetLength(lproj) : 0;
3544 if (localized && value) {
3545
3546 if (CFGetTypeID(value) == CFStringGetTypeID()){
3547 // We had one result, but since we are going to do a search in a different localization, we will convert the one result into an array of results.
3548 value = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&value, 1, &kCFTypeArrayCallBacks);
3549 } else {
3550 CFRetain(value);
3551 }
3552
3553 CFRange resultRange, searchRange;
3554 CFIndex pathValueLen;
3555 CFIndex limit = returnArray ? CFArrayGetCount((CFArrayRef)value) : 1;
3556 searchRange.location = 0;
3557 for (CFIndex i = 0; i < limit; i++) {
3558 CFStringRef pathValue = (CFStringRef) CFArrayGetValueAtIndex((CFArrayRef)value, i);
3559 pathValueLen = CFStringGetLength(pathValue);
3560 searchRange.length = pathValueLen;
3561
3562 // if we have subdir, we find the subdir and see if it is after the base path (bundle path + res dir)
3563 Boolean searchForLocalization = false;
3564 if (subDirLen) {
3565 if (CFStringFindWithOptions(pathValue, subDir, searchRange, kCFCompareEqualTo, &resultRange) && resultRange.location != searchRange.location) {
3566 searchForLocalization = true;
3567 }
3568 } else if (!subDirLen && searchRange.length != 0) {
3569 if (CFStringFindWithOptions(pathValue, _CFBundleLprojExtensionWithDot, searchRange, kCFCompareEqualTo, &resultRange) && resultRange.location + 7 < pathValueLen) {
3570 searchForLocalization = true;
3571 }
3572 }
3573
3574 if (searchForLocalization) {
3575 if (!lpLen || !(CFStringFindWithOptions(pathValue, lproj, searchRange, kCFCompareEqualTo | kCFCompareAnchored, &resultRange) && CFStringFindWithOptions(pathValue, CFSTR("."), CFRangeMake(resultRange.location + resultRange.length, 1), kCFCompareEqualTo, &resultRange))) {
3576 break;
3577 }
3578 checkLP = false;
3579 }
3580
3581 CFArrayAppendValue(interResult, pathValue);
3582 }
3583
3584 CFRelease(value);
3585
3586 if (!returnArray && CFArrayGetCount(interResult) != 0) {
3587 checkLP = false;
3588 }
3589 } else if (value) {
3590 if (CFGetTypeID(value) == CFArrayGetTypeID()) {
3591 CFArrayAppendArray(interResult, (CFArrayRef)value, CFRangeMake(0, CFArrayGetCount((CFArrayRef)value)));
3592 } else {
3593 CFArrayAppendValue(interResult, value);
3594 }
3595 }
3596
3597 value = NULL;
3598 CFRelease(subTable);
3599
3600 // we fetch the result for a given lproj and join them with the nonlocalized result fetched above
3601 if (lpLen && checkLP) {
3602 CFIndex lprojBuffLen = lpLen;
3603 // lprojBuff is allocated with the actual size of lproj
3604 CFStringGetCharacters(lproj, CFRangeMake(0, lpLen), lprojBuff);
3605 _CFAppendPathExtension(lprojBuff, &lprojBuffLen, lprojBuffLen+7, _LprojUniChars, _LprojLen);
3606
3607 if (subDirLen) {
3608 _CFAppendTrailingPathSlash(lprojBuff, &lprojBuffLen, lprojBuffLen+1);
3609 }
3610 subTable = _CFBundleCopyQueryTable(bundle, bundleURL, languages, resDir, resDirLen, lprojBuff, subDirLen+lprojBuffLen);
3611
3612 value = CFDictionaryGetValue(subTable, key);
3613
3614 if (value) {
3615 if (CFGetTypeID(value) == CFStringGetTypeID()) {
3616 CFArrayAppendValue(interResult, value);
3617 } else {
3618 CFArrayAppendArray(interResult, (CFArrayRef)value, CFRangeMake(0, CFArrayGetCount((CFArrayRef)value)));
3619 }
3620 }
3621
3622 CFRelease(subTable);
3623 }
3624
3625 // after getting paths, we create urls from the paths
3626 CFTypeRef result = NULL;
3627 if (CFArrayGetCount(interResult) > 0) {
3628 UniChar slash = _CFGetSlash();
3629 UniChar *urlBuffer = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * CFMaxPathSize, 0);
3630 CFMutableStringRef urlStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
3631 CFStringRef bundlePath = NULL;
3632 if (bundle) {
3633 bundlePath = _CFBundleGetBundlePath(bundle);
3634 CFRetain(bundlePath);
3635 } else {
3636 CFURLRef url = CFURLCopyAbsoluteURL(bundleURL);
3637 bundlePath = CFURLCopyFileSystemPath(url, PLATFORM_PATH_STYLE);
3638 CFRelease(url);
3639 }
3640 CFIndex urlBufferLen = CFStringGetLength(bundlePath);
3641 CFStringGetCharacters(bundlePath, CFRangeMake(0, urlBufferLen), urlBuffer);
3642 CFRelease(bundlePath);
3643
3644 if (resDirLen) {
3645 _CFAppendPathComponent(urlBuffer, &urlBufferLen, CFMaxPathSize, resDir, resDirLen);
3646 }
3647 _CFAppendTrailingPathSlash(urlBuffer, &urlBufferLen, CFMaxPathSize);
3648
3649 if (!returnArray) {
3650 Boolean isOnlyTypeOrAllFiles = CFStringHasPrefix(key, _CFBundleTypeIndicator);
3651 isOnlyTypeOrAllFiles |= CFStringHasPrefix(key, _CFBundleAllFiles);
3652
3653 CFStringRef resultPath = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)interResult, 0);
3654 if (!isOnlyTypeOrAllFiles) {
3655 result = (CFURLRef)_CFBundleCreateURLFromPath((CFStringRef)resultPath, slash, urlBuffer, urlBufferLen, urlStr);
3656 } else { // need to create relative URLs for binary compatibility issues
3657 CFStringSetExternalCharactersNoCopy(urlStr, urlBuffer, urlBufferLen, CFMaxPathSize);
3658 CFURLRef base = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, urlStr, PLATFORM_PATH_STYLE, YES);
3659 CFStringRef slashStr = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &slash, 1);
3660 result = (CFURLRef)_CFBundleCreateRelativeURLFromBaseAndPath(resultPath, base, slash, slashStr);
3661 CFRelease(slashStr);
3662 CFRelease(base);
3663 }
3664 } else {
3665 // need to create relative URLs for binary compatibility issues
3666 CFIndex numOfPaths = CFArrayGetCount((CFArrayRef)interResult);
3667 CFStringSetExternalCharactersNoCopy(urlStr, urlBuffer, urlBufferLen, CFMaxPathSize);
3668 CFURLRef base = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, urlStr, PLATFORM_PATH_STYLE, YES);
3669 CFStringRef slashStr = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &slash, 1);
3670 CFMutableArrayRef urls = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
3671 for (CFIndex i = 0; i < numOfPaths; i++) {
3672 CFStringRef path = (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)interResult, i);
3673 CFURLRef url = _CFBundleCreateRelativeURLFromBaseAndPath(path, base, slash, slashStr);
3674 CFArrayAppendValue(urls, url);
3675 CFRelease(url);
3676 }
3677 result = urls;
3678 CFRelease(base);
3679 }
3680 CFRelease(urlStr);
3681 CFAllocatorDeallocate(kCFAllocatorSystemDefault, urlBuffer);
3682 } else if (returnArray) {
3683 result = CFRetain(interResult);
3684 }
3685
3686 CFRelease(interResult);
3687 return result;
3688}
3689
3690CFTypeRef _CFBundleCopyFindResources(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef languages, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subPath, CFStringRef lproj, Boolean returnArray, Boolean localized, Boolean (^predicate)(CFStringRef filename, Boolean *stop))
3691{
3692 CFIndex rnameLen = 0;
3693 CFIndex typeLen = resourceType ? CFStringGetLength(resourceType) : 0;
3694 CFIndex lprojLen = lproj ? CFStringGetLength(lproj) + 7 : 0; // 7 is the length of ".lproj/"
3695 CFStringRef subPathFromResourceName = NULL;
3696 CFIndex subPathFromResourceNameLen = 0;
3697 if (resourceName) {
3698 UniChar tmpNameBuffer[CFMaxPathSize];
3699 rnameLen = CFStringGetLength(resourceName);
3700 if (rnameLen > CFMaxPathSize) rnameLen = CFMaxPathSize;
3701 CFStringGetCharacters(resourceName, CFRangeMake(0, rnameLen), tmpNameBuffer);
3702 CFIndex rnameIndex = _CFStartOfLastPathComponent(tmpNameBuffer, rnameLen);
3703 if (rnameIndex != 0) {
3704 resourceName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, tmpNameBuffer+rnameIndex, rnameLen - rnameIndex);
3705 subPathFromResourceName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, tmpNameBuffer, rnameIndex-1);
3706 subPathFromResourceNameLen = rnameIndex-1;
3707 } else {
3708 CFRetain(resourceName);
3709 }
3710
3711 char buff[CFMaxPathSize];
3712 CFStringRef newResName = NULL;
3713 if (CFStringGetFileSystemRepresentation(resourceName, buff, CFMaxPathSize)) {
3714 newResName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff);
3715 }
3716 CFStringRef tmpStr = resourceName;
3717 resourceName = newResName ? newResName : (CFStringRef)CFRetain(resourceName);
3718 rnameLen = CFStringGetLength(resourceName);
3719 CFRelease(tmpStr);
3720 }
3721 CFIndex subDirLen = subPath ? CFStringGetLength(subPath) : 0;
3722 if (subDirLen == 0) subPath = NULL;
3723 if (subDirLen && subPathFromResourceName) {
3724 subDirLen += (subPathFromResourceNameLen + 1);
3725 } else if (subPathFromResourceNameLen) {
3726 subDirLen = subPathFromResourceNameLen;
3727 }
3728
3729 // the nameBuff have the format: [resourceName].[resourceType][lprojName.lproj/][subPath][resource dir]
3730 UniChar *nameBuff = (UniChar *) CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (rnameLen + 1 + typeLen + subDirLen + lprojLen + CFMaxPathSize), 0);
3731 UniChar *typeBuff = rnameLen ? nameBuff + rnameLen + 1 : nameBuff + CFStringGetLength(_CFBundleTypeIndicator) + 1;
3732 UniChar *lprojBuffer = typeBuff + typeLen;
3733 UniChar *subDirBuffer = lprojBuffer + lprojLen;
3734 UniChar *resDir = subDirBuffer + subDirLen;
3735
3736 CFStringRef key = NULL;
3737
3738 CFIndex typeP = 0;
3739 if (typeLen && CFStringGetCharacterAtIndex(resourceType, 0) == '.') {
3740 typeP = 1;
3741 typeLen--;
3742 }
3743 if (rnameLen && typeLen) {
3744 CFStringGetCharacters(resourceName, CFRangeMake(0, rnameLen), nameBuff);
3745 nameBuff[rnameLen] = '.';
3746 CFStringGetCharacters(resourceType, CFRangeMake(typeP, typeLen), typeBuff);
3747 key = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, nameBuff, rnameLen+typeLen+1);
3748 } else if (rnameLen) {
3749 CFStringGetCharacters(resourceName, CFRangeMake(0, rnameLen), nameBuff);
3750 key = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, nameBuff, rnameLen);
3751 } else if (typeLen) {
3752 rnameLen = CFStringGetLength(_CFBundleTypeIndicator);
3753 CFStringGetCharacters(_CFBundleTypeIndicator, CFRangeMake(0, rnameLen), nameBuff);
3754 nameBuff[rnameLen] = '.';
3755 CFStringGetCharacters(resourceType, CFRangeMake(typeP, typeLen), typeBuff);
3756 key = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, nameBuff, rnameLen+typeLen+1);
3757 } else {
3758 key = (CFStringRef)CFRetain(_CFBundleAllFiles);
3759 }
3760
3761 if (subDirLen) {
3762 CFIndex subPathLen = 0;
3763 if (subPath) {
3764 subPathLen = CFStringGetLength(subPath);
3765 CFStringGetCharacters(subPath, CFRangeMake(0, subPathLen), subDirBuffer);
3766 if (subPathFromResourceName) _CFAppendTrailingPathSlash(subDirBuffer, &subPathLen, subDirLen);
3767 }
3768 if (subPathFromResourceName) {
3769 CFStringGetCharacters(subPathFromResourceName, CFRangeMake(0, subPathFromResourceNameLen), subDirBuffer+subPathLen);
3770 subPathLen += subPathFromResourceNameLen;
3771 subPath = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, subDirBuffer, subPathLen);
3772 } else {
3773 CFRetain(subPath);
3774 }
3775 }
3776
3777 // Init the one-time-only unichar buffers.
3778 _CFEnsureStaticBuffersInited();
3779
3780 CFIndex resDirLen = 0;
3781 uint8_t bundleVersion = bundle ? _CFBundleLayoutVersion(bundle) : 0;
3782 if (bundleURL && !languages) {
3783 languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, bundleURL, &bundleVersion);
3784 } else if (languages) {
3785 CFRetain(languages);
3786 }
3787
3788 _CFBundleSetResourceDir(resDir, &resDirLen, CFMaxPathSize, bundleVersion);
3789
3790 CFTypeRef returnValue = _CFBundleCopyURLsOfKey(bundle, bundleURL, languages, resDir, resDirLen, subDirBuffer, subDirLen, subPath, key, lproj, lprojBuffer, returnArray, localized, bundleVersion, predicate);
3791
3792 if ((!returnValue || (CFGetTypeID(returnValue) == CFArrayGetTypeID() && CFArrayGetCount((CFArrayRef)returnValue) == 0)) && (0 == bundleVersion || 2 == bundleVersion)) {
3793 CFStringRef bundlePath = NULL;
3794 if (bundle) {
3795 bundlePath = _CFBundleGetBundlePath(bundle);
3796 CFRetain(bundlePath);
3797 } else {
3798 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(bundleURL);
3799 bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
3800 CFRelease(absoluteURL);
3801 }
3802 if ((0 == bundleVersion) || CFEqual(CFSTR("/Library/Spotlight"), bundlePath)){
3803 if (returnValue) CFRelease(returnValue);
3804 CFRange found;
3805 // 9 is the length of "Resources"
3806 if ((bundleVersion == 0 && subPath && CFEqual(subPath, CFSTR("Resources"))) || (bundleVersion == 2 && subPath && CFEqual(subPath, CFSTR("Contents/Resources")))){
3807 subDirLen = 0;
3808 } else if ((bundleVersion == 0 && subPath && CFStringFindWithOptions(subPath, CFSTR("Resources/"), CFRangeMake(0, 10), kCFCompareEqualTo, &found) && found.location+10 < subDirLen)) {
3809 subDirBuffer = subDirBuffer + 10;
3810 subDirLen -= 10;
3811 } else if ((bundleVersion == 2 && subPath && CFStringFindWithOptions(subPath, CFSTR("Contents/Resources/"), CFRangeMake(0, 19), kCFCompareEqualTo, &found) && found.location+19 < subDirLen)) {
3812 subDirBuffer = subDirBuffer + 19;
3813 subDirLen -= 19;
3814 } else {
3815 resDirLen = 0;
3816 }
3817 if (subDirLen > 0) {
3818 CFRelease(subPath);
3819 subPath = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, subDirBuffer, subDirLen);
3820 }
3821 returnValue = _CFBundleCopyURLsOfKey(bundle, bundleURL, languages, resDir, resDirLen, subDirBuffer, subDirLen, subPath, key, lproj, lprojBuffer, returnArray, localized, bundleVersion, predicate);
3822 }
3823 CFRelease(bundlePath);
3824 }
3825
3826 if (resourceName) CFRelease(resourceName);
3827 if (subPath) CFRelease(subPath);
3828 if (subPathFromResourceName) CFRelease(subPathFromResourceName);
3829 if (languages) CFRelease(languages);
3830 CFAllocatorDeallocate(kCFAllocatorSystemDefault, nameBuff);
3831 CFRelease(key);
3832 return returnValue;
3833}
3834
3835__private_extern__ CFTypeRef _CFBundleCopyFindResourcesWithNoBlock(CFBundleRef bundle, CFURLRef bundleURL, CFArrayRef languages, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subPath, CFStringRef lproj, Boolean returnArray, Boolean localized)
3836{
3837 return _CFBundleCopyFindResources(bundle, bundleURL, languages, resourceName, resourceType, subPath, lproj, returnArray, localized, NULL);
3838}