]> git.saurik.com Git - apple/cf.git/blame - CFBundle_Resources.c
CF-744.12.tar.gz
[apple/cf.git] / CFBundle_Resources.c
CommitLineData
9ce05555 1/*
259c77ee 2 * Copyright (c) 2012 Apple Inc. All rights reserved.
9ce05555
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
9ce05555
A
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 */
f64f9b69 23
cf7d2af9 24/* CFBundle_Resources.c
856091c5
A
25 Copyright (c) 1999-2012, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
9ce05555
A
27*/
28
bd5b749c 29#define READ_DIRECTORIES_CACHE_CAPACITY 128
9ce05555
A
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>
cf7d2af9
A
36#include <CoreFoundation/CFLocale.h>
37#include <CoreFoundation/CFPreferences.h>
9ce05555
A
38#include <string.h>
39#include "CFInternal.h"
cf7d2af9 40#include <CoreFoundation/CFPriv.h>
9ce05555
A
41#include <sys/stat.h>
42#include <fcntl.h>
9ce05555 43#include <stdio.h>
bd5b749c 44#include <ctype.h>
8ca704e1
A
45#include <errno.h>
46#include <sys/types.h>
856091c5
A
47
48#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
49#include <unistd.h>
8ca704e1 50#include <sys/sysctl.h>
856091c5
A
51#include <sys/stat.h>
52#include <dirent.h>
8ca704e1 53#endif
bd5b749c
A
54
55#if DEPLOYMENT_TARGET_MACOSX
856091c5 56
9ce05555 57#endif
9ce05555 58
856091c5
A
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
bd5b749c 76
8ca704e1 77CF_EXPORT bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey);
bd5b749c 78
856091c5
A
79extern void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len);
80
9ce05555 81
8ca704e1
A
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
856091c5
A
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
8ca704e1
A
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
8ca704e1
A
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
856091c5 103__private_extern__ void _CFBundleResourcesInitialize() {
8ca704e1
A
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
856091c5
A
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
8ca704e1
A
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
856091c5
A
144CF_EXPORT CFStringRef _CFGetProductName(void) {
145#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
8ca704e1
A
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
9ce05555 180// All new-style bundles will have these extensions.
856091c5 181CF_EXPORT CFStringRef _CFGetPlatformName(void) {
8ca704e1 182#if DEPLOYMENT_TARGET_MACOSX
9ce05555 183 return _CFBundleMacOSXPlatformName;
856091c5 184#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
8ca704e1 185 return _CFBundleiPhoneOSPlatformName;
cf7d2af9
A
186#elif DEPLOYMENT_TARGET_WINDOWS
187 return _CFBundleWindowsPlatformName;
bd5b749c 188#elif DEPLOYMENT_TARGET_SOLARIS
9ce05555 189 return _CFBundleSolarisPlatformName;
bd5b749c 190#elif DEPLOYMENT_TARGET_HPUX
9ce05555 191 return _CFBundleHPUXPlatformName;
bd5b749c 192#elif DEPLOYMENT_TARGET_LINUX
9ce05555 193 return _CFBundleLinuxPlatformName;
bd5b749c 194#elif DEPLOYMENT_TARGET_FREEBSD
9ce05555
A
195 return _CFBundleFreeBSDPlatformName;
196#else
bd5b749c 197#error Unknown or unspecified DEPLOYMENT_TARGET
9ce05555
A
198#endif
199}
200
856091c5 201CF_EXPORT CFStringRef _CFGetAlternatePlatformName(void) {
8ca704e1 202#if DEPLOYMENT_TARGET_MACOSX
9ce05555 203 return _CFBundleAlternateMacOSXPlatformName;
856091c5 204#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
8ca704e1 205 return _CFBundleMacOSXPlatformName;
cf7d2af9
A
206#elif DEPLOYMENT_TARGET_WINDOWS
207 return CFSTR("");
208#else
209#error Unknown or unspecified DEPLOYMENT_TARGET
9ce05555
A
210#endif
211}
212
9ce05555
A
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;
856091c5
A
225static UniChar *_BaseUniChars = NULL;
226static CFIndex _BaseLen;
9ce05555
A
227static UniChar *_GlobalResourcesUniChars = NULL;
228static CFIndex _GlobalResourcesLen = 0;
229static UniChar *_InfoExtensionUniChars = NULL;
230static CFIndex _InfoExtensionLen = 0;
231
856091c5 232#if 0
8ca704e1
A
233static UniChar _ResourceSuffix3[32];
234static CFIndex _ResourceSuffix3Len = 0;
856091c5 235#endif
8ca704e1
A
236static UniChar _ResourceSuffix2[16];
237static CFIndex _ResourceSuffix2Len = 0;
238static UniChar _ResourceSuffix1[16];
239static CFIndex _ResourceSuffix1Len = 0;
240
9ce05555
A
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;
856091c5 250 CFStringRef baseStr = _CFBundleBaseDirectory;
9ce05555 251
9ce05555
A
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);
856091c5 260 _BaseLen = CFStringGetLength(baseStr);
9ce05555 261
856091c5 262 _AppSupportUniChars1 = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (_AppSupportLen1 + _AppSupportLen2 + _ResourcesLen + _PlatformLen + _AlternatePlatformLen + _LprojLen + _GlobalResourcesLen + _InfoExtensionLen + _BaseLen), 0);
9ce05555
A
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;
856091c5
A
270 _BaseUniChars = _InfoExtensionUniChars + _InfoExtensionLen;
271
bd5b749c
A
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);
856091c5 280 if (_BaseLen > 0) CFStringGetCharacters(baseStr, CFRangeMake(0, _BaseLen), _BaseUniChars);
8ca704e1
A
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++;
856091c5 294#if 0
8ca704e1
A
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 }
856091c5 300#endif
9ce05555
A
301}
302
303CF_INLINE void _CFEnsureStaticBuffersInited(void) {
856091c5
A
304 static dispatch_once_t once = 0;
305 dispatch_once(&once, ^{
306 _CFBundleInitStaticUniCharBuffers();
307 });
9ce05555
A
308}
309
856091c5
A
310static CFSpinLock_t _cacheLock = CFSpinLockInit;
311static CFMutableDictionaryRef _contentsCache = NULL;
312static CFMutableDictionaryRef _directoryContentsCache = NULL;
313static CFMutableDictionaryRef _unknownContentsCache = NULL;
d8925383
A
314
315typedef enum {
316 _CFBundleAllContents = 0,
317 _CFBundleDirectoryContents = 1,
318 _CFBundleUnknownContents = 2
319} _CFBundleDirectoryContentsType;
9ce05555 320
8ca704e1
A
321extern void _CFArraySortValues(CFMutableArrayRef array, CFComparatorFunction comparator, void *context);
322
323static CFArrayRef _CFBundleCopySortedDirectoryContentsAtPath(CFStringRef path, _CFBundleDirectoryContentsType contentsType) {
9ce05555
A
324 CFArrayRef result = NULL;
325
9f29f3f8
A
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
856091c5 332 __CFSpinLock(&_cacheLock);
d8925383 333 if (contentsType == _CFBundleUnknownContents) {
856091c5 334 if (_unknownContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(_unknownContentsCache, path);
d8925383 335 } else if (contentsType == _CFBundleDirectoryContents) {
856091c5 336 if (_directoryContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(_directoryContentsCache, path);
9ce05555 337 } else {
856091c5 338 if (_contentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(_contentsCache, path);
9ce05555
A
339 }
340 if (result) CFRetain(result);
856091c5 341 __CFSpinUnlock(&_cacheLock);
9ce05555
A
342
343 if (!result) {
bd5b749c
A
344 Boolean tryToOpen = false, allDots = true;
345 char cpathBuff[CFMaxPathSize];
d8925383 346 CFIndex cpathLen = 0, idx, lastSlashIdx = 0;
bd5b749c 347 CFMutableArrayRef contents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), unknownContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
d8925383 348 CFStringRef dirName, name;
9ce05555 349
bd5b749c
A
350 cpathBuff[0] = '\0';
351 if (CFStringGetFileSystemRepresentation(path, cpathBuff, CFMaxPathSize)) {
352 tryToOpen = true;
9ce05555 353 cpathLen = strlen(cpathBuff);
d8925383
A
354
355 // First see whether we already know that the directory doesn't exist
356 for (idx = cpathLen; lastSlashIdx == 0 && idx-- > 0;) {
856091c5 357 if (cpathBuff[idx] == PATH_SEP) lastSlashIdx = idx;
d8925383
A
358 else if (cpathBuff[idx] != '.') allDots = false;
359 }
360 if (lastSlashIdx > 0 && lastSlashIdx + 1 < cpathLen && !allDots) {
361 cpathBuff[lastSlashIdx] = '\0';
bd5b749c 362 dirName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff);
d8925383 363 if (dirName) {
bd5b749c 364 name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff + lastSlashIdx + 1);
d8925383 365 if (name) {
856091c5 366 // ??? we might like to use _directoryContentsCache rather than _contentsCache here, but we cannot unless we resolve DT_LNKs below
d8925383
A
367 CFArrayRef dirDirContents = NULL;
368
856091c5
A
369 __CFSpinLock(&_cacheLock);
370 if (_contentsCache) dirDirContents = (CFArrayRef)CFDictionaryGetValue(_contentsCache, dirName);
d8925383
A
371 if (dirDirContents) {
372 Boolean foundIt = false;
373 CFIndex dirDirIdx, dirDirLength = CFArrayGetCount(dirDirContents);
856091c5 374 for (dirDirIdx = 0; !foundIt && dirDirIdx < dirDirLength; dirDirIdx++) if (kCFCompareEqualTo == CFStringCompare(name, (CFStringRef)CFArrayGetValueAtIndex(dirDirContents, dirDirIdx), kCFCompareCaseInsensitive)) foundIt = true;
d8925383
A
375 if (!foundIt) tryToOpen = false;
376 }
856091c5 377 __CFSpinUnlock(&_cacheLock);
d8925383
A
378 CFRelease(name);
379 }
380 CFRelease(dirName);
381 }
856091c5 382 cpathBuff[lastSlashIdx] = PATH_SEP;
d8925383 383 }
bd5b749c 384 }
856091c5
A
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;
8ca704e1 430 if (tryToOpen && (dirp = opendir(cpathBuff))) {
bd5b749c 431 while ((dent = readdir(dirp))) {
8ca704e1 432 CFIndex nameLen = dent->d_namlen;
bd5b749c
A
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);
9ce05555 444 }
bd5b749c 445 CFRelease(name);
9ce05555
A
446 }
447 }
bd5b749c 448 (void)closedir(dirp);
9ce05555 449 }
856091c5 450#endif
9ce05555 451
8ca704e1
A
452 _CFArraySortValues(contents, (CFComparatorFunction)CFStringCompare, NULL);
453 _CFArraySortValues(directoryContents, (CFComparatorFunction)CFStringCompare, NULL);
454 _CFArraySortValues(unknownContents, (CFComparatorFunction)CFStringCompare, NULL);
455
856091c5
A
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);
9ce05555 460
856091c5
A
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);
9ce05555 464
856091c5
A
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);
d8925383
A
468
469 if (contentsType == _CFBundleUnknownContents) {
856091c5 470 result = (CFArrayRef)CFRetain(unknownContents);
d8925383 471 } else if (contentsType == _CFBundleDirectoryContents) {
856091c5 472 result = (CFArrayRef)CFRetain(directoryContents);
d8925383 473 } else {
856091c5 474 result = (CFArrayRef)CFRetain(contents);
d8925383
A
475 }
476
9ce05555
A
477 CFRelease(contents);
478 CFRelease(directoryContents);
d8925383 479 CFRelease(unknownContents);
856091c5 480 __CFSpinUnlock(&_cacheLock);
9ce05555 481 }
9ce05555
A
482 return result;
483}
484
485static void _CFBundleFlushContentsCaches(void) {
856091c5
A
486 __CFSpinLock(&_cacheLock);
487 if (_contentsCache) CFDictionaryRemoveAllValues(_contentsCache);
488 if (_directoryContentsCache) CFDictionaryRemoveAllValues(_directoryContentsCache);
489 if (_unknownContentsCache) CFDictionaryRemoveAllValues(_unknownContentsCache);
490 __CFSpinUnlock(&_cacheLock);
d8925383
A
491}
492
493static void _CFBundleFlushContentsCacheForPath(CFMutableDictionaryRef cache, CFStringRef path) {
bd5b749c 494 CFStringRef keys[READ_DIRECTORIES_CACHE_CAPACITY];
d8925383 495 unsigned i, count = CFDictionaryGetCount(cache);
bd5b749c 496 if (count <= READ_DIRECTORIES_CACHE_CAPACITY) {
d8925383
A
497 CFDictionaryGetKeysAndValues(cache, (const void **)keys, NULL);
498 for (i = 0; i < count; i++) {
bd5b749c 499 if (CFStringFindWithOptions(keys[i], path, CFRangeMake(0, CFStringGetLength(keys[i])), kCFCompareAnchored|kCFCompareCaseInsensitive, NULL)) CFDictionaryRemoveValue(cache, keys[i]);
d8925383
A
500 }
501 }
502}
503
504static void _CFBundleFlushContentsCachesForPath(CFStringRef path) {
856091c5
A
505 __CFSpinLock(&_cacheLock);
506 if (_contentsCache) _CFBundleFlushContentsCacheForPath(_contentsCache, path);
507 if (_directoryContentsCache) _CFBundleFlushContentsCacheForPath(_directoryContentsCache, path);
508 if (_unknownContentsCache) _CFBundleFlushContentsCacheForPath(_unknownContentsCache, path);
509 __CFSpinUnlock(&_cacheLock);
9ce05555
A
510}
511
d8925383 512CF_EXPORT void _CFBundleFlushCachesForURL(CFURLRef url) {
d8925383
A
513 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url);
514 CFStringRef path = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
515 _CFBundleFlushContentsCachesForPath(path);
516 CFRelease(path);
517 CFRelease(absoluteURL);
d8925383
A
518}
519
9ce05555 520CF_EXPORT void _CFBundleFlushCaches(void) {
9ce05555 521 _CFBundleFlushContentsCaches();
9ce05555
A
522}
523
8ca704e1 524static inline Boolean _CFIsResourceCommon(char *path, Boolean *isDir) {
9ce05555
A
525 Boolean exists;
526 SInt32 mode;
8ca704e1 527 if (_CFGetPathProperties(kCFAllocatorSystemDefault, path, &exists, &mode, NULL, NULL, NULL, NULL) == 0) {
bd5b749c 528 if (isDir) *isDir = ((exists && ((mode & S_IFMT) == S_IFDIR)) ? true : false);
9ce05555 529 return (exists && (mode & 0444));
9ce05555 530 }
8ca704e1
A
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);
9ce05555
A
539}
540
541__private_extern__ Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir) {
8ca704e1
A
542 char pathBuf[CFMaxPathSize];
543 if (!CFStringGetFileSystemRepresentation(path, pathBuf, CFMaxPathSize)) return false;
544
545 return _CFIsResourceCommon(pathBuf, isDir);
9ce05555
A
546}
547
856091c5
A
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
bd5b749c
A
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);
8ca704e1 566 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
bd5b749c
A
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++) {
856091c5 572 CFStringRef content = (CFStringRef)CFArrayGetValueAtIndex(contents, i);
bd5b749c
A
573 if (CFStringHasPrefix(content, cheapStr)) {
574 //fprintf(stderr, "found ");CFShow(content);
575 for (j = 0; j < numResTypes; j++) {
856091c5 576 CFStringRef resType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, j);
bd5b749c
A
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}
bd5b749c 588
856091c5 589#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
8ca704e1
A
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
9ce05555 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) {
8ca704e1 691
856091c5 692#if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
8ca704e1
A
693 _CFSearchBundleDirectory2(alloc, result, pathUniChars, pathLen, nameUniChars, nameLen, typeUniChars, typeLen, cheapStr, tmpString, version);
694#else
9ce05555
A
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;
cf7d2af9 702 Boolean appendSucceeded = true, platformGenericFound = false, platformSpecificFound = false, platformGenericIsDir = false, platformSpecificIsDir = false;
cf7d2af9 703 Boolean platformGenericIsUnknown = false, platformSpecificIsUnknown = false;
9ce05555
A
704 CFStringRef platformGenericStr = NULL;
705
9ce05555 706 CFIndex dirPathLen = pathLen;
d8925383
A
707 CFArrayRef contents, directoryContents, unknownContents;
708 CFRange contentsRange, directoryContentsRange, unknownContentsRange;
709
9ce05555
A
710 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
711 CFStringReplaceAll(cheapStr, tmpString);
712 //fprintf(stderr, "looking in ");CFShow(cheapStr);
8ca704e1 713 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
9ce05555 714 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
8ca704e1 715 directoryContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents);
9ce05555 716 directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents));
8ca704e1 717 unknownContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents);
d8925383 718 unknownContentsRange = CFRangeMake(0, CFArrayGetCount(unknownContents));
8ca704e1 719
d8925383 720 if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen);
9ce05555 721 savedPathLen = pathLen;
d8925383
A
722 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
723 if (appendSucceeded) {
d8925383
A
724 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1);
725 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1
A
726 platformGenericFound = _CFBundleSortedArrayContains(contents, cheapStr);
727 platformGenericIsDir = _CFBundleSortedArrayContains(directoryContents, cheapStr);
728 platformGenericIsUnknown = _CFBundleSortedArrayContains(unknownContents, cheapStr);
d8925383
A
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 }
d8925383 736 }
9ce05555
A
737
738 // Check for platform specific.
739 if (platformGenericFound) {
8ca704e1 740 platformGenericStr = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, cheapStr);
9ce05555
A
741 if (!platformSpecificFound && (_PlatformLen > 0)) {
742 pathLen = savedPathLen;
743 pathUniChars[pathLen++] = (UniChar)'-';
744 memmove(pathUniChars + pathLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
745 pathLen += _PlatformLen;
d8925383
A
746 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
747 if (appendSucceeded) {
d8925383
A
748 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1);
749 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1
A
750 platformSpecificFound = _CFBundleSortedArrayContains(contents, cheapStr);
751 platformSpecificIsDir = _CFBundleSortedArrayContains(directoryContents, cheapStr);
752 platformSpecificIsUnknown = _CFBundleSortedArrayContains(unknownContents, cheapStr);
d8925383
A
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 }
d8925383 760 }
9ce05555
A
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) {
bd5b749c 768 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, platformGenericStr ? platformGenericStr : cheapStr, PLATFORM_PATH_STYLE, platformGenericIsDir);
9ce05555
A
769 CFArrayAppendValue(result, url);
770 CFRelease(url);
771 }
bd5b749c 772 if (platformGenericStr) CFRelease(platformGenericStr);
9ce05555
A
773 CFRelease(contents);
774 CFRelease(directoryContents);
d8925383 775 CFRelease(unknownContents);
8ca704e1
A
776#endif
777}
778
8ca704e1
A
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++) {
856091c5 806 CFStringRef candidateFilename = (CFStringRef)CFArrayGetValueAtIndex(contents, i);
8ca704e1
A
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);
9ce05555 863}
856091c5 864
9ce05555 865
8ca704e1
A
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) {
8ca704e1
A
868 _CFSearchBundleDirectoryWithPredicate(alloc, result, workingUniChars, workingLen, predicate, cheapStr, tmpString, stopLooking, version);
869 return;
8ca704e1 870 }
9ce05555
A
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 {
bd5b749c
A
876 CFArrayRef subResTypes = resTypes;
877 Boolean releaseSubResTypes = false;
9ce05555 878 CFIndex i, c = CFArrayGetCount(resTypes);
bd5b749c
A
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 }
bd5b749c
A
886 for (i = 0; i < c; i++) {
887 CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(subResTypes, i);
9ce05555 888 CFIndex typeLen = CFStringGetLength(curType);
bd5b749c 889 STACK_BUFFER_DECL(UniChar, typeChars, typeLen);
9ce05555
A
890 CFStringGetCharacters(curType, CFRangeMake(0, typeLen), typeChars);
891 _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, typeChars, typeLen, cheapStr, tmpString, version);
bd5b749c 892 if (limit <= CFArrayGetCount(result)) break;
9ce05555 893 }
bd5b749c 894 if (releaseSubResTypes) CFRelease(subResTypes);
9ce05555
A
895 }
896 } else {
897 // If we have no resName, do it by hand. We may have to loop over the resTypes.
bd5b749c 898 char cpathBuff[CFMaxPathSize];
9ce05555
A
899 CFIndex cpathLen;
900 CFMutableArrayRef children;
901
902 CFStringSetExternalCharactersNoCopy(tmpString, workingUniChars, workingLen, workingLen);
bd5b749c 903 if (!CFStringGetFileSystemRepresentation(tmpString, cpathBuff, CFMaxPathSize)) return;
9ce05555
A
904 cpathLen = strlen(cpathBuff);
905
906 if (!resTypes) {
d8925383 907 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
9ce05555
A
908 children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, NULL);
909 if (children) {
910 CFIndex childIndex, childCount = CFArrayGetCount(children);
bd5b749c 911 for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex));
9ce05555
A
912 CFRelease(children);
913 }
914 } else {
915 CFIndex i, c = CFArrayGetCount(resTypes);
bd5b749c 916 for (i = 0; i < c; i++) {
9ce05555
A
917 CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, i);
918
d8925383 919 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
9ce05555
A
920 children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, curType);
921 if (children) {
922 CFIndex childIndex, childCount = CFArrayGetCount(children);
bd5b749c 923 for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex));
9ce05555
A
924 CFRelease(children);
925 }
bd5b749c 926 if (limit <= CFArrayGetCount(result)) break;
9ce05555
A
927 }
928 }
929 }
930}
931
856091c5 932
8ca704e1 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) {
9ce05555 934 CFIndex savedWorkingLen = workingLen;
8ca704e1 935 Boolean stopLooking = false; // for predicate based-queries, we set stopLooking instead of using a limit
9ce05555
A
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.
d8925383
A
939 Boolean appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _GlobalResourcesUniChars, _GlobalResourcesLen);
940 if (appendSucceeded && subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen);
8ca704e1 941 if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
9ce05555
A
942 // Strip the non-localized resource directory.
943 workingLen = savedWorkingLen;
944 }
856091c5 945
8ca704e1 946 if (CFArrayGetCount(result) < limit && !stopLooking) {
d8925383
A
947 Boolean appendSucceeded = true;
948 if (subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen);
8ca704e1 949 if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
9ce05555
A
950 }
951
856091c5
A
952 // Now search the first localized resources (user language).
953 CFIndex langCount = (searchLanguages ? CFArrayGetCount(searchLanguages) : 0);
954 UniChar curLangUniChars[255];
955
9ce05555 956 workingLen = savedWorkingLen;
856091c5
A
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
8ca704e1 980 if (CFArrayGetCount(result) < limit && !stopLooking) {
9ce05555 981 CFIndex numResults = CFArrayGetCount(result);
856091c5
A
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 }
9ce05555 996
856091c5
A
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++) {
8ca704e1 1005 CFStringRef curLangStr = (CFStringRef)CFArrayGetValueAtIndex(searchLanguages, langIndex);
856091c5 1006 CFIndex curLangLen = MIN(CFStringGetLength(curLangStr), 255);
9ce05555
A
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 }
8ca704e1 1023 _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
9ce05555
A
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
8ca704e1
A
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
9ce05555 1053 UniChar *workingUniChars, *nameUniChars, *subDirUniChars;
cf7d2af9 1054 CFIndex nameLen = 0;
9ce05555 1055 CFIndex workingLen, savedWorkingLen;
9ce05555
A
1056 CFMutableStringRef cheapStr, tmpString;
1057
cf7d2af9 1058 if (resName) {
8ca704e1 1059 char buff[CFMaxPathSize];
cf7d2af9 1060 CFStringRef newResName = NULL;
8ca704e1 1061 if (CFStringGetFileSystemRepresentation(resName, buff, CFMaxPathSize)) newResName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff);
cf7d2af9
A
1062 resName = newResName ? newResName : (CFStringRef)CFRetain(resName);
1063 nameLen = CFStringGetLength(resName);
1064 }
1065
9ce05555
A
1066 // Init the one-time-only unichar buffers.
1067 _CFEnsureStaticBuffersInited();
1068
1069 // Build UniChar buffers for some of the string pieces we need.
8ca704e1
A
1070 CFIndex subDirLen = (subDirName ? CFStringGetLength(subDirName) : 0);
1071 nameUniChars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (nameLen + subDirLen + CFMaxPathSize), 0);
cf7d2af9
A
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);
8ca704e1
A
1078
1079 if ((workingLen = CFStringGetLength(basePath)) > 0) CFStringGetCharacters(basePath, CFRangeMake(0, workingLen), workingUniChars);
cf7d2af9 1080 savedWorkingLen = workingLen;
856091c5 1081 _CFBundleSetResourceDir(workingUniChars, &workingLen, CFMaxPathSize, version);
9ce05555 1082
cf7d2af9 1083 // both of these used for temp string operations, for slightly different purposes, where each type is appropriate
8ca704e1 1084 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
cf7d2af9
A
1085 _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize);
1086 tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
9ce05555 1087
8ca704e1 1088 _CFFindBundleResourcesInResourcesDir(kCFAllocatorSystemDefault, workingUniChars, workingLen, subDirUniChars, subDirLen, searchLanguages, nameUniChars, nameLen, resTypes, limit, predicate, version, cheapStr, tmpString, result);
cf7d2af9 1089
cf7d2af9
A
1090 CFRelease(cheapStr);
1091 CFRelease(tmpString);
8ca704e1 1092 CFAllocatorDeallocate(kCFAllocatorSystemDefault, nameUniChars);
cf7d2af9
A
1093 }
1094 if (resName) CFRelease(resName);
8ca704e1 1095 if (basePath) CFRelease(basePath);
9ce05555
A
1096 return result;
1097}
1098
856091c5
A
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
9ce05555 1103CF_EXPORT CFURLRef CFBundleCopyResourceURL(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) {
856091c5
A
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
9ce05555 1109 CFURLRef result = NULL;
d8925383 1110 CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array;
8ca704e1
A
1111 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1112 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, NULL, _CFBundleLayoutVersion(bundle));
d8925383 1113 if (types) CFRelease(types);
9ce05555 1114 if (array) {
bd5b749c 1115 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
9ce05555
A
1116 CFRelease(array);
1117 }
1118 return result;
856091c5 1119#endif
9ce05555
A
1120}
1121
1122CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfType(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName) {
856091c5
A
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
d8925383 1128 CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array;
8ca704e1 1129 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
9ce05555 1130 // MF:!!! Better "limit" than 1,000,000?
8ca704e1 1131 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, NULL, _CFBundleLayoutVersion(bundle));
d8925383 1132 if (types) CFRelease(types);
9ce05555
A
1133
1134 return array;
856091c5 1135#endif
9ce05555
A
1136}
1137
cf7d2af9
A
1138CF_EXPORT CFURLRef _CFBundleCopyResourceURLForLanguage(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {
1139 return CFBundleCopyResourceURLForLocalization(bundle, resourceName, resourceType, subDirName, language);
1140}
9ce05555
A
1141
1142CF_EXPORT CFURLRef CFBundleCopyResourceURLForLocalization(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) {
856091c5
A
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
9ce05555 1148 CFURLRef result = NULL;
d8925383 1149 CFArrayRef languages = NULL, types = NULL, array;
9ce05555 1150
8ca704e1
A
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));
9ce05555 1154 if (array) {
bd5b749c 1155 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
9ce05555
A
1156 CFRelease(array);
1157 }
d8925383
A
1158 if (types) CFRelease(types);
1159 if (languages) CFRelease(languages);
9ce05555 1160 return result;
856091c5 1161#endif
9ce05555
A
1162}
1163
cf7d2af9
A
1164CF_EXPORT CFArrayRef _CFBundleCopyResourceURLsOfTypeForLanguage(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {
1165 return CFBundleCopyResourceURLsOfTypeForLocalization(bundle, resourceType, subDirName, language);
1166}
9ce05555
A
1167
1168CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeForLocalization(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) {
856091c5
A
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
d8925383 1174 CFArrayRef languages = NULL, types = NULL, array;
9ce05555 1175
8ca704e1
A
1176 if (localizationName) languages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&localizationName, 1, &kCFTypeArrayCallBacks);
1177 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
9ce05555 1178 // MF:!!! Better "limit" than 1,000,000?
8ca704e1 1179 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, NULL, _CFBundleLayoutVersion(bundle));
d8925383
A
1180 if (types) CFRelease(types);
1181 if (languages) CFRelease(languages);
9ce05555 1182 return array;
856091c5 1183#endif
9ce05555
A
1184}
1185
8ca704e1 1186
856091c5
A
1187static Boolean CFBundleAllowMixedLocalizations(void);
1188
9ce05555
A
1189CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName) {
1190 CFStringRef result = NULL;
1191 CFDictionaryRef stringTable = NULL;
cf7d2af9 1192 static CFSpinLock_t CFBundleLocalizedStringLock = CFSpinLockInit;
9ce05555 1193
bd5b749c 1194 if (!key) return (value ? (CFStringRef)CFRetain(value) : (CFStringRef)CFRetain(CFSTR("")));
9ce05555 1195
856091c5
A
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
bd5b749c 1199 if (!tableName || CFEqual(tableName, CFSTR(""))) tableName = _CFBundleDefaultStringTableName;
cf7d2af9
A
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
bd5b749c 1208 if (!stringTable) {
9ce05555
A
1209 // Go load the table.
1210 CFURLRef tableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringTableType, NULL);
1211 if (tableURL) {
1212 CFStringRef nameForSharing = NULL;
bd5b749c 1213 if (!stringTable) {
9ce05555
A
1214 CFDataRef tableData = NULL;
1215 SInt32 errCode;
1216 CFStringRef errStr;
8ca704e1 1217 if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tableURL, &tableData, NULL, NULL, &errCode)) {
bd5b749c
A
1218 stringTable = (CFDictionaryRef)CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), tableData, kCFPropertyListImmutable, &errStr);
1219 if (errStr) {
9ce05555
A
1220 CFRelease(errStr);
1221 errStr = NULL;
1222 }
d8925383
A
1223 if (stringTable && CFDictionaryGetTypeID() != CFGetTypeID(stringTable)) {
1224 CFRelease(stringTable);
1225 stringTable = NULL;
1226 }
9ce05555 1227 CFRelease(tableData);
8ca704e1 1228
9ce05555
A
1229 }
1230 }
1231 if (nameForSharing) CFRelease(nameForSharing);
8ca704e1 1232 if (tableURL) CFRelease(tableURL);
9ce05555 1233 }
bd5b749c 1234 if (!stringTable) stringTable = CFDictionaryCreate(CFGetAllocator(bundle), NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
cf7d2af9
A
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 }
9ce05555
A
1242 }
1243
bd5b749c
A
1244 result = (CFStringRef)CFDictionaryGetValue(stringTable, key);
1245 if (!result) {
bd5b749c
A
1246 if (!value) {
1247 result = (CFStringRef)CFRetain(key);
9ce05555 1248 } else if (CFEqual(value, CFSTR(""))) {
bd5b749c 1249 result = (CFStringRef)CFRetain(key);
9ce05555 1250 } else {
bd5b749c 1251 result = (CFStringRef)CFRetain(value);
9ce05555 1252 }
856091c5
A
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;
d8925383 1260 }
9ce05555
A
1261 } else {
1262 CFRetain(result);
1263 }
cf7d2af9 1264 CFRelease(stringTable);
9ce05555
A
1265 return result;
1266}
1267
1268CF_EXPORT CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) {
1269 CFURLRef result = NULL;
bd5b749c 1270 unsigned char buff[CFMaxPathSize];
9ce05555
A
1271 CFURLRef newURL = NULL;
1272
1273 if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL;
1274
bd5b749c
A
1275 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
1276 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL);
9ce05555 1277 if (_CFBundleCouldBeBundle(newURL)) {
856091c5
A
1278#ifdef CFBUNDLE_NEWLOOKUP
1279 result = (CFURLRef) _CFBundleCopyFindResources(NULL, bundleURL, NULL, resourceName, resourceType, subDirName, NULL, NO, NO, NULL);
1280#else
9ce05555 1281 uint8_t version = 0;
bd5b749c
A
1282 CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL, array;
1283 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
8ca704e1 1284 array = _CFFindBundleResources(NULL, newURL, subDirName, languages, resourceName, types, 1, NULL, version);
d8925383
A
1285 if (types) CFRelease(types);
1286 if (languages) CFRelease(languages);
9ce05555 1287 if (array) {
bd5b749c 1288 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
9ce05555
A
1289 CFRelease(array);
1290 }
856091c5 1291#endif
9ce05555
A
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;
bd5b749c 1299 unsigned char buff[CFMaxPathSize];
9ce05555
A
1300 CFURLRef newURL = NULL;
1301
1302 if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL;
1303
bd5b749c
A
1304 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
1305 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL);
9ce05555 1306 if (_CFBundleCouldBeBundle(newURL)) {
856091c5
A
1307#ifdef CFBUNDLE_NEWLOOKUP
1308 array = (CFArrayRef) _CFBundleCopyFindResources(NULL, bundleURL, NULL, NULL, resourceType, subDirName, NULL, YES, NO, NULL);
1309#else
9ce05555 1310 uint8_t version = 0;
bd5b749c
A
1311 CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL;
1312 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
9ce05555 1313 // MF:!!! Better "limit" than 1,000,000?
8ca704e1 1314 array = _CFFindBundleResources(NULL, newURL, subDirName, languages, NULL, types, 1000000, NULL, version);
d8925383
A
1315 if (types) CFRelease(types);
1316 if (languages) CFRelease(languages);
856091c5 1317#endif
9ce05555
A
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"
d8925383 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"
bd5b749c 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"
d8925383 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"
9ce05555 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"
bd5b749c 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"
9ce05555
A
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"
bd5b749c 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"
9ce05555 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"
bd5b749c
A
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"
d8925383
A
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";
9ce05555 1339
cf7d2af9
A
1340#define NUM_LOCALE_ABBREVIATIONS 109
1341#define LOCALE_ABBREVIATION_LENGTH 6
9ce05555
A
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",
d8925383 1362 "Scottish", "Manx", "Irish", "Tongan", "Greek", "Greenlandic", "Azerbaijani", "Nynorsk"
9ce05555
A
1363};
1364
cf7d2af9
A
1365#define NUM_LANGUAGE_NAMES 152
1366#define LANGUAGE_NAME_LENGTH 13
9ce05555
A
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"
d8925383 1371 "pt\0" "nb\0" "he\0" "ja\0" "ar\0" "fi\0" "el\0" "is\0"
9ce05555
A
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"
d8925383 1388 "gd\0" "gv\0" "ga\0" "to\0" "el\0" "kl\0" "az\0" "nn\0";
9ce05555 1389
cf7d2af9
A
1390#define NUM_LANGUAGE_ABBREVIATIONS 152
1391#define LANGUAGE_ABBREVIATION_LENGTH 3
9ce05555 1392
bd5b749c 1393#if defined(__CONSTANT_CFSTRINGS__)
d8925383
A
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
bd5b749c 1402#endif /* __CONSTANT_CFSTRINGS__ */
d8925383 1403
9ce05555 1404static const SInt32 __CFBundleScriptCodesArray[] = {
bd5b749c 1405 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 0,
9ce05555
A
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,
d8925383 1414 0, 0, 0, 0, 6, 0, 0, 0
9ce05555
A
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,
d8925383 1427 39, 39, 40, 0, 6, 0, 0, 0
9ce05555
A
1428};
1429
1430static SInt32 _CFBundleGetLanguageCodeForLocalization(CFStringRef localizationName) {
1431 SInt32 result = -1, i;
1432 char buff[256];
1433 CFIndex length = CFStringGetLength(localizationName);
bd5b749c 1434 if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= 255 && CFStringGetCString(localizationName, buff, 255, kCFStringEncodingASCII)) {
9ce05555
A
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 }
bd5b749c
A
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 }
9ce05555
A
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;
bd5b749c 1455 if (languageAbbreviation && *languageAbbreviation != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull);
9ce05555
A
1456 }
1457 return result;
1458}
1459
bd5b749c 1460CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) {
9ce05555
A
1461 CFStringRef result = NULL;
1462 if (0 <= languageCode && languageCode < NUM_LANGUAGE_NAMES) {
1463 const char *languageName = __CFBundleLanguageNamesArray[languageCode];
bd5b749c 1464 if (languageName && *languageName != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageName, kCFStringEncodingASCII, kCFAllocatorNull);
9ce05555
A
1465 }
1466 return result;
1467}
1468
bd5b749c 1469CF_INLINE CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName) {
9ce05555
A
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) == '_')) {
bd5b749c 1477 result = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, localizationName, CFRangeMake(0, LANGUAGE_ABBREVIATION_LENGTH - 1));
9ce05555
A
1478 }
1479 }
1480 return result;
1481}
1482
bd5b749c 1483CF_INLINE CFStringRef _CFBundleCopyModifiedLocalization(CFStringRef localizationName) {
d8925383
A
1484 CFMutableStringRef result = NULL;
1485 CFIndex length = CFStringGetLength(localizationName);
1486 if (length >= 4) {
1487 UniChar c = CFStringGetCharacterAtIndex(localizationName, 2);
1488 if ('-' == c || '_' == c) {
bd5b749c 1489 result = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, length, localizationName);
d8925383
A
1490 CFStringReplace(result, CFRangeMake(2, 1), ('-' == c) ? CFSTR("_") : CFSTR("-"));
1491 }
1492 }
1493 return result;
1494}
1495
bd5b749c 1496CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) {
9ce05555
A
1497 CFStringRef result = NULL;
1498 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1499 if (languageCode >= 0) {
1500 result = _CFBundleCopyLanguageNameForLanguageCode(languageCode);
1501 } else {
bd5b749c 1502 result = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, localizationName);
9ce05555
A
1503 }
1504 return result;
1505}
1506
1507static SInt32 _CFBundleGetLanguageCodeForRegionCode(SInt32 regionCode) {
1508 SInt32 result = -1, i;
cf7d2af9 1509 if (52 == regionCode) { // hack for mixed-up Chinese language codes
9ce05555
A
1510 result = 33;
1511 } else if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) {
1512 const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH;
bd5b749c 1513 if (localeAbbreviation && *localeAbbreviation != '\0') {
9ce05555
A
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;
cf7d2af9 1524 if (19 == languageCode) { // hack for mixed-up Chinese language codes
9ce05555
A
1525 result = 53;
1526 } else if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) {
1527 const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH;
bd5b749c 1528 if (languageAbbreviation && *languageAbbreviation != '\0') {
9ce05555
A
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 }
bd5b749c
A
1534 if (25 == result) result = 68;
1535 if (28 == result) result = 82;
9ce05555
A
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);
cf7d2af9 1543 if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= LOCALE_ABBREVIATION_LENGTH - 1 && CFStringGetCString(localizationName, buff, LOCALE_ABBREVIATION_LENGTH, kCFStringEncodingASCII)) {
9ce05555
A
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 }
bd5b749c
A
1549 if (25 == result) result = 68;
1550 if (28 == result) result = 82;
1551 if (37 == result) result = 0;
9ce05555
A
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;
bd5b749c
A
1563 if (localeAbbreviation && *localeAbbreviation != '\0') {
1564 result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, localeAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull);
9ce05555
A
1565 }
1566 }
1567 return result;
1568}
1569
1570Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding) {
bd5b749c 1571 Boolean retval = false;
9ce05555
A
1572 SInt32 language = -1, region = -1, script = 0;
1573 CFStringEncoding encoding = kCFStringEncodingMacRoman;
bd5b749c
A
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);
9ce05555 1583 }
cf7d2af9
A
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 }
bd5b749c
A
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);
9ce05555
A
1613 }
1614 if (languageCode) *languageCode = language;
1615 if (regionCode) *regionCode = region;
1616 if (scriptCode) *scriptCode = script;
1617 if (stringEncoding) *stringEncoding = encoding;
bd5b749c 1618 return retval;
9ce05555
A
1619}
1620
1621CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding) {
1622 CFStringRef localizationName = NULL;
cf7d2af9
A
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);
9ce05555
A
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
cf7d2af9 1649
9ce05555 1650__private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops) {
856091c5
A
1651 static CFArrayRef _CFBundleUserLanguages = NULL;
1652 static dispatch_once_t once = 0;
1653 dispatch_once(&once, ^{
1654 CFArrayRef preferencesArray = NULL;
9ce05555
A
1655 if (__CFAppleLanguages) {
1656 CFDataRef data;
bd5b749c 1657 CFIndex length = strlen((const char *)__CFAppleLanguages);
9ce05555 1658 if (length > 0) {
bd5b749c 1659 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8 *)__CFAppleLanguages, length, kCFAllocatorNull);
9ce05555 1660 if (data) {
cf7d2af9 1661 _CFBundleUserLanguages = (CFArrayRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL);
9ce05555
A
1662 CFRelease(data);
1663 }
1664 }
1665 }
cf7d2af9 1666 if (!_CFBundleUserLanguages && preferencesArray) _CFBundleUserLanguages = (CFArrayRef)CFRetain(preferencesArray);
d8925383
A
1667 Boolean useEnglishAsBackstop = true;
1668 // could perhaps read out of LANG environment variable
cf7d2af9 1669 if (useEnglishAsBackstop && !_CFBundleUserLanguages) {
bd5b749c 1670 CFStringRef english = CFSTR("en");
cf7d2af9 1671 _CFBundleUserLanguages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&english, 1, &kCFTypeArrayCallBacks);
9ce05555 1672 }
cf7d2af9
A
1673 if (_CFBundleUserLanguages && CFGetTypeID(_CFBundleUserLanguages) != CFArrayGetTypeID()) {
1674 CFRelease(_CFBundleUserLanguages);
1675 _CFBundleUserLanguages = NULL;
9ce05555 1676 }
856091c5
A
1677 if (preferencesArray) CFRelease(preferencesArray);
1678 });
1679
1680 if (_CFBundleUserLanguages) {
1681 CFRetain(_CFBundleUserLanguages);
1682 return _CFBundleUserLanguages;
1683 } else {
1684 return NULL;
9ce05555 1685 }
9ce05555
A
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;
9ce05555
A
1694 if (mainBundle) {
1695 languages = _CFBundleGetLanguageSearchList(mainBundle);
1696 if (languages) CFRetain(languages);
1697 }
1698 if (!languages) languages = _CFBundleCopyUserLanguages(false);
bd5b749c
A
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;
cf7d2af9
A
1704 retval = CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(localizationName, &langCode, &regCode, NULL, NULL);
1705 if (retval) {
1706 language = langCode;
1707 region = regCode;
1708 }
bd5b749c
A
1709 if (!retval) {
1710 language = _CFBundleGetLanguageCodeForLocalization(localizationName);
1711 region = _CFBundleGetRegionCodeForLocalization(localizationName);
1712 }
9ce05555
A
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
d8925383 1724
cf7d2af9 1725static Boolean _CFBundleTryOnePreferredLprojNameInDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, uint8_t version, CFDictionaryRef infoDict, CFStringRef curLangStr, CFMutableArrayRef lprojNames, Boolean fallBackToLanguage) {
bd5b749c 1726 CFIndex curLangLen = CFStringGetLength(curLangStr), savedPathLen;
9ce05555 1727 UniChar curLangUniChars[255];
cf7d2af9 1728 CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL, canonicalLanguageAbbreviation = NULL;
d8925383 1729 CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL, predefinedCanonicalLanguageIdentifiers = NULL;
cf7d2af9 1730 Boolean foundOne = false, specifiesScript = false;
9ce05555
A
1731 CFArrayRef predefinedLocalizations = NULL;
1732 CFRange predefinedLocalizationsRange;
1733 CFMutableStringRef cheapStr, tmpString;
d8925383
A
1734 CFArrayRef contents;
1735 CFRange contentsRange;
9ce05555
A
1736
1737 // both of these used for temp string operations, for slightly
1738 // different purposes, where each type is appropriate
8ca704e1 1739 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
9ce05555 1740 _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize);
d8925383
A
1741 tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
1742
9ce05555
A
1743 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1744 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1 1745 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
d8925383 1746 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
9ce05555
A
1747
1748 if (infoDict) {
bd5b749c
A
1749 predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
1750 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) {
9ce05555
A
1751 predefinedLocalizations = NULL;
1752 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey);
1753 }
1754 }
1755 predefinedLocalizationsRange = CFRangeMake(0, predefinedLocalizations ? CFArrayGetCount(predefinedLocalizations) : 0);
1756
d8925383 1757 if (curLangLen > 255) curLangLen = 255;
9ce05555
A
1758 CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1759 savedPathLen = pathLen;
d8925383 1760 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
d8925383
A
1761 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1762 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1 1763 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
d8925383
A
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);
d8925383 1769 CFRelease(contents);
d8925383
A
1770 return foundOne;
1771 }
1772 }
1773 }
bd5b749c
A
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 }
d8925383 1781 }
bd5b749c 1782#endif /* __CONSTANT_CFSTRINGS__ */
d8925383
A
1783 if (foundOne && altLangStr) {
1784 CFRelease(cheapStr);
1785 CFRelease(tmpString);
d8925383 1786 CFRelease(contents);
d8925383
A
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)) {
d8925383
A
1795 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1796 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1 1797 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
d8925383
A
1798 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr);
1799 foundOne = true;
1800 CFRelease(cheapStr);
1801 CFRelease(tmpString);
d8925383 1802 CFRelease(contents);
d8925383
A
1803 return foundOne;
1804 }
1805 }
1806 }
bd5b749c 1807 if (!foundOne && (!predefinedLocalizations || CFArrayGetCount(predefinedLocalizations) == 0)) {
d8925383 1808 Boolean hasLocalizations = false;
bd5b749c 1809 CFIndex idx;
d8925383 1810 for (idx = 0; !hasLocalizations && idx < contentsRange.length; idx++) {
856091c5 1811 CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(contents, idx);
d8925383
A
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 }
d8925383
A
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)) {
d8925383
A
1827 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1828 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1 1829 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
d8925383
A
1830 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr);
1831 foundOne = true;
1832 }
1833 }
9ce05555 1834 }
cf7d2af9 1835 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) {
9ce05555 1836 curLangLen = CFStringGetLength(languageAbbreviation);
d8925383 1837 if (curLangLen > 255) curLangLen = 255;
9ce05555
A
1838 CFStringGetCharacters(languageAbbreviation, CFRangeMake(0, curLangLen), curLangUniChars);
1839 pathLen = savedPathLen;
d8925383 1840 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
d8925383
A
1841 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1842 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1 1843 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
d8925383
A
1844 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation);
1845 foundOne = true;
1846 }
9ce05555
A
1847 }
1848 }
cf7d2af9 1849 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) {
9ce05555 1850 curLangLen = CFStringGetLength(languageName);
d8925383 1851 if (curLangLen > 255) curLangLen = 255;
9ce05555
A
1852 CFStringGetCharacters(languageName, CFRangeMake(0, curLangLen), curLangUniChars);
1853 pathLen = savedPathLen;
d8925383 1854 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
d8925383
A
1855 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1856 CFStringReplaceAll(cheapStr, tmpString);
8ca704e1 1857 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
d8925383
A
1858 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName);
1859 foundOne = true;
1860 }
9ce05555
A
1861 }
1862 }
d8925383 1863 if (modifiedLangStr) CFRelease(modifiedLangStr);
9ce05555
A
1864 if (languageAbbreviation) CFRelease(languageAbbreviation);
1865 if (languageName) CFRelease(languageName);
d8925383
A
1866 if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier);
1867 if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers);
1868 if (predefinedCanonicalLanguageIdentifiers) CFRelease(predefinedCanonicalLanguageIdentifiers);
cf7d2af9 1869 if (canonicalLanguageAbbreviation) CFRelease(canonicalLanguageAbbreviation);
d8925383
A
1870 CFRelease(cheapStr);
1871 CFRelease(tmpString);
d8925383 1872 CFRelease(contents);
9ce05555
A
1873 return foundOne;
1874}
1875
1876static Boolean CFBundleAllowMixedLocalizations(void) {
856091c5
A
1877 static Boolean allowMixed = false;
1878 static dispatch_once_t once = 0;
1879 dispatch_once(&once, ^{
9ce05555
A
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 }
856091c5
A
1893 }
1894 });
9ce05555
A
1895 return allowMixed;
1896}
1897
cf7d2af9
A
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
9ce05555
A
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.
8ca704e1 1919 CFURLRef resourcesURL = _CFBundleCopyResourcesDirectoryURLInDirectory(bundleURL, version);
9ce05555 1920 CFURLRef absoluteURL;
cf7d2af9 1921 CFIndex idx, startIdx;
9ce05555
A
1922 CFIndex count;
1923 CFStringRef resourcesPath;
1924 UniChar pathUniChars[CFMaxPathSize];
1925 CFIndex pathLen;
cf7d2af9 1926 CFStringRef curLangStr, nextLangStr;
9ce05555 1927 Boolean foundOne = false;
9ce05555
A
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);
d8925383 1938 if (pathLen > CFMaxPathSize) pathLen = CFMaxPathSize;
9ce05555
A
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);
cf7d2af9
A
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);
8ca704e1 1954 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
cf7d2af9 1955 }
9ce05555 1956 }
cf7d2af9 1957 CFRelease(mainBundleURL);
9ce05555 1958 }
9ce05555
A
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);
cf7d2af9 1966 for (idx = 0, startIdx = -1; !foundOne && idx < count; idx++) {
bd5b749c 1967 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx);
cf7d2af9
A
1968 nextLangStr = (idx + 1 < count) ? (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx + 1) : NULL;
1969 if (nextLangStr && _CFBundleLocalizationsHaveCommonPrefix(curLangStr, nextLangStr)) {
8ca704e1 1970 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, false);
cf7d2af9
A
1971 if (startIdx < 0) startIdx = idx;
1972 } else if (startIdx >= 0 && startIdx <= idx) {
8ca704e1 1973 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, false);
cf7d2af9
A
1974 for (; !foundOne && startIdx <= idx; startIdx++) {
1975 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, startIdx);
8ca704e1 1976 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
cf7d2af9
A
1977 }
1978 startIdx = -1;
1979 } else {
8ca704e1 1980 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
cf7d2af9
A
1981 startIdx = -1;
1982 }
9ce05555
A
1983 }
1984 // use development region and U.S. English as backstops
8ca704e1
A
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);
bd5b749c 1987 if (userLanguages) CFRelease(userLanguages);
9ce05555
A
1988 }
1989}
1990
cf7d2af9
A
1991static Boolean _CFBundleTryOnePreferredLprojNameInArray(CFArrayRef array, CFStringRef curLangStr, CFMutableArrayRef lprojNames, Boolean fallBackToLanguage) {
1992 Boolean foundOne = false, specifiesScript = false;
9ce05555 1993 CFRange range = CFRangeMake(0, CFArrayGetCount(array));
cf7d2af9 1994 CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL, canonicalLanguageAbbreviation = NULL;
d8925383 1995 CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL;
9ce05555 1996
d8925383 1997 if (range.length == 0) return foundOne;
9ce05555 1998 if (CFArrayContainsValue(array, range, curLangStr)) {
d8925383
A
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;
bd5b749c
A
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 }
d8925383 2011 }
bd5b749c 2012#endif /* __CONSTANT_CFSTRINGS__ */
d8925383
A
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);
9ce05555 2016 foundOne = true;
d8925383 2017 return foundOne;
9ce05555 2018 }
d8925383
A
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 }
cf7d2af9 2025 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) {
9ce05555 2026 if (CFArrayContainsValue(array, range, languageAbbreviation)) {
d8925383 2027 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation);
9ce05555
A
2028 foundOne = true;
2029 }
2030 }
cf7d2af9 2031 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) {
9ce05555 2032 if (CFArrayContainsValue(array, range, languageName)) {
d8925383 2033 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName);
9ce05555
A
2034 foundOne = true;
2035 }
2036 }
d8925383 2037 if (modifiedLangStr) CFRelease(modifiedLangStr);
9ce05555
A
2038 if (languageAbbreviation) CFRelease(languageAbbreviation);
2039 if (languageName) CFRelease(languageName);
d8925383
A
2040 if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier);
2041 if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers);
cf7d2af9 2042 if (canonicalLanguageAbbreviation) CFRelease(canonicalLanguageAbbreviation);
9ce05555
A
2043 return foundOne;
2044}
2045
2046static CFArrayRef _CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray, Boolean considerMain) {
bd5b749c 2047 CFMutableArrayRef lprojNames = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
9ce05555 2048 Boolean foundOne = false, releasePrefArray = false;
cf7d2af9 2049 CFIndex idx, count, startIdx;
9ce05555
A
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);
cf7d2af9 2056 if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0), lprojNames, true);
9ce05555
A
2057 }
2058 }
cf7d2af9 2059
9ce05555 2060 if (!foundOne) {
cf7d2af9 2061 CFStringRef curLangStr, nextLangStr;
9ce05555
A
2062 if (!prefArray) {
2063 prefArray = _CFBundleCopyUserLanguages(true);
2064 if (prefArray) releasePrefArray = true;
2065 }
2066 count = (prefArray ? CFArrayGetCount(prefArray) : 0);
cf7d2af9
A
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 }
9ce05555
A
2084 }
2085 // use U.S. English as backstop
cf7d2af9 2086 if (!foundOne) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, CFSTR("en_US"), lprojNames, true);
9ce05555 2087 // use random entry as backstop
cf7d2af9 2088 if (!foundOne && CFArrayGetCount(locArray) > 0) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(locArray, 0), lprojNames, true);
9ce05555
A
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
cf7d2af9
A
2100CF_EXPORT CFArrayRef CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray) {
2101 return _CFBundleCopyLocalizationsForPreferences(locArray, prefArray, false);
2102}
9ce05555 2103
cf7d2af9
A
2104CF_EXPORT CFArrayRef CFBundleCopyPreferredLocalizationsFromArray(CFArrayRef locArray) {
2105 return _CFBundleCopyLocalizationsForPreferences(locArray, NULL, true);
2106}
9ce05555
A
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;
8ca704e1 2111 CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, &localVersion);
9ce05555 2112 CFStringRef devLang = NULL;
bd5b749c
A
2113 if (infoDict) devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey);
2114 if (devLang && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) devLang = NULL;
9ce05555
A
2115
2116 _CFBundleAddPreferredLprojNamesInDirectory(alloc, url, localVersion, infoDict, langs, devLang);
2117
bd5b749c
A
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
8ca704e1 2123 if (infoDict && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
bd5b749c 2124 if (version) *version = localVersion;
9ce05555
A
2125 return langs;
2126}
2127
2128CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) {
2129 Boolean result = false;
bd5b749c 2130 CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault, url);
9ce05555
A
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) {
bd5b749c 2140 Boolean isDir = false, result = false;
cf7d2af9 2141 CFURLRef dirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, subDirName, url);
bd5b749c
A
2142 if (dirURL) {
2143 if (_CFIsResourceAtURL(dirURL, &isDir) && isDir) result = true;
9ce05555
A
2144 CFRelease(dirURL);
2145 }
2146 return result;
2147}
2148
2149__private_extern__ Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, uint8_t *version) {
9ce05555
A
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)
d8925383 2157 uint8_t localVersion = 3;
d8925383
A
2158 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url);
2159 CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
8ca704e1 2160 CFArrayRef contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
856091c5
A
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) {
8ca704e1
A
2167 if (_CFBundleSortedArrayContains(contents, _CFBundleResourcesDirectoryName)) localVersion = 0;
2168 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName2)) localVersion = 2;
2169 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName1)) localVersion = 1;
9ce05555 2170 } else {
8ca704e1
A
2171 if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName2)) localVersion = 2;
2172 else if (_CFBundleSortedArrayContains(contents, _CFBundleResourcesDirectoryName)) localVersion = 0;
2173 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName1)) localVersion = 1;
d8925383 2174 }
9f29f3f8
A
2175 if (contents) CFRelease(contents);
2176 if (directoryPath) CFRelease(directoryPath);
2177 if (absoluteURL) CFRelease(absoluteURL);
856091c5 2178#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
d8925383 2179 if (localVersion == 3) {
856091c5 2180 if (hasFrameworkSuffix) {
d8925383
A
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;
9ce05555
A
2188 }
2189 }
856091c5 2190#endif
d8925383 2191 if (version) *version = localVersion;
cf7d2af9 2192 return (localVersion != 3);
9ce05555
A
2193}
2194
8ca704e1
A
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) {
856091c5 2231#if __CONSTANT_STRINGS__
8ca704e1
A
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 }
856091c5 2238#endif
8ca704e1
A
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
9ce05555
A
2417__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) {
2418 CFDictionaryRef dict = NULL;
bd5b749c 2419 unsigned char buff[CFMaxPathSize];
9ce05555
A
2420 uint8_t localVersion = 0;
2421
2422 if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) {
8ca704e1 2423 CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
bd5b749c 2424 if (!newURL) newURL = (CFURLRef)CFRetain(url);
9ce05555 2425
bd5b749c
A
2426 // version 3 is for flattened pseudo-bundles with no Contents, Support Files, or Resources directories
2427 if (!_CFBundleURLLooksLikeBundleVersion(newURL, &localVersion)) localVersion = 3;
2428
9ce05555
A
2429 dict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc, newURL, localVersion);
2430 CFRelease(newURL);
2431 }
2432 if (version) *version = localVersion;
2433 return dict;
2434}
2435
8ca704e1 2436// returns zero-ref dictionary under GC if given kCFAllocatorSystemDefaultGCRefZero
9ce05555
A
2437__private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, uint8_t version) {
2438 CFDictionaryRef result = NULL;
bd5b749c
A
2439 if (url) {
2440 CFURLRef infoURL = NULL, rawInfoURL = NULL;
9ce05555
A
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;
d8925383
A
2447 CFURLRef directoryURL = NULL, absoluteURL;
2448 CFStringRef directoryPath;
2449 CFArrayRef contents = NULL;
2450 CFRange contentsRange = CFRangeMake(0, 0);
9ce05555
A
2451
2452 _CFEnsureStaticBuffersInited();
2453
2454 if (0 == version) {
8ca704e1 2455 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, url);
9ce05555
A
2456 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0;
2457 infoURLFromBase = _CFBundleInfoURLFromBase0;
2458 } else if (1 == version) {
8ca704e1 2459 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, url);
9ce05555
A
2460 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension1;
2461 infoURLFromBase = _CFBundleInfoURLFromBase1;
2462 } else if (2 == version) {
8ca704e1 2463 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, url);
9ce05555
A
2464 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension2;
2465 infoURLFromBase = _CFBundleInfoURLFromBase2;
2466 } else if (3 == version) {
cf7d2af9 2467 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
9ce05555 2468 // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle
cf7d2af9
A
2469 if (path) {
2470 if (!(CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(path, _CFBundleResourcesDirectoryName))) {
856091c5 2471 directoryURL = (CFURLRef)CFRetain(url);
9ce05555
A
2472 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3;
2473 infoURLFromBase = _CFBundleInfoURLFromBase3;
2474 }
cf7d2af9 2475 CFRelease(path);
9ce05555
A
2476 }
2477 }
d8925383
A
2478 if (directoryURL) {
2479 absoluteURL = CFURLCopyAbsoluteURL(directoryURL);
2480 directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
8ca704e1 2481 contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
d8925383 2482 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
9f29f3f8
A
2483 if (directoryPath) CFRelease(directoryPath);
2484 if (absoluteURL) CFRelease(absoluteURL);
2485 if (directoryURL) CFRelease(directoryURL);
d8925383 2486 }
d8925383 2487
9ce05555
A
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);
8ca704e1 2494 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
9ce05555 2495 CFStringAppendCharacters(cheapStr, buff, len);
8ca704e1 2496 infoURL = CFURLCreateWithString(kCFAllocatorSystemDefault, cheapStr, url);
d8925383
A
2497 if (contents) {
2498 CFIndex resourcesLen, idx;
856091c5 2499 for (resourcesLen = len; resourcesLen > 0; resourcesLen--) if (buff[resourcesLen - 1] == PATH_SEP) break;
9ce05555
A
2500 CFStringDelete(cheapStr, CFRangeMake(0, CFStringGetLength(cheapStr)));
2501 CFStringAppendCharacters(cheapStr, buff + resourcesLen, len - resourcesLen);
d8925383
A
2502 for (tryPlatformSpecific = false, idx = 0; !tryPlatformSpecific && idx < contentsRange.length; idx++) {
2503 // Need to do this case-insensitive to accommodate Palm
856091c5 2504 if (kCFCompareEqualTo == CFStringCompare(cheapStr, (CFStringRef)CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryPlatformSpecific = true;
d8925383 2505 }
9ce05555 2506 }
8ca704e1 2507 if (tryPlatformSpecific) CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoURL, &infoData, NULL, NULL, NULL);
d8925383 2508 //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryPlatformSpecific ? "missed it\n" : "skipped it\n"));
9ce05555
A
2509 CFRelease(cheapStr);
2510 if (!infoData) {
2511 // Check for global Info.plist
2512 CFRelease(infoURL);
8ca704e1 2513 infoURL = CFURLCreateWithString(kCFAllocatorSystemDefault, infoURLFromBase, url);
d8925383
A
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
856091c5 2518 if (kCFCompareEqualTo == CFStringCompare(_CFBundleInfoFileName, (CFStringRef)CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryGlobal = true;
d8925383
A
2519 }
2520 }
8ca704e1 2521 if (tryGlobal) CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoURL, &infoData, NULL, NULL, NULL);
d8925383 2522 //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryGlobal ? "missed it\n" : "skipped it\n"));
9ce05555
A
2523 }
2524
2525 if (infoData) {
bd5b749c 2526 result = (CFDictionaryRef)CFPropertyListCreateFromXMLData(alloc, infoData, kCFPropertyListMutableContainers, NULL);
9ce05555
A
2527 if (result) {
2528 if (CFDictionaryGetTypeID() == CFGetTypeID(result)) {
2529 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, infoURL);
2530 } else {
8ca704e1 2531 if (!_CFAllocatorIsGCRefZero(alloc)) CFRelease(result);
9ce05555
A
2532 result = NULL;
2533 }
2534 }
bd5b749c 2535 if (!result) rawInfoURL = infoURL;
9ce05555
A
2536 CFRelease(infoData);
2537 }
2538 if (!result) {
2539 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
bd5b749c 2540 if (rawInfoURL) CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleRawInfoPlistURLKey, rawInfoURL);
9ce05555
A
2541 }
2542
2543 CFRelease(infoURL);
d8925383 2544 if (contents) CFRelease(contents);
9ce05555 2545 }
8ca704e1 2546 _processInfoDictionary((CFMutableDictionaryRef)result, _CFGetPlatformName(), _CFGetProductName());
9ce05555
A
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
8ca704e1
A
2556 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase2, url);
2557 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
9ce05555 2558 CFRelease(tempURL);
bd5b749c 2559 if (!pkgInfoData) {
8ca704e1
A
2560 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase1, url);
2561 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
9ce05555
A
2562 CFRelease(tempURL);
2563 }
bd5b749c 2564 if (!pkgInfoData) {
9ce05555 2565 // Check for a "pseudo" new bundle
8ca704e1
A
2566 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePseudoPkgInfoURLFromBase, url);
2567 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
9ce05555
A
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
bd5b749c 2575 if (pkgInfoData && CFDataGetLength(pkgInfoData) >= (int)(sizeof(UInt32) * 2)) {
9ce05555 2576 UInt32 *pkgInfo = (UInt32 *)CFDataGetBytePtr(pkgInfoData);
bd5b749c
A
2577 if (packageType) *packageType = CFSwapInt32BigToHost(pkgInfo[0]);
2578 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(pkgInfo[1]);
9ce05555
A
2579 retVal = hasType = hasCreator = true;
2580 }
bd5b749c 2581 if (pkgInfoData) CFRelease(pkgInfoData);
9ce05555
A
2582 if (!retVal) {
2583 if (!infoDict) {
8ca704e1 2584 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, NULL);
9ce05555
A
2585 releaseInfoDict = true;
2586 }
2587 if (infoDict) {
bd5b749c 2588 CFStringRef typeString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleSignatureKey);
9ce05555
A
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) {
bd5b749c 2592 if (packageType) *packageType = CFSwapInt32BigToHost(tmp);
9ce05555
A
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) {
bd5b749c 2596 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(tmp);
9ce05555
A
2597 retVal = hasCreator = true;
2598 }
8ca704e1 2599 if (releaseInfoDict && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
9ce05555
A
2600 }
2601 }
2602 if (!hasType || !hasCreator) {
2603 // If this looks like a bundle then manufacture the type and creator.
2604 if (retVal || _CFBundleURLLooksLikeBundle(url)) {
bd5b749c
A
2605 if (packageCreator && !hasCreator) *packageCreator = 0x3f3f3f3f; // '????'
2606 if (packageType && !hasType) {
9ce05555
A
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);
d8925383 2617 if (strLen > CFMaxPathSize) strLen = CFMaxPathSize;
9ce05555
A
2618 CFStringGetCharacters(urlStr, CFRangeMake(0, strLen), buff);
2619 CFRelease(urlStr);
2620 startOfExtension = _CFStartOfPathExtension(buff, strLen);
856091c5 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)) {
9ce05555 2622 // This is an app
4c91a73d 2623 *packageType = 0x4150504c; // 'APPL'
856091c5 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)) {
9ce05555 2625 // This is an app (debug version)
4c91a73d 2626 *packageType = 0x4150504c; // 'APPL'
856091c5 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)) {
9ce05555 2628 // This is an app (profile version)
4c91a73d 2629 *packageType = 0x4150504c; // 'APPL'
856091c5 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)) {
9ce05555 2631 // This is a service
4c91a73d 2632 *packageType = 0x4150504c; // 'APPL'
856091c5 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)) {
9ce05555 2634 // This is a framework
4c91a73d 2635 *packageType = 0x464d574b; // 'FMWK'
9ce05555
A
2636 } else {
2637 // Default to BNDL for generic bundle
4c91a73d 2638 *packageType = 0x424e444c; // 'BNDL'
9ce05555
A
2639 }
2640 }
2641 retVal = true;
2642 }
2643 }
2644 return retVal;
2645}
2646
cf7d2af9
A
2647CF_EXPORT Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
2648 return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc, url, NULL, packageType, packageCreator);
2649}
9ce05555
A
2650
2651CF_EXPORT void CFBundleGetPackageInfo(CFBundleRef bundle, UInt32 *packageType, UInt32 *packageCreator) {
2652 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle);
8ca704e1 2653 if (!_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(kCFAllocatorSystemDefault, bundleURL, CFBundleGetInfoDictionary(bundle), packageType, packageCreator)) {
bd5b749c
A
2654 if (packageType) *packageType = 0x424e444c; // 'BNDL'
2655 if (packageCreator) *packageCreator = 0x3f3f3f3f; // '????'
9ce05555
A
2656 }
2657 if (bundleURL) CFRelease(bundleURL);
2658}
2659
cf7d2af9
A
2660CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
2661 return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault, url, packageType, packageCreator);
2662}
9ce05555 2663
cf7d2af9
A
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) {
8ca704e1 2694 CFMutableArrayRef mutableArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
cf7d2af9
A
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) {
bd5b749c 2726#if DEPLOYMENT_TARGET_MACOSX
9ce05555 2727 return CFSTR("MacOS");
856091c5 2728#elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
cf7d2af9
A
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) {
856091c5 2746#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
cf7d2af9
A
2747 return CFSTR("MacOS");
2748#elif DEPLOYMENT_TARGET_WINDOWS
2749 return CFSTR("Windows");
bd5b749c 2750#elif DEPLOYMENT_TARGET_SOLARIS
9ce05555 2751 return CFSTR("Solaris");
bd5b749c
A
2752#elif DEPLOYMENT_TARGET_HPUX
2753 return CFSTR("HPUX");
2754#elif DEPLOYMENT_TARGET_LINUX
9ce05555 2755 return CFSTR("Linux");
bd5b749c 2756#elif DEPLOYMENT_TARGET_FREEBSD
9ce05555
A
2757 return CFSTR("FreeBSD");
2758#else
bd5b749c 2759#error Unknown or unspecified DEPLOYMENT_TARGET
9ce05555
A
2760#endif
2761}
2762
2763__private_extern__ CFStringRef _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(void) {
856091c5 2764#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
9ce05555 2765 return CFSTR("Mac OS X");
cf7d2af9
A
2766#elif DEPLOYMENT_TARGET_WINDOWS
2767 return CFSTR("WinNT");
bd5b749c 2768#elif DEPLOYMENT_TARGET_SOLARIS
9ce05555 2769 return CFSTR("Solaris");
bd5b749c
A
2770#elif DEPLOYMENT_TARGET_HPUX
2771 return CFSTR("HP-UX");
2772#elif DEPLOYMENT_TARGET_LINUX
9ce05555 2773 return CFSTR("Linux");
bd5b749c 2774#elif DEPLOYMENT_TARGET_FREEBSD
9ce05555
A
2775 return CFSTR("FreeBSD");
2776#else
bd5b749c 2777#error Unknown or unspecified DEPLOYMENT_TARGET
9ce05555
A
2778#endif
2779}
2780
2781__private_extern__ CFStringRef _CFBundleGetOtherPlatformExecutablesSubdirectoryName(void) {
856091c5 2782#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
9ce05555 2783 return CFSTR("MacOSClassic");
cf7d2af9
A
2784#elif DEPLOYMENT_TARGET_WINDOWS
2785 return CFSTR("Other");
bd5b749c 2786#elif DEPLOYMENT_TARGET_HPUX
9ce05555 2787 return CFSTR("Other");
bd5b749c 2788#elif DEPLOYMENT_TARGET_SOLARIS
9ce05555 2789 return CFSTR("Other");
bd5b749c 2790#elif DEPLOYMENT_TARGET_LINUX
9ce05555 2791 return CFSTR("Other");
bd5b749c 2792#elif DEPLOYMENT_TARGET_FREEBSD
9ce05555
A
2793 return CFSTR("Other");
2794#else
bd5b749c 2795#error Unknown or unspecified DEPLOYMENT_TARGET
9ce05555
A
2796#endif
2797}
2798
2799__private_extern__ CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(void) {
856091c5 2800#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
9ce05555 2801 return CFSTR("Mac OS 8");
cf7d2af9
A
2802#elif DEPLOYMENT_TARGET_WINDOWS
2803 return CFSTR("Other");
bd5b749c 2804#elif DEPLOYMENT_TARGET_HPUX
9ce05555 2805 return CFSTR("Other");
bd5b749c 2806#elif DEPLOYMENT_TARGET_SOLARIS
9ce05555 2807 return CFSTR("Other");
bd5b749c 2808#elif DEPLOYMENT_TARGET_LINUX
9ce05555 2809 return CFSTR("Other");
bd5b749c 2810#elif DEPLOYMENT_TARGET_FREEBSD
9ce05555
A
2811 return CFSTR("Other");
2812#else
bd5b749c 2813#error Unknown or unspecified DEPLOYMENT_TARGET
9ce05555
A
2814#endif
2815}
2816
cf7d2af9
A
2817__private_extern__ CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle) {
2818 return CFBundleCopyBundleLocalizations(bundle);
2819}
9ce05555
A
2820
2821CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) {
d8925383 2822 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
9ce05555 2823 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
d8925383
A
2824 CFURLRef absoluteURL;
2825 CFStringRef directoryPath;
2826 CFArrayRef contents;
2827 CFRange contentsRange;
2828 CFIndex idx;
9ce05555 2829 CFArrayRef predefinedLocalizations = NULL;
9ce05555
A
2830 CFMutableArrayRef result = NULL;
2831
2832 if (infoDict) {
bd5b749c
A
2833 predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
2834 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) {
9ce05555
A
2835 predefinedLocalizations = NULL;
2836 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey);
2837 }
bd5b749c 2838 if (predefinedLocalizations) {
9ce05555 2839 CFIndex i, c = CFArrayGetCount(predefinedLocalizations);
d8925383
A
2840 if (c > 0 && !result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2841 for (i = 0; i < c; i++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(predefinedLocalizations, i));
9ce05555
A
2842 }
2843 }
2844
d8925383
A
2845 if (resourcesURL) {
2846 absoluteURL = CFURLCopyAbsoluteURL(resourcesURL);
2847 directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
8ca704e1 2848 contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
d8925383
A
2849 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
2850 for (idx = 0; idx < contentsRange.length; idx++) {
856091c5 2851 CFStringRef name = (CFStringRef)CFArrayGetValueAtIndex(contents, idx);
d8925383 2852 if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) {
bd5b749c 2853 CFStringRef localization = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, name, CFRangeMake(0, CFStringGetLength(name) - 6));
d8925383
A
2854 if (!result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2855 CFArrayAppendValue(result, localization);
2856 CFRelease(localization);
2857 }
2858 }
9f29f3f8
A
2859 if (contents) CFRelease(contents);
2860 if (directoryPath) CFRelease(directoryPath);
2861 if (absoluteURL) CFRelease(absoluteURL);
d8925383 2862 }
d8925383 2863
9ce05555
A
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);
9ce05555
A
2872 return result;
2873}
2874
2875
2876CF_EXPORT CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) {
2877 CFDictionaryRef result = NULL;
bd5b749c 2878 Boolean isDir = false;
9ce05555
A
2879 if (_CFIsResourceAtURL(url, &isDir)) {
2880 if (isDir) {
8ca704e1 2881 result = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, NULL);
9ce05555 2882 } else {
8ca704e1 2883 result = _CFBundleCopyInfoDictionaryInExecutable(url); // return zero-ref dictionary under GC
9ce05555
A
2884 }
2885 }
8ca704e1 2886 if (result && _CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRetain(result); // conditionally put on a retain for a Copy function
9ce05555
A
2887 return result;
2888}
2889
bd5b749c
A
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
9ce05555
A
2902CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) {
2903 CFArrayRef result = NULL;
bd5b749c 2904 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
9ce05555
A
2905 CFStringRef devLang = NULL;
2906 if (bundle) {
2907 result = CFBundleCopyBundleLocalizations(bundle);
2908 CFRelease(bundle);
2909 } else {
8ca704e1 2910 CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInExecutable(url); // return zero-ref dictionary under GC
9ce05555 2911 if (infoDict) {
bd5b749c
A
2912 CFArrayRef predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
2913 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) == CFArrayGetTypeID()) result = (CFArrayRef)CFRetain(predefinedLocalizations);
9ce05555 2914 if (!result) {
bd5b749c
A
2915 devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey);
2916 if (devLang && (CFGetTypeID(devLang) == CFStringGetTypeID() && CFStringGetLength(devLang) > 0)) result = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&devLang, 1, &kCFTypeArrayCallBacks);
9ce05555 2917 }
8ca704e1 2918 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
9ce05555
A
2919 }
2920 }
2921 return result;
2922}
856091c5
A
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}