]> git.saurik.com Git - apple/cf.git/blob - CFBundle_Resources.c
CF-635.19.tar.gz
[apple/cf.git] / CFBundle_Resources.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFBundle_Resources.c
25 Copyright (c) 1999-2011, Apple Inc. All rights reserved.
26 Responsibility: David Smith
27 */
28
29 #if DEPLOYMENT_TARGET_MACOSX
30 #define READ_DIRECTORIES 1
31 #elif DEPLOYMENT_TARGET_EMBEDDED
32 #define READ_DIRECTORIES 1
33 #elif DEPLOYMENT_TARGET_WINDOWS
34 #define READ_DIRECTORIES 0
35 #else
36 #error Unknown or unspecified DEPLOYMENT_TARGET
37 #endif
38
39 #define READ_DIRECTORIES_CACHE_CAPACITY 128
40
41 #include "CFBundle_Internal.h"
42 #include <CoreFoundation/CFURLAccess.h>
43 #include <CoreFoundation/CFPropertyList.h>
44 #include <CoreFoundation/CFByteOrder.h>
45 #include <CoreFoundation/CFNumber.h>
46 #include <CoreFoundation/CFLocale.h>
47 #include <CoreFoundation/CFPreferences.h>
48 #include <string.h>
49 #include "CFInternal.h"
50 #include <CoreFoundation/CFPriv.h>
51 #include <sys/stat.h>
52 #include <fcntl.h>
53 #include <stdio.h>
54 #include <ctype.h>
55 #include <errno.h>
56 #include <sys/types.h>
57 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
58 #include <sys/sysctl.h>
59 #endif
60
61 #if DEPLOYMENT_TARGET_MACOSX
62 #include <unistd.h>
63 #elif DEPLOYMENT_TARGET_EMBEDDED
64 #include <unistd.h>
65 #elif DEPLOYMENT_TARGET_WINDOWS
66 #else
67 #error Unknown or unspecified DEPLOYMENT_TARGET
68 #endif
69
70 #if READ_DIRECTORIES
71 #include <dirent.h>
72 #endif /* READ_DIRECTORIES */
73
74 CF_EXPORT bool CFDictionaryGetKeyIfPresent(CFDictionaryRef dict, const void *key, const void **actualkey);
75
76
77 static inline Boolean _CFBundleSortedArrayContains(CFArrayRef arr, CFStringRef target) {
78 CFRange arrRange = CFRangeMake(0, CFArrayGetCount(arr));
79 CFIndex itemIdx = CFArrayBSearchValues(arr, arrRange, target, (CFComparatorFunction)CFStringCompare, NULL);
80 return itemIdx < arrRange.length && CFEqual(CFArrayGetValueAtIndex(arr, itemIdx), target);
81 }
82
83 // 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 Windows
84 // This is here to make sure it gets updated when _CFGetPlatformName does
85 #define _CFBundleNumberOfPlatforms 7
86 static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
87 static const char *_CFBundleSupportedPlatformStrings[_CFBundleNumberOfPlatforms] = { "iphoneos", "macos", "windows", "linux", "freebsd", "solaris", "hpux" };
88
89 // This is here to make sure it gets updated when _CFGetProductName does
90 #define _CFBundleNumberOfProducts 3
91 static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { NULL, NULL, NULL };
92 static const char *_CFBundleSupportedProductStrings[_CFBundleNumberOfProducts] = { "iphone", "ipod", "ipad" };
93
94 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3
95 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { NULL, NULL, NULL };
96 static const char *_CFBundleSupportediPhoneOSPlatformProductStrings[_CFBundleNumberOfiPhoneOSPlatformProducts] = { "iphone", "ipod", "ipad" };
97
98 void _CFBundleResourcesInitialize() {
99 for (unsigned int i = 0; i < _CFBundleNumberOfPlatforms; i++) _CFBundleSupportedPlatforms[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedPlatformStrings[i], kCFStringEncodingUTF8);
100
101 for (unsigned int i = 0; i < _CFBundleNumberOfProducts; i++) _CFBundleSupportedProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedProductStrings[i], kCFStringEncodingUTF8);
102
103 for (unsigned int i = 0; i < _CFBundleNumberOfiPhoneOSPlatformProducts; i++) _CFBundleSupportediPhoneOSPlatformProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportediPhoneOSPlatformProductStrings[i], kCFStringEncodingUTF8);
104 }
105
106 static CFStringRef platform = NULL;
107
108 void _CFSetProductName(CFStringRef str) {
109 if (str) CFRetain(str);
110 platform = str;
111 // Note that the previous value is leaked, which is fine normally
112 // because the initial values would tend to be the constant strings
113 // below. That is required for thread-safety value due to the Get
114 // function [not being Copy]. It is also fine because people
115 // shouldn't be screwing around with this value casually.
116 }
117
118 CFStringRef _CFGetProductName(void) {
119 #if DEPLOYMENT_TARGET_EMBEDDED
120 if (!platform) {
121 char buffer[256];
122 memset(buffer, 0, sizeof(buffer));
123 size_t buflen = sizeof(buffer);
124 int ret = sysctlbyname("hw.machine", buffer, &buflen, NULL, 0);
125 if (0 == ret || (-1 == ret && ENOMEM == errno)) {
126 if (6 <= buflen && 0 == memcmp(buffer, "iPhone", 6)) {
127 platform = CFSTR("iphone");
128 } else if (4 <= buflen && 0 == memcmp(buffer, "iPod", 4)) {
129 platform = CFSTR("ipod");
130 } else if (4 <= buflen && 0 == memcmp(buffer, "iPad", 4)) {
131 platform = CFSTR("ipad");
132 } else {
133 const char *env = __CFgetenv("IPHONE_SIMULATOR_DEVICE");
134 if (env) {
135 if (0 == strcmp(env, "iPhone")) {
136 platform = CFSTR("iphone");
137 } else if (0 == strcmp(env, "iPad")) {
138 platform = CFSTR("ipad");
139 } else {
140 // fallback, unrecognized IPHONE_SIMULATOR_DEVICE
141 }
142 } else {
143 // fallback, unrecognized hw.machine and no IPHONE_SIMULATOR_DEVICE
144 }
145 }
146 }
147 if (!platform) platform = CFSTR("iphone"); // fallback
148 }
149 return platform;
150 #endif
151 return CFSTR("");
152 }
153
154 // All new-style bundles will have these extensions.
155 __private_extern__ CFStringRef _CFGetPlatformName(void) {
156 #if DEPLOYMENT_TARGET_MACOSX
157 return _CFBundleMacOSXPlatformName;
158 #elif DEPLOYMENT_TARGET_EMBEDDED
159 return _CFBundleiPhoneOSPlatformName;
160 #elif DEPLOYMENT_TARGET_WINDOWS
161 return _CFBundleWindowsPlatformName;
162 #elif DEPLOYMENT_TARGET_SOLARIS
163 return _CFBundleSolarisPlatformName;
164 #elif DEPLOYMENT_TARGET_HPUX
165 return _CFBundleHPUXPlatformName;
166 #elif DEPLOYMENT_TARGET_LINUX
167 return _CFBundleLinuxPlatformName;
168 #elif DEPLOYMENT_TARGET_FREEBSD
169 return _CFBundleFreeBSDPlatformName;
170 #else
171 #error Unknown or unspecified DEPLOYMENT_TARGET
172 #endif
173 }
174
175 __private_extern__ CFStringRef _CFGetAlternatePlatformName(void) {
176 #if DEPLOYMENT_TARGET_MACOSX
177 return _CFBundleAlternateMacOSXPlatformName;
178 #elif DEPLOYMENT_TARGET_EMBEDDED
179 return _CFBundleMacOSXPlatformName;
180 #elif DEPLOYMENT_TARGET_WINDOWS
181 return CFSTR("");
182 #else
183 #error Unknown or unspecified DEPLOYMENT_TARGET
184 #endif
185 }
186
187 static CFSpinLock_t CFBundleResourceGlobalDataLock = CFSpinLockInit;
188 static UniChar *_AppSupportUniChars1 = NULL;
189 static CFIndex _AppSupportLen1 = 0;
190 static UniChar *_AppSupportUniChars2 = NULL;
191 static CFIndex _AppSupportLen2 = 0;
192 static UniChar *_ResourcesUniChars = NULL;
193 static CFIndex _ResourcesLen = 0;
194 static UniChar *_PlatformUniChars = NULL;
195 static CFIndex _PlatformLen = 0;
196 static UniChar *_AlternatePlatformUniChars = NULL;
197 static CFIndex _AlternatePlatformLen = 0;
198 static UniChar *_LprojUniChars = NULL;
199 static CFIndex _LprojLen = 0;
200 static UniChar *_GlobalResourcesUniChars = NULL;
201 static CFIndex _GlobalResourcesLen = 0;
202 static UniChar *_InfoExtensionUniChars = NULL;
203 static CFIndex _InfoExtensionLen = 0;
204
205 static UniChar _ResourceSuffix3[32];
206 static CFIndex _ResourceSuffix3Len = 0;
207 static UniChar _ResourceSuffix2[16];
208 static CFIndex _ResourceSuffix2Len = 0;
209 static UniChar _ResourceSuffix1[16];
210 static CFIndex _ResourceSuffix1Len = 0;
211
212 static void _CFBundleInitStaticUniCharBuffers(void) {
213 CFStringRef appSupportStr1 = _CFBundleSupportFilesDirectoryName1;
214 CFStringRef appSupportStr2 = _CFBundleSupportFilesDirectoryName2;
215 CFStringRef resourcesStr = _CFBundleResourcesDirectoryName;
216 CFStringRef platformStr = _CFGetPlatformName();
217 CFStringRef alternatePlatformStr = _CFGetAlternatePlatformName();
218 CFStringRef lprojStr = _CFBundleLprojExtension;
219 CFStringRef globalResourcesStr = _CFBundleNonLocalizedResourcesDirectoryName;
220 CFStringRef infoExtensionStr = _CFBundleInfoExtension;
221
222 _AppSupportLen1 = CFStringGetLength(appSupportStr1);
223 _AppSupportLen2 = CFStringGetLength(appSupportStr2);
224 _ResourcesLen = CFStringGetLength(resourcesStr);
225 _PlatformLen = CFStringGetLength(platformStr);
226 _AlternatePlatformLen = CFStringGetLength(alternatePlatformStr);
227 _LprojLen = CFStringGetLength(lprojStr);
228 _GlobalResourcesLen = CFStringGetLength(globalResourcesStr);
229 _InfoExtensionLen = CFStringGetLength(infoExtensionStr);
230
231 _AppSupportUniChars1 = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (_AppSupportLen1 + _AppSupportLen2 + _ResourcesLen + _PlatformLen + _AlternatePlatformLen + _LprojLen + _GlobalResourcesLen + _InfoExtensionLen), 0);
232 _AppSupportUniChars2 = _AppSupportUniChars1 + _AppSupportLen1;
233 _ResourcesUniChars = _AppSupportUniChars2 + _AppSupportLen2;
234 _PlatformUniChars = _ResourcesUniChars + _ResourcesLen;
235 _AlternatePlatformUniChars = _PlatformUniChars + _PlatformLen;
236 _LprojUniChars = _AlternatePlatformUniChars + _AlternatePlatformLen;
237 _GlobalResourcesUniChars = _LprojUniChars + _LprojLen;
238 _InfoExtensionUniChars = _GlobalResourcesUniChars + _GlobalResourcesLen;
239
240 if (_AppSupportLen1 > 0) CFStringGetCharacters(appSupportStr1, CFRangeMake(0, _AppSupportLen1), _AppSupportUniChars1);
241 if (_AppSupportLen2 > 0) CFStringGetCharacters(appSupportStr2, CFRangeMake(0, _AppSupportLen2), _AppSupportUniChars2);
242 if (_ResourcesLen > 0) CFStringGetCharacters(resourcesStr, CFRangeMake(0, _ResourcesLen), _ResourcesUniChars);
243 if (_PlatformLen > 0) CFStringGetCharacters(platformStr, CFRangeMake(0, _PlatformLen), _PlatformUniChars);
244 if (_AlternatePlatformLen > 0) CFStringGetCharacters(alternatePlatformStr, CFRangeMake(0, _AlternatePlatformLen), _AlternatePlatformUniChars);
245 if (_LprojLen > 0) CFStringGetCharacters(lprojStr, CFRangeMake(0, _LprojLen), _LprojUniChars);
246 if (_GlobalResourcesLen > 0) CFStringGetCharacters(globalResourcesStr, CFRangeMake(0, _GlobalResourcesLen), _GlobalResourcesUniChars);
247 if (_InfoExtensionLen > 0) CFStringGetCharacters(infoExtensionStr, CFRangeMake(0, _InfoExtensionLen), _InfoExtensionUniChars);
248
249 _ResourceSuffix1Len = CFStringGetLength(platformStr);
250 if (_ResourceSuffix1Len > 0) _ResourceSuffix1[0] = '-';
251 if (_ResourceSuffix1Len > 0) CFStringGetCharacters(platformStr, CFRangeMake(0, _ResourceSuffix1Len), _ResourceSuffix1 + 1);
252 if (_ResourceSuffix1Len > 0) _ResourceSuffix1Len++;
253 CFStringRef productStr = _CFGetProductName();
254 if (CFEqual(productStr, CFSTR("ipod"))) { // For now, for resource lookups, hide ipod distinction and make it look for iphone resources
255 productStr = CFSTR("iphone");
256 }
257 _ResourceSuffix2Len = CFStringGetLength(productStr);
258 if (_ResourceSuffix2Len > 0) _ResourceSuffix2[0] = '~';
259 if (_ResourceSuffix2Len > 0) CFStringGetCharacters(productStr, CFRangeMake(0, _ResourceSuffix2Len), _ResourceSuffix2 + 1);
260 if (_ResourceSuffix2Len > 0) _ResourceSuffix2Len++;
261 if (_ResourceSuffix1Len > 1 && _ResourceSuffix2Len > 1) {
262 _ResourceSuffix3Len = _ResourceSuffix1Len + _ResourceSuffix2Len;
263 memmove(_ResourceSuffix3, _ResourceSuffix1, sizeof(UniChar) * _ResourceSuffix1Len);
264 memmove(_ResourceSuffix3 + _ResourceSuffix1Len, _ResourceSuffix2, sizeof(UniChar) * _ResourceSuffix2Len);
265 }
266 }
267
268 CF_INLINE void _CFEnsureStaticBuffersInited(void) {
269 __CFSpinLock(&CFBundleResourceGlobalDataLock);
270 if (!_AppSupportUniChars1) _CFBundleInitStaticUniCharBuffers();
271 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
272 }
273
274 #if READ_DIRECTORIES
275
276 static CFMutableDictionaryRef contentsCache = NULL;
277 static CFMutableDictionaryRef directoryContentsCache = NULL;
278 static CFMutableDictionaryRef unknownContentsCache = NULL;
279
280 typedef enum {
281 _CFBundleAllContents = 0,
282 _CFBundleDirectoryContents = 1,
283 _CFBundleUnknownContents = 2
284 } _CFBundleDirectoryContentsType;
285
286 extern void _CFArraySortValues(CFMutableArrayRef array, CFComparatorFunction comparator, void *context);
287
288 static CFArrayRef _CFBundleCopySortedDirectoryContentsAtPath(CFStringRef path, _CFBundleDirectoryContentsType contentsType) {
289 CFArrayRef result = NULL;
290
291 __CFSpinLock(&CFBundleResourceGlobalDataLock);
292 if (contentsType == _CFBundleUnknownContents) {
293 if (unknownContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(unknownContentsCache, path);
294 } else if (contentsType == _CFBundleDirectoryContents) {
295 if (directoryContentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(directoryContentsCache, path);
296 } else {
297 if (contentsCache) result = (CFMutableArrayRef)CFDictionaryGetValue(contentsCache, path);
298 }
299 if (result) CFRetain(result);
300 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
301
302 if (!result) {
303 Boolean tryToOpen = false, allDots = true;
304 char cpathBuff[CFMaxPathSize];
305 CFIndex cpathLen = 0, idx, lastSlashIdx = 0;
306 DIR *dirp = NULL;
307 struct dirent *dent;
308 CFMutableArrayRef contents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), directoryContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks), unknownContents = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
309 CFStringRef dirName, name;
310
311 cpathBuff[0] = '\0';
312 if (CFStringGetFileSystemRepresentation(path, cpathBuff, CFMaxPathSize)) {
313 tryToOpen = true;
314 cpathLen = strlen(cpathBuff);
315
316 // First see whether we already know that the directory doesn't exist
317 for (idx = cpathLen; lastSlashIdx == 0 && idx-- > 0;) {
318 if (cpathBuff[idx] == '/') lastSlashIdx = idx;
319 else if (cpathBuff[idx] != '.') allDots = false;
320 }
321 if (lastSlashIdx > 0 && lastSlashIdx + 1 < cpathLen && !allDots) {
322 cpathBuff[lastSlashIdx] = '\0';
323 dirName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff);
324 if (dirName) {
325 name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, cpathBuff + lastSlashIdx + 1);
326 if (name) {
327 // ??? we might like to use directoryContentsCache rather than contentsCache here, but we cannot unless we resolve DT_LNKs below
328 CFArrayRef dirDirContents = NULL;
329
330 __CFSpinLock(&CFBundleResourceGlobalDataLock);
331 if (contentsCache) dirDirContents = (CFArrayRef)CFDictionaryGetValue(contentsCache, dirName);
332 if (dirDirContents) {
333 Boolean foundIt = false;
334 CFIndex dirDirIdx, dirDirLength = CFArrayGetCount(dirDirContents);
335 for (dirDirIdx = 0; !foundIt && dirDirIdx < dirDirLength; dirDirIdx++) if (kCFCompareEqualTo == CFStringCompare(name, CFArrayGetValueAtIndex(dirDirContents, dirDirIdx), kCFCompareCaseInsensitive)) foundIt = true;
336 if (!foundIt) tryToOpen = false;
337 }
338 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
339 CFRelease(name);
340 }
341 CFRelease(dirName);
342 }
343 cpathBuff[lastSlashIdx] = '/';
344 }
345 }
346 if (tryToOpen && (dirp = opendir(cpathBuff))) {
347 while ((dent = readdir(dirp))) {
348 CFIndex nameLen = dent->d_namlen;
349 if (0 == nameLen || 0 == dent->d_fileno || ('.' == dent->d_name[0] && (1 == nameLen || (2 == nameLen && '.' == dent->d_name[1]) || '_' == dent->d_name[1]))) continue;
350 name = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, dent->d_name);
351 if (name) {
352 // ??? should we follow links for DT_LNK? unless we do, results are approximate, but for performance reasons we do not
353 // ??? likewise for DT_UNKNOWN
354 // ??? the utility of distinguishing directories from other contents is somewhat doubtful anyway
355 CFArrayAppendValue(contents, name);
356 if (dent->d_type == DT_DIR) {
357 CFArrayAppendValue(directoryContents, name);
358 } else if (dent->d_type == DT_UNKNOWN) {
359 CFArrayAppendValue(unknownContents, name);
360 }
361 CFRelease(name);
362 }
363 }
364 (void)closedir(dirp);
365 }
366
367 _CFArraySortValues(contents, (CFComparatorFunction)CFStringCompare, NULL);
368 _CFArraySortValues(directoryContents, (CFComparatorFunction)CFStringCompare, NULL);
369 _CFArraySortValues(unknownContents, (CFComparatorFunction)CFStringCompare, NULL);
370
371 __CFSpinLock(&CFBundleResourceGlobalDataLock);
372 if (!contentsCache) contentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
373 if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(contentsCache)) CFDictionaryRemoveAllValues(contentsCache);
374 CFDictionaryAddValue(contentsCache, path, contents);
375
376 if (!directoryContentsCache) directoryContentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
377 if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(directoryContentsCache)) CFDictionaryRemoveAllValues(directoryContentsCache);
378 CFDictionaryAddValue(directoryContentsCache, path, directoryContents);
379
380 if (!unknownContentsCache) unknownContentsCache = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, READ_DIRECTORIES_CACHE_CAPACITY, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
381 if (READ_DIRECTORIES_CACHE_CAPACITY <= CFDictionaryGetCount(unknownContentsCache)) CFDictionaryRemoveAllValues(unknownContentsCache);
382 CFDictionaryAddValue(unknownContentsCache, path, unknownContents);
383
384 if (contentsType == _CFBundleUnknownContents) {
385 result = CFRetain(unknownContents);
386 } else if (contentsType == _CFBundleDirectoryContents) {
387 result = CFRetain(directoryContents);
388 } else {
389 result = CFRetain(contents);
390 }
391
392 CFRelease(contents);
393 CFRelease(directoryContents);
394 CFRelease(unknownContents);
395 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
396 }
397 return result;
398 }
399
400 static void _CFBundleFlushContentsCaches(void) {
401 __CFSpinLock(&CFBundleResourceGlobalDataLock);
402 if (contentsCache) CFDictionaryRemoveAllValues(contentsCache);
403 if (directoryContentsCache) CFDictionaryRemoveAllValues(directoryContentsCache);
404 if (unknownContentsCache) CFDictionaryRemoveAllValues(unknownContentsCache);
405 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
406 }
407
408 static void _CFBundleFlushContentsCacheForPath(CFMutableDictionaryRef cache, CFStringRef path) {
409 CFStringRef keys[READ_DIRECTORIES_CACHE_CAPACITY];
410 unsigned i, count = CFDictionaryGetCount(cache);
411 if (count <= READ_DIRECTORIES_CACHE_CAPACITY) {
412 CFDictionaryGetKeysAndValues(cache, (const void **)keys, NULL);
413 for (i = 0; i < count; i++) {
414 if (CFStringFindWithOptions(keys[i], path, CFRangeMake(0, CFStringGetLength(keys[i])), kCFCompareAnchored|kCFCompareCaseInsensitive, NULL)) CFDictionaryRemoveValue(cache, keys[i]);
415 }
416 }
417 }
418
419 static void _CFBundleFlushContentsCachesForPath(CFStringRef path) {
420 __CFSpinLock(&CFBundleResourceGlobalDataLock);
421 if (contentsCache) _CFBundleFlushContentsCacheForPath(contentsCache, path);
422 if (directoryContentsCache) _CFBundleFlushContentsCacheForPath(directoryContentsCache, path);
423 if (unknownContentsCache) _CFBundleFlushContentsCacheForPath(unknownContentsCache, path);
424 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
425 }
426
427 #endif /* READ_DIRECTORIES */
428
429 CF_EXPORT void _CFBundleFlushCachesForURL(CFURLRef url) {
430 #if READ_DIRECTORIES
431 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url);
432 CFStringRef path = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
433 _CFBundleFlushContentsCachesForPath(path);
434 CFRelease(path);
435 CFRelease(absoluteURL);
436 #endif /* READ_DIRECTORIES */
437 }
438
439 CF_EXPORT void _CFBundleFlushCaches(void) {
440 #if READ_DIRECTORIES
441 _CFBundleFlushContentsCaches();
442 #endif /* READ_DIRECTORIES */
443 }
444
445 static inline Boolean _CFIsResourceCommon(char *path, Boolean *isDir) {
446 Boolean exists;
447 SInt32 mode;
448 if (_CFGetPathProperties(kCFAllocatorSystemDefault, path, &exists, &mode, NULL, NULL, NULL, NULL) == 0) {
449 if (isDir) *isDir = ((exists && ((mode & S_IFMT) == S_IFDIR)) ? true : false);
450 return (exists && (mode & 0444));
451 }
452 return false;
453 }
454
455 __private_extern__ Boolean _CFIsResourceAtURL(CFURLRef url, Boolean *isDir) {
456 char path[CFMaxPathSize];
457 if (!CFURLGetFileSystemRepresentation(url, true, (uint8_t *)path, CFMaxPathLength)) return false;
458
459 return _CFIsResourceCommon(path, isDir);
460 }
461
462 __private_extern__ Boolean _CFIsResourceAtPath(CFStringRef path, Boolean *isDir) {
463 char pathBuf[CFMaxPathSize];
464 if (!CFStringGetFileSystemRepresentation(path, pathBuf, CFMaxPathSize)) return false;
465
466 return _CFIsResourceCommon(pathBuf, isDir);
467 }
468
469 #if READ_DIRECTORIES
470 static CFArrayRef _CFCopyTypesForSearchBundleDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, UniChar *nameUniChars, CFIndex nameLen, CFArrayRef resTypes, CFMutableStringRef cheapStr, CFMutableStringRef tmpString, uint8_t version) {
471 CFMutableArrayRef result = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
472 CFArrayRef contents;
473 CFRange contentsRange, resultRange = CFRangeMake(0, 0);
474 CFIndex dirPathLen = pathLen, numResTypes = CFArrayGetCount(resTypes), i, j;
475
476 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
477 CFStringReplaceAll(cheapStr, tmpString);
478 //fprintf(stderr, "looking in ");CFShow(cheapStr);
479 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
480 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
481
482 CFStringSetExternalCharactersNoCopy(tmpString, nameUniChars, nameLen, nameLen);
483 CFStringReplaceAll(cheapStr, tmpString);
484 for (i = 0; i < contentsRange.length; i++) {
485 CFStringRef content = CFArrayGetValueAtIndex(contents, i);
486 if (CFStringHasPrefix(content, cheapStr)) {
487 //fprintf(stderr, "found ");CFShow(content);
488 for (j = 0; j < numResTypes; j++) {
489 CFStringRef resType = CFArrayGetValueAtIndex(resTypes, j);
490 if (!CFArrayContainsValue(result, resultRange, resType) && CFStringHasSuffix(content, resType)) {
491 CFArrayAppendValue(result, resType);
492 resultRange.length = CFArrayGetCount(result);
493 }
494 }
495 }
496 }
497 //fprintf(stderr, "result ");CFShow(result);
498 CFRelease(contents);
499 return result;
500 }
501 #endif /* READ_DIRECTORIES */
502
503 #if DEPLOYMENT_TARGET_EMBEDDED
504 static 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) {
505 // pathUniChars is the full path to the directory we are searching.
506 // nameUniChars is what we are looking for.
507 // typeUniChars is the type we are looking for.
508 // platformUniChars is the platform name.
509 // cheapStr is available for our use for whatever we want.
510 // URLs for found resources get added to result.
511
512 Boolean appendSucceeded = true;
513 if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen);
514 if (! appendSucceeded) return;
515 CFIndex savedPathLen = pathLen;
516
517 // Try in order:
518 // NAME-PLATFORM~PRODUCT.TYPE (disabled for now)
519 // NAME~PRODUCT.TYPE
520 // NAME-PLATFORM.TYPE (disabled for now)
521 // NAME.TYPE
522
523 #if 0
524 appendSucceeded = (pathLen + _ResourceSuffix3Len < CFMaxPathSize);
525 if (appendSucceeded) {
526 memmove(pathUniChars + pathLen, _ResourceSuffix3, _ResourceSuffix3Len * sizeof(UniChar));
527 pathLen += _ResourceSuffix3Len;
528 }
529 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
530 if (appendSucceeded) {
531 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
532 CFStringReplaceAll(cheapStr, tmpString);
533 Boolean Found = false, IsDir = false;
534 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
535 if (Found) {
536 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
537 CFArrayAppendValue(result, url);
538 CFRelease(url);
539 return;
540 }
541 }
542 #endif
543
544 pathLen = savedPathLen;
545 appendSucceeded = (pathLen + _ResourceSuffix2Len < CFMaxPathSize);
546 if (appendSucceeded) {
547 memmove(pathUniChars + pathLen, _ResourceSuffix2, _ResourceSuffix2Len * sizeof(UniChar));
548 pathLen += _ResourceSuffix2Len;
549 }
550 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
551 if (appendSucceeded) {
552 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
553 CFStringReplaceAll(cheapStr, tmpString);
554 Boolean Found = false, IsDir = false;
555 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
556 if (Found) {
557 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
558 CFArrayAppendValue(result, url);
559 CFRelease(url);
560 return;
561 }
562 }
563
564 #if 0
565 pathLen = savedPathLen;
566 appendSucceeded = (pathLen + _ResourceSuffix1Len < CFMaxPathSize);
567 if (appendSucceeded) {
568 memmove(pathUniChars + pathLen, _ResourceSuffix1, _ResourceSuffix1Len * sizeof(UniChar));
569 pathLen += _ResourceSuffix1Len;
570 }
571 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
572 if (appendSucceeded) {
573 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
574 CFStringReplaceAll(cheapStr, tmpString);
575 Boolean Found = false, IsDir = false;
576 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
577 if (Found) {
578 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
579 CFArrayAppendValue(result, url);
580 CFRelease(url);
581 return;
582 }
583 }
584 #endif
585
586 pathLen = savedPathLen;
587 appendSucceeded = true;
588 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
589 if (appendSucceeded) {
590 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
591 CFStringReplaceAll(cheapStr, tmpString);
592 Boolean Found = false, IsDir = false;
593 Found = _CFIsResourceAtPath(cheapStr, &IsDir);
594 if (Found) {
595 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, IsDir);
596 CFArrayAppendValue(result, url);
597 CFRelease(url);
598 return;
599 }
600 }
601 }
602 #endif
603
604 static 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) {
605
606 #if DEPLOYMENT_TARGET_EMBEDDED
607 _CFSearchBundleDirectory2(alloc, result, pathUniChars, pathLen, nameUniChars, nameLen, typeUniChars, typeLen, cheapStr, tmpString, version);
608 #else
609 // pathUniChars is the full path to the directory we are searching.
610 // nameUniChars is what we are looking for.
611 // typeUniChars is the type we are looking for.
612 // platformUniChars is the platform name.
613 // cheapStr is available for our use for whatever we want.
614 // URLs for found resources get added to result.
615 CFIndex savedPathLen;
616 Boolean appendSucceeded = true, platformGenericFound = false, platformSpecificFound = false, platformGenericIsDir = false, platformSpecificIsDir = false;
617 #if READ_DIRECTORIES
618 Boolean platformGenericIsUnknown = false, platformSpecificIsUnknown = false;
619 #endif
620 CFStringRef platformGenericStr = NULL;
621
622 #if READ_DIRECTORIES
623 CFIndex dirPathLen = pathLen;
624 CFArrayRef contents, directoryContents, unknownContents;
625 CFRange contentsRange, directoryContentsRange, unknownContentsRange;
626
627 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
628 CFStringReplaceAll(cheapStr, tmpString);
629 //fprintf(stderr, "looking in ");CFShow(cheapStr);
630 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
631 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
632 directoryContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents);
633 directoryContentsRange = CFRangeMake(0, CFArrayGetCount(directoryContents));
634 unknownContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents);
635 unknownContentsRange = CFRangeMake(0, CFArrayGetCount(unknownContents));
636 #endif /* READ_DIRECTORIES */
637
638 if (nameLen > 0) appendSucceeded = _CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, nameUniChars, nameLen);
639 savedPathLen = pathLen;
640 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
641 if (appendSucceeded) {
642 #if READ_DIRECTORIES
643 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1);
644 CFStringReplaceAll(cheapStr, tmpString);
645 platformGenericFound = _CFBundleSortedArrayContains(contents, cheapStr);
646 platformGenericIsDir = _CFBundleSortedArrayContains(directoryContents, cheapStr);
647 platformGenericIsUnknown = _CFBundleSortedArrayContains(unknownContents, cheapStr);
648 //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformGenericFound) fprintf(stderr, "found it\n"); if (platformGenericIsDir) fprintf(stderr, "a directory\n");
649 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
650 CFStringReplaceAll(cheapStr, tmpString);
651 if (platformGenericFound && platformGenericIsUnknown) {
652 (void)_CFIsResourceAtPath(cheapStr, &platformGenericIsDir);
653 //if (platformGenericIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n");
654 }
655 #else /* READ_DIRECTORIES */
656 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
657 CFStringReplaceAll(cheapStr, tmpString);
658 platformGenericFound = _CFIsResourceAtPath(cheapStr, &platformGenericIsDir);
659 #endif /* READ_DIRECTORIES */
660 }
661
662 // Check for platform specific.
663 if (platformGenericFound) {
664 platformGenericStr = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, cheapStr);
665 if (!platformSpecificFound && (_PlatformLen > 0)) {
666 pathLen = savedPathLen;
667 pathUniChars[pathLen++] = (UniChar)'-';
668 memmove(pathUniChars + pathLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
669 pathLen += _PlatformLen;
670 if (appendSucceeded && typeLen > 0) appendSucceeded = _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, typeUniChars, typeLen);
671 if (appendSucceeded) {
672 #if READ_DIRECTORIES
673 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen + 1, pathLen - dirPathLen - 1, pathLen - dirPathLen - 1);
674 CFStringReplaceAll(cheapStr, tmpString);
675 platformSpecificFound = _CFBundleSortedArrayContains(contents, cheapStr);
676 platformSpecificIsDir = _CFBundleSortedArrayContains(directoryContents, cheapStr);
677 platformSpecificIsUnknown = _CFBundleSortedArrayContains(unknownContents, cheapStr);
678 //fprintf(stderr, "looking for ");CFShow(cheapStr);if (platformSpecificFound) fprintf(stderr, "found it\n"); if (platformSpecificIsDir) fprintf(stderr, "a directory\n");
679 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
680 CFStringReplaceAll(cheapStr, tmpString);
681 if (platformSpecificFound && platformSpecificIsUnknown) {
682 (void)_CFIsResourceAtPath(cheapStr, &platformSpecificIsDir);
683 //if (platformSpecificIsDir) fprintf(stderr, "a directory after all\n"); else fprintf(stderr, "not a directory after all\n");
684 }
685 #else /* READ_DIRECTORIES */
686 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
687 CFStringReplaceAll(cheapStr, tmpString);
688 platformSpecificFound = _CFIsResourceAtPath(cheapStr, &platformSpecificIsDir);
689 #endif /* READ_DIRECTORIES */
690 }
691 }
692 }
693 if (platformSpecificFound) {
694 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, platformSpecificIsDir);
695 CFArrayAppendValue(result, url);
696 CFRelease(url);
697 } else if (platformGenericFound) {
698 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, platformGenericStr ? platformGenericStr : cheapStr, PLATFORM_PATH_STYLE, platformGenericIsDir);
699 CFArrayAppendValue(result, url);
700 CFRelease(url);
701 }
702 if (platformGenericStr) CFRelease(platformGenericStr);
703 #if READ_DIRECTORIES
704 CFRelease(contents);
705 CFRelease(directoryContents);
706 CFRelease(unknownContents);
707 #endif /* READ_DIRECTORIES */
708 #endif
709 }
710
711 #if READ_DIRECTORIES
712 static 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) {
713
714 // pathUniChars is the full path to the directory we are searching.
715 // platformUniChars is the platform name.
716 // predicate is a block that evaluates a given filename to see if it's a match.
717 // cheapStr is available for our use for whatever we want.
718 // URLs for found resources get added to result.
719
720 // get the contents of the directory
721 CFArrayRef contents, directoryContents, unknownContents;
722 CFRange contentsRange;
723
724 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen, dirPathLen);
725 CFStringReplaceAll(cheapStr, tmpString);
726
727 if (!_CFAppendTrailingPathSlash(pathUniChars, &dirPathLen, CFMaxPathSize)) {
728 return;
729 }
730
731 //fprintf(stderr, "looking in ");CFShow(cheapStr);
732 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
733 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
734 directoryContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleDirectoryContents);
735 unknownContents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleUnknownContents);
736
737 // scan directory contents for matches against predicate
738 for (int i = 0; i < contentsRange.length; i++) {
739 CFStringRef candidateFilename = CFArrayGetValueAtIndex(contents, i);
740 if (predicate(candidateFilename, stopLooking)) {
741 // we want this resource, though possibly a platform specific version of it
742 // unpack candidateFilename string into pathUniChars after verifying that we have enough space in the buffer
743 CFIndex candidateFilenameLength = CFStringGetLength(candidateFilename);
744 if ((dirPathLen + candidateFilenameLength < CFMaxPathSize)) {
745 CFStringGetCharacters(candidateFilename, CFRangeMake(0, candidateFilenameLength), pathUniChars + dirPathLen);
746
747 // is there a platform specific version available? if so update pathUniChars to contain it and candidateFilenameLength to describe its length.
748 static const int platformSeparatorLen = 1; // the length of '-', as appears in foo-macos.tiff. sugar to make the following easier to read.
749 if (_PlatformLen && (dirPathLen + candidateFilenameLength + platformSeparatorLen + _PlatformLen < CFMaxPathSize)) {
750 CFIndex candidateFilenameWithoutExtensionLen = _CFLengthAfterDeletingPathExtension(pathUniChars + dirPathLen, candidateFilenameLength);
751 CFIndex extensionLen = candidateFilenameLength - candidateFilenameWithoutExtensionLen;
752 // shift the extension over to make space for the platform
753 memmove(pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen + platformSeparatorLen + _PlatformLen, pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen, extensionLen * sizeof(UniChar));
754 // write the platform into the middle of the string
755 pathUniChars[dirPathLen + candidateFilenameWithoutExtensionLen] = (UniChar)'-';
756 memcpy(pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen + platformSeparatorLen, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
757 // pack it up as a CFStringRef
758 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + dirPathLen, candidateFilenameLength + platformSeparatorLen + _PlatformLen, candidateFilenameLength + _PlatformLen);
759 CFStringReplaceAll(cheapStr, tmpString);
760 // is the platform specialized version there?
761 if (_CFBundleSortedArrayContains(contents, cheapStr)) {
762 // woo. update the candidateFilenameLength. we'll update the candidateFilename too for consistency, but we don't actually use it again.
763 // the pathUniChars now contains the full path to the file
764 candidateFilename = cheapStr;
765 candidateFilenameLength = candidateFilenameLength + _PlatformLen + platformSeparatorLen;
766 } else {
767 // nope, no platform specific resource. Put the pathUniChars back how they were before, without the platform.
768 memmove(pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen, pathUniChars + dirPathLen + candidateFilenameWithoutExtensionLen + platformSeparatorLen + _PlatformLen, extensionLen * sizeof(UniChar));
769 }
770 }
771
772 // get the full path into cheapStr
773 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, dirPathLen + candidateFilenameLength, dirPathLen + candidateFilenameLength);
774 CFStringReplaceAll(cheapStr, tmpString);
775
776 // is the resource a directory? we need to know so that we can avoid file access when making a URL.
777 Boolean isDir = 0;
778 if (_CFBundleSortedArrayContains(directoryContents, cheapStr)) {
779 isDir = 1;
780 } else if (_CFBundleSortedArrayContains(unknownContents, cheapStr)) {
781 _CFIsResourceAtPath(cheapStr, &isDir);
782 }
783
784 CFURLRef url = CFURLCreateWithFileSystemPath(alloc, cheapStr, PLATFORM_PATH_STYLE, isDir);
785 CFArrayAppendValue(result, url);
786 CFRelease(url);
787 }
788 }
789
790 if (*stopLooking) break;
791 }
792
793 CFRelease(contents);
794 CFRelease(directoryContents);
795 CFRelease(unknownContents);
796 }
797 #endif
798
799 static 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) {
800 if (predicate) {
801 #if READ_DIRECTORIES
802 _CFSearchBundleDirectoryWithPredicate(alloc, result, workingUniChars, workingLen, predicate, cheapStr, tmpString, stopLooking, version);
803 return;
804 #else
805 CFLog(kCFLogLevelCritical, CFSTR("_CFFindBundleResourcesInRawDir: predicate blocks are not supported on this platform"));
806 HALT;
807 #endif
808 }
809 if (nameLen > 0) {
810 // If we have a resName, just call the search API. We may have to loop over the resTypes.
811 if (!resTypes) {
812 _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, NULL, 0, cheapStr, tmpString, version);
813 } else {
814 CFArrayRef subResTypes = resTypes;
815 Boolean releaseSubResTypes = false;
816 CFIndex i, c = CFArrayGetCount(resTypes);
817 #if READ_DIRECTORIES
818 if (c > 2) {
819 // this is an optimization we employ when searching for large numbers of types, if the directory contents are available
820 // we scan the directory contents and restrict the list of resTypes to the types that might actually occur with the specified name
821 subResTypes = _CFCopyTypesForSearchBundleDirectory(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, cheapStr, tmpString, version);
822 c = CFArrayGetCount(subResTypes);
823 releaseSubResTypes = true;
824 }
825 #endif /* READ_DIRECTORIES */
826 for (i = 0; i < c; i++) {
827 CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(subResTypes, i);
828 CFIndex typeLen = CFStringGetLength(curType);
829 STACK_BUFFER_DECL(UniChar, typeChars, typeLen);
830 CFStringGetCharacters(curType, CFRangeMake(0, typeLen), typeChars);
831 _CFSearchBundleDirectory(alloc, result, workingUniChars, workingLen, nameUniChars, nameLen, typeChars, typeLen, cheapStr, tmpString, version);
832 if (limit <= CFArrayGetCount(result)) break;
833 }
834 if (releaseSubResTypes) CFRelease(subResTypes);
835 }
836 } else {
837 // If we have no resName, do it by hand. We may have to loop over the resTypes.
838 char cpathBuff[CFMaxPathSize];
839 CFIndex cpathLen;
840 CFMutableArrayRef children;
841
842 CFStringSetExternalCharactersNoCopy(tmpString, workingUniChars, workingLen, workingLen);
843 if (!CFStringGetFileSystemRepresentation(tmpString, cpathBuff, CFMaxPathSize)) return;
844 cpathLen = strlen(cpathBuff);
845
846 if (!resTypes) {
847 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
848 children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, NULL);
849 if (children) {
850 CFIndex childIndex, childCount = CFArrayGetCount(children);
851 for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex));
852 CFRelease(children);
853 }
854 } else {
855 CFIndex i, c = CFArrayGetCount(resTypes);
856 for (i = 0; i < c; i++) {
857 CFStringRef curType = (CFStringRef)CFArrayGetValueAtIndex(resTypes, i);
858
859 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
860 children = _CFContentsOfDirectory(alloc, cpathBuff, NULL, NULL, curType);
861 if (children) {
862 CFIndex childIndex, childCount = CFArrayGetCount(children);
863 for (childIndex = 0; childIndex < childCount; childIndex++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(children, childIndex));
864 CFRelease(children);
865 }
866 if (limit <= CFArrayGetCount(result)) break;
867 }
868 }
869 }
870 }
871
872 static 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) {
873 CFIndex savedWorkingLen = workingLen;
874 Boolean stopLooking = false; // for predicate based-queries, we set stopLooking instead of using a limit
875 // Look directly in the directory specified in workingUniChars. as if it is a Resources directory.
876 if (1 == version) {
877 // Add the non-localized resource directory.
878 Boolean appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _GlobalResourcesUniChars, _GlobalResourcesLen);
879 if (appendSucceeded && subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen);
880 if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
881 // Strip the non-localized resource directory.
882 workingLen = savedWorkingLen;
883 }
884 if (CFArrayGetCount(result) < limit && !stopLooking) {
885 Boolean appendSucceeded = true;
886 if (subDirLen > 0) appendSucceeded = _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen);
887 if (appendSucceeded) _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
888 }
889
890 // Now search the local resources.
891 workingLen = savedWorkingLen;
892 if (CFArrayGetCount(result) < limit && !stopLooking) {
893 CFIndex langCount = (searchLanguages ? CFArrayGetCount(searchLanguages) : 0);
894 // MF:??? OK to hard-wire this length?
895 UniChar curLangUniChars[255];
896 CFIndex numResults = CFArrayGetCount(result);
897
898 for (CFIndex langIndex = 0; langIndex < langCount; langIndex++) {
899 CFStringRef curLangStr = (CFStringRef)CFArrayGetValueAtIndex(searchLanguages, langIndex);
900 CFIndex curLangLen = CFStringGetLength(curLangStr);
901 if (curLangLen > 255) curLangLen = 255;
902 CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
903 savedWorkingLen = workingLen;
904 if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, curLangUniChars, curLangLen)) {
905 workingLen = savedWorkingLen;
906 continue;
907 }
908 if (!_CFAppendPathExtension(workingUniChars, &workingLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
909 workingLen = savedWorkingLen;
910 continue;
911 }
912 if (subDirLen > 0) {
913 if (!_CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, subDirUniChars, subDirLen)) {
914 workingLen = savedWorkingLen;
915 continue;
916 }
917 }
918 _CFFindBundleResourcesInRawDir(alloc, workingUniChars, workingLen, nameUniChars, nameLen, resTypes, limit, &stopLooking, predicate, version, cheapStr, tmpString, result);
919
920 // Back off this lproj component
921 workingLen = savedWorkingLen;
922 if (CFArrayGetCount(result) != numResults) {
923 // We found resources in a language we already searched. Don't look any farther.
924 // We also don't need to check the limit, since if the count changed at all, we are bailing.
925 break;
926 }
927 }
928 }
929 }
930
931 extern void _CFStrSetDesiredCapacity(CFMutableStringRef str, CFIndex len);
932
933 CFArrayRef _CFFindBundleResources(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef subDirName, CFArrayRef searchLanguages, CFStringRef resName, CFArrayRef resTypes, CFIndex limit, Boolean (^predicate)(CFStringRef filename, Boolean *stop), uint8_t version) {
934 CFMutableArrayRef result = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
935
936 // Build an absolute path to the base directory.
937 // If no URL was passed, we get it from the bundle.
938 CFURLRef baseURL = bundleURL ? (CFURLRef)CFRetain(bundleURL) : (bundle ? CFBundleCopyBundleURL(bundle) : NULL);
939 CFURLRef absoluteURL = baseURL ? CFURLCopyAbsoluteURL(baseURL) : NULL;
940 CFStringRef basePath = absoluteURL ? CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE) : NULL;
941 if (absoluteURL) CFRelease(absoluteURL);
942 if (baseURL) CFRelease(baseURL);
943 baseURL = absoluteURL = bundleURL = NULL;
944 bundle = NULL;
945 // bundle and bundleURL arguments are not used any further
946
947 if (!basePath) return result;
948
949
950 UniChar *workingUniChars, *nameUniChars, *subDirUniChars;
951 CFIndex nameLen = 0;
952 CFIndex workingLen, savedWorkingLen;
953 CFMutableStringRef cheapStr, tmpString;
954
955 if (resName) {
956 char buff[CFMaxPathSize];
957 CFStringRef newResName = NULL;
958 if (CFStringGetFileSystemRepresentation(resName, buff, CFMaxPathSize)) newResName = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff);
959 resName = newResName ? newResName : (CFStringRef)CFRetain(resName);
960 nameLen = CFStringGetLength(resName);
961 }
962
963 // Init the one-time-only unichar buffers.
964 _CFEnsureStaticBuffersInited();
965
966 // Build UniChar buffers for some of the string pieces we need.
967 CFIndex subDirLen = (subDirName ? CFStringGetLength(subDirName) : 0);
968 nameUniChars = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (nameLen + subDirLen + CFMaxPathSize), 0);
969 if (nameUniChars) {
970 subDirUniChars = nameUniChars + nameLen;
971 workingUniChars = subDirUniChars + subDirLen;
972
973 if (nameLen > 0) CFStringGetCharacters(resName, CFRangeMake(0, nameLen), nameUniChars);
974 if (subDirLen > 0) CFStringGetCharacters(subDirName, CFRangeMake(0, subDirLen), subDirUniChars);
975
976 if ((workingLen = CFStringGetLength(basePath)) > 0) CFStringGetCharacters(basePath, CFRangeMake(0, workingLen), workingUniChars);
977 savedWorkingLen = workingLen;
978 if (1 == version) {
979 _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _AppSupportUniChars1, _AppSupportLen1);
980 } else if (2 == version) {
981 _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _AppSupportUniChars2, _AppSupportLen2);
982 }
983 if (0 == version || 1 == version || 2 == version) _CFAppendPathComponent(workingUniChars, &workingLen, CFMaxPathSize, _ResourcesUniChars, _ResourcesLen);
984
985 // both of these used for temp string operations, for slightly different purposes, where each type is appropriate
986 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
987 _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize);
988 tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
989
990 _CFFindBundleResourcesInResourcesDir(kCFAllocatorSystemDefault, workingUniChars, workingLen, subDirUniChars, subDirLen, searchLanguages, nameUniChars, nameLen, resTypes, limit, predicate, version, cheapStr, tmpString, result);
991
992 // drd: This unfortunate hack is still necessary because of installer packages and Spotlight importers
993 if (CFArrayGetCount(result) == 0 && (0 == version || (2 == version && CFEqual(CFSTR("/Library/Spotlight"), basePath)))) {
994 // Try looking directly in the bundle path
995 workingLen = savedWorkingLen;
996 _CFFindBundleResourcesInResourcesDir(kCFAllocatorSystemDefault, workingUniChars, workingLen, subDirUniChars, subDirLen, searchLanguages, nameUniChars, nameLen, resTypes, limit, predicate, version, cheapStr, tmpString, result);
997 }
998
999 CFRelease(cheapStr);
1000 CFRelease(tmpString);
1001 CFAllocatorDeallocate(kCFAllocatorSystemDefault, nameUniChars);
1002 }
1003 if (resName) CFRelease(resName);
1004 if (basePath) CFRelease(basePath);
1005 return result;
1006 }
1007
1008 CF_EXPORT CFURLRef CFBundleCopyResourceURL(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) {
1009 if (!bundle)
1010 return NULL;
1011 CFURLRef result = NULL;
1012 CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array;
1013 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1014 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, NULL, _CFBundleLayoutVersion(bundle));
1015 if (types) CFRelease(types);
1016 if (array) {
1017 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
1018 CFRelease(array);
1019 }
1020 return result;
1021 }
1022
1023 CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfType(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName) {
1024 CFArrayRef languages = _CFBundleGetLanguageSearchList(bundle), types = NULL, array;
1025 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1026 // MF:!!! Better "limit" than 1,000,000?
1027 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, NULL, _CFBundleLayoutVersion(bundle));
1028 if (types) CFRelease(types);
1029
1030 return array;
1031 }
1032
1033 CF_EXPORT CFURLRef _CFBundleCopyResourceURLForLanguage(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {
1034 return CFBundleCopyResourceURLForLocalization(bundle, resourceName, resourceType, subDirName, language);
1035 }
1036
1037 CF_EXPORT CFURLRef CFBundleCopyResourceURLForLocalization(CFBundleRef bundle, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) {
1038 CFURLRef result = NULL;
1039 CFArrayRef languages = NULL, types = NULL, array;
1040
1041 if (localizationName) languages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&localizationName, 1, &kCFTypeArrayCallBacks);
1042 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1043 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, resourceName, types, 1, NULL, _CFBundleLayoutVersion(bundle));
1044 if (array) {
1045 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
1046 CFRelease(array);
1047 }
1048 if (types) CFRelease(types);
1049 if (languages) CFRelease(languages);
1050 return result;
1051 }
1052
1053 CF_EXPORT CFArrayRef _CFBundleCopyResourceURLsOfTypeForLanguage(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef language) {
1054 return CFBundleCopyResourceURLsOfTypeForLocalization(bundle, resourceType, subDirName, language);
1055 }
1056
1057 CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeForLocalization(CFBundleRef bundle, CFStringRef resourceType, CFStringRef subDirName, CFStringRef localizationName) {
1058 CFArrayRef languages = NULL, types = NULL, array;
1059
1060 if (localizationName) languages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&localizationName, 1, &kCFTypeArrayCallBacks);
1061 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1062 // MF:!!! Better "limit" than 1,000,000?
1063 array = _CFFindBundleResources(bundle, NULL, subDirName, languages, NULL, types, 1000000, NULL, _CFBundleLayoutVersion(bundle));
1064 if (types) CFRelease(types);
1065 if (languages) CFRelease(languages);
1066 return array;
1067 }
1068
1069
1070 CF_EXPORT CFStringRef CFBundleCopyLocalizedString(CFBundleRef bundle, CFStringRef key, CFStringRef value, CFStringRef tableName) {
1071 CFStringRef result = NULL;
1072 CFDictionaryRef stringTable = NULL;
1073 static CFSpinLock_t CFBundleLocalizedStringLock = CFSpinLockInit;
1074
1075 if (!key) return (value ? (CFStringRef)CFRetain(value) : (CFStringRef)CFRetain(CFSTR("")));
1076
1077 if (!tableName || CFEqual(tableName, CFSTR(""))) tableName = _CFBundleDefaultStringTableName;
1078
1079 __CFSpinLock(&CFBundleLocalizedStringLock);
1080 if (__CFBundleGetResourceData(bundle)->_stringTableCache) {
1081 stringTable = (CFDictionaryRef)CFDictionaryGetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName);
1082 if (stringTable) CFRetain(stringTable);
1083 }
1084 __CFSpinUnlock(&CFBundleLocalizedStringLock);
1085
1086 if (!stringTable) {
1087 // Go load the table.
1088 CFURLRef tableURL = CFBundleCopyResourceURL(bundle, tableName, _CFBundleStringTableType, NULL);
1089 if (tableURL) {
1090 CFStringRef nameForSharing = NULL;
1091 if (!stringTable) {
1092 CFDataRef tableData = NULL;
1093 SInt32 errCode;
1094 CFStringRef errStr;
1095 if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tableURL, &tableData, NULL, NULL, &errCode)) {
1096 stringTable = (CFDictionaryRef)CFPropertyListCreateFromXMLData(CFGetAllocator(bundle), tableData, kCFPropertyListImmutable, &errStr);
1097 if (errStr) {
1098 CFRelease(errStr);
1099 errStr = NULL;
1100 }
1101 if (stringTable && CFDictionaryGetTypeID() != CFGetTypeID(stringTable)) {
1102 CFRelease(stringTable);
1103 stringTable = NULL;
1104 }
1105 CFRelease(tableData);
1106
1107 }
1108 }
1109 if (nameForSharing) CFRelease(nameForSharing);
1110 if (tableURL) CFRelease(tableURL);
1111 }
1112 if (!stringTable) stringTable = CFDictionaryCreate(CFGetAllocator(bundle), NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1113
1114 if (!CFStringHasSuffix(tableName, CFSTR(".nocache")) || !_CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) {
1115 __CFSpinLock(&CFBundleLocalizedStringLock);
1116 if (!__CFBundleGetResourceData(bundle)->_stringTableCache) __CFBundleGetResourceData(bundle)->_stringTableCache = CFDictionaryCreateMutable(CFGetAllocator(bundle), 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
1117 CFDictionarySetValue(__CFBundleGetResourceData(bundle)->_stringTableCache, tableName, stringTable);
1118 __CFSpinUnlock(&CFBundleLocalizedStringLock);
1119 }
1120 }
1121
1122 result = (CFStringRef)CFDictionaryGetValue(stringTable, key);
1123 if (!result) {
1124 static int capitalize = -1;
1125 if (!value) {
1126 result = (CFStringRef)CFRetain(key);
1127 } else if (CFEqual(value, CFSTR(""))) {
1128 result = (CFStringRef)CFRetain(key);
1129 } else {
1130 result = (CFStringRef)CFRetain(value);
1131 }
1132 if (capitalize != 0) {
1133 if (capitalize != 0) {
1134 CFMutableStringRef capitalizedResult = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, result);
1135 CFLog(__kCFLogBundle, CFSTR("Localizable string \"%@\" not found in strings table \"%@\" of bundle %@."), key, tableName, bundle);
1136 CFStringUppercase(capitalizedResult, NULL);
1137 CFRelease(result);
1138 result = capitalizedResult;
1139 }
1140 }
1141 } else {
1142 CFRetain(result);
1143 }
1144 CFRelease(stringTable);
1145 return result;
1146 }
1147
1148 CF_EXPORT CFURLRef CFBundleCopyResourceURLInDirectory(CFURLRef bundleURL, CFStringRef resourceName, CFStringRef resourceType, CFStringRef subDirName) {
1149 CFURLRef result = NULL;
1150 unsigned char buff[CFMaxPathSize];
1151 CFURLRef newURL = NULL;
1152
1153 if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL;
1154
1155 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
1156 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL);
1157 if (_CFBundleCouldBeBundle(newURL)) {
1158 uint8_t version = 0;
1159 CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL, array;
1160 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1161 array = _CFFindBundleResources(NULL, newURL, subDirName, languages, resourceName, types, 1, NULL, version);
1162 if (types) CFRelease(types);
1163 if (languages) CFRelease(languages);
1164 if (array) {
1165 if (CFArrayGetCount(array) > 0) result = (CFURLRef)CFRetain(CFArrayGetValueAtIndex(array, 0));
1166 CFRelease(array);
1167 }
1168 }
1169 if (newURL) CFRelease(newURL);
1170 return result;
1171 }
1172
1173 CF_EXPORT CFArrayRef CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleURL, CFStringRef resourceType, CFStringRef subDirName) {
1174 CFArrayRef array = NULL;
1175 unsigned char buff[CFMaxPathSize];
1176 CFURLRef newURL = NULL;
1177
1178 if (!CFURLGetFileSystemRepresentation(bundleURL, true, buff, CFMaxPathSize)) return NULL;
1179
1180 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
1181 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL);
1182 if (_CFBundleCouldBeBundle(newURL)) {
1183 uint8_t version = 0;
1184 CFArrayRef languages = _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault, newURL, &version), types = NULL;
1185 if (resourceType) types = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&resourceType, 1, &kCFTypeArrayCallBacks);
1186 // MF:!!! Better "limit" than 1,000,000?
1187 array = _CFFindBundleResources(NULL, newURL, subDirName, languages, NULL, types, 1000000, NULL, version);
1188 if (types) CFRelease(types);
1189 if (languages) CFRelease(languages);
1190 }
1191 if (newURL) CFRelease(newURL);
1192 return array;
1193 }
1194
1195 // string, with groups of 6 characters being 1 element in the array of locale abbreviations
1196 const char * __CFBundleLocaleAbbreviationsArray =
1197 "en_US\0" "fr_FR\0" "en_GB\0" "de_DE\0" "it_IT\0" "nl_NL\0" "nl_BE\0" "sv_SE\0"
1198 "es_ES\0" "da_DK\0" "pt_PT\0" "fr_CA\0" "nb_NO\0" "he_IL\0" "ja_JP\0" "en_AU\0"
1199 "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"
1200 "tr_TR\0" "hr_HR\0" "nl_NL\0" "nl_BE\0" "en_CA\0" "en_CA\0" "pt_PT\0" "nb_NO\0"
1201 "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"
1202 "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"
1203 "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"
1204 "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"
1205 "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"
1206 "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"
1207 "en_CA\0" "ga_IE\0" "en_CA\0" "dz_BT\0" "hy_AM\0" "ka_GE\0" "es_XL\0" "es_ES\0"
1208 "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"
1209 "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"
1210 "mr_IN\0" "bo\0\0\0\0" "ne_NP\0" "kl\0\0\0\0" "en_IE\0";
1211
1212 #define NUM_LOCALE_ABBREVIATIONS 109
1213 #define LOCALE_ABBREVIATION_LENGTH 6
1214
1215 static const char * const __CFBundleLanguageNamesArray[] = {
1216 "English", "French", "German", "Italian", "Dutch", "Swedish", "Spanish", "Danish",
1217 "Portuguese", "Norwegian", "Hebrew", "Japanese", "Arabic", "Finnish", "Greek", "Icelandic",
1218 "Maltese", "Turkish", "Croatian", "Chinese", "Urdu", "Hindi", "Thai", "Korean",
1219 "Lithuanian", "Polish", "Hungarian", "Estonian", "Latvian", "Sami", "Faroese", "Farsi",
1220 "Russian", "Chinese", "Dutch", "Irish", "Albanian", "Romanian", "Czech", "Slovak",
1221 "Slovenian", "Yiddish", "Serbian", "Macedonian", "Bulgarian", "Ukrainian", "Byelorussian", "Uzbek",
1222 "Kazakh", "Azerbaijani", "Azerbaijani", "Armenian", "Georgian", "Moldavian", "Kirghiz", "Tajiki",
1223 "Turkmen", "Mongolian", "Mongolian", "Pashto", "Kurdish", "Kashmiri", "Sindhi", "Tibetan",
1224 "Nepali", "Sanskrit", "Marathi", "Bengali", "Assamese", "Gujarati", "Punjabi", "Oriya",
1225 "Malayalam", "Kannada", "Tamil", "Telugu", "Sinhalese", "Burmese", "Khmer", "Lao",
1226 "Vietnamese", "Indonesian", "Tagalog", "Malay", "Malay", "Amharic", "Tigrinya", "Oromo",
1227 "Somali", "Swahili", "Kinyarwanda", "Rundi", "Nyanja", "Malagasy", "Esperanto", "",
1228 "", "", "", "", "", "", "", "",
1229 "", "", "", "", "", "", "", "",
1230 "", "", "", "", "", "", "", "",
1231 "", "", "", "", "", "", "", "",
1232 "Welsh", "Basque", "Catalan", "Latin", "Quechua", "Guarani", "Aymara", "Tatar",
1233 "Uighur", "Dzongkha", "Javanese", "Sundanese", "Galician", "Afrikaans", "Breton", "Inuktitut",
1234 "Scottish", "Manx", "Irish", "Tongan", "Greek", "Greenlandic", "Azerbaijani", "Nynorsk"
1235 };
1236
1237 #define NUM_LANGUAGE_NAMES 152
1238 #define LANGUAGE_NAME_LENGTH 13
1239
1240 // string, with groups of 3 characters being 1 element in the array of abbreviations
1241 const char * __CFBundleLanguageAbbreviationsArray =
1242 "en\0" "fr\0" "de\0" "it\0" "nl\0" "sv\0" "es\0" "da\0"
1243 "pt\0" "nb\0" "he\0" "ja\0" "ar\0" "fi\0" "el\0" "is\0"
1244 "mt\0" "tr\0" "hr\0" "zh\0" "ur\0" "hi\0" "th\0" "ko\0"
1245 "lt\0" "pl\0" "hu\0" "et\0" "lv\0" "se\0" "fo\0" "fa\0"
1246 "ru\0" "zh\0" "nl\0" "ga\0" "sq\0" "ro\0" "cs\0" "sk\0"
1247 "sl\0" "yi\0" "sr\0" "mk\0" "bg\0" "uk\0" "be\0" "uz\0"
1248 "kk\0" "az\0" "az\0" "hy\0" "ka\0" "mo\0" "ky\0" "tg\0"
1249 "tk\0" "mn\0" "mn\0" "ps\0" "ku\0" "ks\0" "sd\0" "bo\0"
1250 "ne\0" "sa\0" "mr\0" "bn\0" "as\0" "gu\0" "pa\0" "or\0"
1251 "ml\0" "kn\0" "ta\0" "te\0" "si\0" "my\0" "km\0" "lo\0"
1252 "vi\0" "id\0" "tl\0" "ms\0" "ms\0" "am\0" "ti\0" "om\0"
1253 "so\0" "sw\0" "rw\0" "rn\0" "\0\0\0" "mg\0" "eo\0" "\0\0\0"
1254 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1255 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1256 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1257 "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0" "\0\0\0"
1258 "cy\0" "eu\0" "ca\0" "la\0" "qu\0" "gn\0" "ay\0" "tt\0"
1259 "ug\0" "dz\0" "jv\0" "su\0" "gl\0" "af\0" "br\0" "iu\0"
1260 "gd\0" "gv\0" "ga\0" "to\0" "el\0" "kl\0" "az\0" "nn\0";
1261
1262 #define NUM_LANGUAGE_ABBREVIATIONS 152
1263 #define LANGUAGE_ABBREVIATION_LENGTH 3
1264
1265 #if defined(__CONSTANT_CFSTRINGS__)
1266
1267 // These are not necessarily common localizations per se, but localizations for which the full language name is still in common use.
1268 // These are used to provide a fast path for it (other localizations usually use the abbreviation, which is even faster).
1269 static CFStringRef const __CFBundleCommonLanguageNamesArray[] = {CFSTR("English"), CFSTR("French"), CFSTR("German"), CFSTR("Italian"), CFSTR("Dutch"), CFSTR("Spanish"), CFSTR("Japanese")};
1270 static CFStringRef const __CFBundleCommonLanguageAbbreviationsArray[] = {CFSTR("en"), CFSTR("fr"), CFSTR("de"), CFSTR("it"), CFSTR("nl"), CFSTR("es"), CFSTR("ja")};
1271
1272 #define NUM_COMMON_LANGUAGE_NAMES 7
1273
1274 #endif /* __CONSTANT_CFSTRINGS__ */
1275
1276 static const SInt32 __CFBundleScriptCodesArray[] = {
1277 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 0,
1278 0, 0, 0, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 0, 4,
1279 7, 25, 0, 0, 0, 0, 29, 29, 0, 5, 7, 7, 7, 7, 7, 7,
1280 7, 7, 4, 24, 23, 7, 7, 7, 7, 27, 7, 4, 4, 4, 4, 26,
1281 9, 9, 9, 13, 13, 11, 10, 12, 17, 16, 14, 15, 18, 19, 20, 22,
1282 30, 0, 0, 0, 4, 28, 28, 28, 0, 0, 0, 0, 0, 0, 0, 0,
1283 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1284 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1285 0, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 0, 28,
1286 0, 0, 0, 0, 6, 0, 0, 0
1287 };
1288
1289 static const CFStringEncoding __CFBundleStringEncodingsArray[] = {
1290 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 4, 0, 6, 37,
1291 0, 35, 36, 2, 4, 9, 21, 3, 29, 29, 29, 29, 29, 0, 37, 0x8C,
1292 7, 25, 0, 39, 0, 38, 29, 29, 36, 5, 7, 7, 7, 0x98, 7, 7,
1293 7, 7, 4, 24, 23, 7, 7, 7, 7, 27, 7, 4, 4, 4, 4, 26,
1294 9, 9, 9, 13, 13, 11, 10, 12, 17, 16, 14, 15, 18, 19, 20, 22,
1295 30, 0, 0, 0, 4, 28, 28, 28, 0, 0, 0, 0, 0, 0, 0, 0,
1296 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1297 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1298 39, 0, 0, 0, 0, 0, 0, 7, 4, 26, 0, 0, 0, 0, 39, 0xEC,
1299 39, 39, 40, 0, 6, 0, 0, 0
1300 };
1301
1302 static SInt32 _CFBundleGetLanguageCodeForLocalization(CFStringRef localizationName) {
1303 SInt32 result = -1, i;
1304 char buff[256];
1305 CFIndex length = CFStringGetLength(localizationName);
1306 if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= 255 && CFStringGetCString(localizationName, buff, 255, kCFStringEncodingASCII)) {
1307 buff[255] = '\0';
1308 for (i = 0; -1 == result && i < NUM_LANGUAGE_NAMES; i++) {
1309 if (0 == strcmp(buff, __CFBundleLanguageNamesArray[i])) result = i;
1310 }
1311 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
1312 if (-1 == result && (length == LANGUAGE_ABBREVIATION_LENGTH - 1 || !isalpha(buff[LANGUAGE_ABBREVIATION_LENGTH - 1]))) {
1313 buff[LANGUAGE_ABBREVIATION_LENGTH - 1] = '\0';
1314 if ('n' == buff[0] && 'o' == buff[1]) result = 9; // hack for Norwegian
1315 for (i = 0; -1 == result && i < NUM_LANGUAGE_ABBREVIATIONS * LANGUAGE_ABBREVIATION_LENGTH; i += LANGUAGE_ABBREVIATION_LENGTH) {
1316 if (buff[0] == *(__CFBundleLanguageAbbreviationsArray + i + 0) && buff[1] == *(__CFBundleLanguageAbbreviationsArray + i + 1)) result = i / LANGUAGE_ABBREVIATION_LENGTH;
1317 }
1318 }
1319 }
1320 return result;
1321 }
1322
1323 static CFStringRef _CFBundleCopyLanguageAbbreviationForLanguageCode(SInt32 languageCode) {
1324 CFStringRef result = NULL;
1325 if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) {
1326 const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH;
1327 if (languageAbbreviation && *languageAbbreviation != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull);
1328 }
1329 return result;
1330 }
1331
1332 CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLanguageCode(SInt32 languageCode) {
1333 CFStringRef result = NULL;
1334 if (0 <= languageCode && languageCode < NUM_LANGUAGE_NAMES) {
1335 const char *languageName = __CFBundleLanguageNamesArray[languageCode];
1336 if (languageName && *languageName != '\0') result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, languageName, kCFStringEncodingASCII, kCFAllocatorNull);
1337 }
1338 return result;
1339 }
1340
1341 CF_INLINE CFStringRef _CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName) {
1342 CFStringRef result = NULL;
1343 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1344 if (languageCode >= 0) {
1345 result = _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode);
1346 } else {
1347 CFIndex length = CFStringGetLength(localizationName);
1348 if (length == LANGUAGE_ABBREVIATION_LENGTH - 1 || (length > LANGUAGE_ABBREVIATION_LENGTH - 1 && CFStringGetCharacterAtIndex(localizationName, LANGUAGE_ABBREVIATION_LENGTH - 1) == '_')) {
1349 result = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, localizationName, CFRangeMake(0, LANGUAGE_ABBREVIATION_LENGTH - 1));
1350 }
1351 }
1352 return result;
1353 }
1354
1355 CF_INLINE CFStringRef _CFBundleCopyModifiedLocalization(CFStringRef localizationName) {
1356 CFMutableStringRef result = NULL;
1357 CFIndex length = CFStringGetLength(localizationName);
1358 if (length >= 4) {
1359 UniChar c = CFStringGetCharacterAtIndex(localizationName, 2);
1360 if ('-' == c || '_' == c) {
1361 result = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, length, localizationName);
1362 CFStringReplace(result, CFRangeMake(2, 1), ('-' == c) ? CFSTR("_") : CFSTR("-"));
1363 }
1364 }
1365 return result;
1366 }
1367
1368 CF_INLINE CFStringRef _CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName) {
1369 CFStringRef result = NULL;
1370 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1371 if (languageCode >= 0) {
1372 result = _CFBundleCopyLanguageNameForLanguageCode(languageCode);
1373 } else {
1374 result = (CFStringRef)CFStringCreateCopy(kCFAllocatorSystemDefault, localizationName);
1375 }
1376 return result;
1377 }
1378
1379 static SInt32 _CFBundleGetLanguageCodeForRegionCode(SInt32 regionCode) {
1380 SInt32 result = -1, i;
1381 if (52 == regionCode) { // hack for mixed-up Chinese language codes
1382 result = 33;
1383 } else if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) {
1384 const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH;
1385 if (localeAbbreviation && *localeAbbreviation != '\0') {
1386 for (i = 0; -1 == result && i < NUM_LANGUAGE_ABBREVIATIONS * LANGUAGE_ABBREVIATION_LENGTH; i += LANGUAGE_ABBREVIATION_LENGTH) {
1387 if (localeAbbreviation[0] == *(__CFBundleLanguageAbbreviationsArray + i + 0) && localeAbbreviation[1] == *(__CFBundleLanguageAbbreviationsArray + i + 1)) result = i / LANGUAGE_ABBREVIATION_LENGTH;
1388 }
1389 }
1390 }
1391 return result;
1392 }
1393
1394 static SInt32 _CFBundleGetRegionCodeForLanguageCode(SInt32 languageCode) {
1395 SInt32 result = -1, i;
1396 if (19 == languageCode) { // hack for mixed-up Chinese language codes
1397 result = 53;
1398 } else if (0 <= languageCode && languageCode < NUM_LANGUAGE_ABBREVIATIONS) {
1399 const char *languageAbbreviation = __CFBundleLanguageAbbreviationsArray + languageCode * LANGUAGE_ABBREVIATION_LENGTH;
1400 if (languageAbbreviation && *languageAbbreviation != '\0') {
1401 for (i = 0; -1 == result && i < NUM_LOCALE_ABBREVIATIONS * LOCALE_ABBREVIATION_LENGTH; i += LOCALE_ABBREVIATION_LENGTH) {
1402 if (*(__CFBundleLocaleAbbreviationsArray + i + 0) == languageAbbreviation[0] && *(__CFBundleLocaleAbbreviationsArray + i + 1) == languageAbbreviation[1]) result = i / LOCALE_ABBREVIATION_LENGTH;
1403 }
1404 }
1405 }
1406 if (25 == result) result = 68;
1407 if (28 == result) result = 82;
1408 return result;
1409 }
1410
1411 static SInt32 _CFBundleGetRegionCodeForLocalization(CFStringRef localizationName) {
1412 SInt32 result = -1, i;
1413 char buff[LOCALE_ABBREVIATION_LENGTH];
1414 CFIndex length = CFStringGetLength(localizationName);
1415 if (length >= LANGUAGE_ABBREVIATION_LENGTH - 1 && length <= LOCALE_ABBREVIATION_LENGTH - 1 && CFStringGetCString(localizationName, buff, LOCALE_ABBREVIATION_LENGTH, kCFStringEncodingASCII)) {
1416 buff[LOCALE_ABBREVIATION_LENGTH - 1] = '\0';
1417 for (i = 0; -1 == result && i < NUM_LOCALE_ABBREVIATIONS * LOCALE_ABBREVIATION_LENGTH; i += LOCALE_ABBREVIATION_LENGTH) {
1418 if (0 == strcmp(buff, __CFBundleLocaleAbbreviationsArray + i)) result = i / LOCALE_ABBREVIATION_LENGTH;
1419 }
1420 }
1421 if (25 == result) result = 68;
1422 if (28 == result) result = 82;
1423 if (37 == result) result = 0;
1424 if (-1 == result) {
1425 SInt32 languageCode = _CFBundleGetLanguageCodeForLocalization(localizationName);
1426 result = _CFBundleGetRegionCodeForLanguageCode(languageCode);
1427 }
1428 return result;
1429 }
1430
1431 static CFStringRef _CFBundleCopyLocaleAbbreviationForRegionCode(SInt32 regionCode) {
1432 CFStringRef result = NULL;
1433 if (0 <= regionCode && regionCode < NUM_LOCALE_ABBREVIATIONS) {
1434 const char *localeAbbreviation = __CFBundleLocaleAbbreviationsArray + regionCode * LOCALE_ABBREVIATION_LENGTH;
1435 if (localeAbbreviation && *localeAbbreviation != '\0') {
1436 result = CFStringCreateWithCStringNoCopy(kCFAllocatorSystemDefault, localeAbbreviation, kCFStringEncodingASCII, kCFAllocatorNull);
1437 }
1438 }
1439 return result;
1440 }
1441
1442 Boolean CFBundleGetLocalizationInfoForLocalization(CFStringRef localizationName, SInt32 *languageCode, SInt32 *regionCode, SInt32 *scriptCode, CFStringEncoding *stringEncoding) {
1443 Boolean retval = false;
1444 SInt32 language = -1, region = -1, script = 0;
1445 CFStringEncoding encoding = kCFStringEncodingMacRoman;
1446 if (!localizationName) {
1447 CFBundleRef mainBundle = CFBundleGetMainBundle();
1448 CFArrayRef languages = NULL;
1449 if (mainBundle) {
1450 languages = _CFBundleGetLanguageSearchList(mainBundle);
1451 if (languages) CFRetain(languages);
1452 }
1453 if (!languages) languages = _CFBundleCopyUserLanguages(false);
1454 if (languages && CFArrayGetCount(languages) > 0) localizationName = (CFStringRef)CFArrayGetValueAtIndex(languages, 0);
1455 }
1456 if (localizationName) {
1457 LangCode langCode = -1;
1458 RegionCode regCode = -1;
1459 ScriptCode scrCode = 0;
1460 CFStringEncoding enc = kCFStringEncodingMacRoman;
1461 retval = CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(localizationName, &langCode, &regCode, &scrCode, &enc);
1462 if (retval) {
1463 language = langCode;
1464 region = regCode;
1465 script = scrCode;
1466 encoding = enc;
1467 }
1468 }
1469 if (!retval) {
1470 if (localizationName) {
1471 language = _CFBundleGetLanguageCodeForLocalization(localizationName);
1472 region = _CFBundleGetRegionCodeForLocalization(localizationName);
1473 } else {
1474 _CFBundleGetLanguageAndRegionCodes(&language, &region);
1475 }
1476 if ((language < 0 || language > (int)(sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32))) && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region);
1477 if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language);
1478 if (language >= 0 && language < (int)(sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32))) {
1479 script = __CFBundleScriptCodesArray[language];
1480 }
1481 if (language >= 0 && language < (int)(sizeof(__CFBundleStringEncodingsArray)/sizeof(CFStringEncoding))) {
1482 encoding = __CFBundleStringEncodingsArray[language];
1483 }
1484 retval = (language != -1 || region != -1);
1485 }
1486 if (languageCode) *languageCode = language;
1487 if (regionCode) *regionCode = region;
1488 if (scriptCode) *scriptCode = script;
1489 if (stringEncoding) *stringEncoding = encoding;
1490 return retval;
1491 }
1492
1493 CFStringRef CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode, SInt32 regionCode, SInt32 scriptCode, CFStringEncoding stringEncoding) {
1494 CFStringRef localizationName = NULL;
1495 if (!localizationName) localizationName = _CFBundleCopyLocaleAbbreviationForRegionCode(regionCode);
1496 #if DEPLOYMENT_TARGET_MACOSX
1497 if (!localizationName && 0 <= languageCode && languageCode < SHRT_MAX) localizationName = CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(kCFAllocatorSystemDefault, (LangCode)languageCode, (RegionCode)-1);
1498 #endif
1499 if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode);
1500 if (!localizationName) {
1501 SInt32 language = -1, scriptLanguage = -1, encodingLanguage = -1;
1502 unsigned int i;
1503 for (i = 0; language == -1 && i < (sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32)); i++) {
1504 if (__CFBundleScriptCodesArray[i] == scriptCode && __CFBundleStringEncodingsArray[i] == stringEncoding) language = i;
1505 }
1506 for (i = 0; scriptLanguage == -1 && i < (sizeof(__CFBundleScriptCodesArray)/sizeof(SInt32)); i++) {
1507 if (__CFBundleScriptCodesArray[i] == scriptCode) scriptLanguage = i;
1508 }
1509 for (i = 0; encodingLanguage == -1 && i < (sizeof(__CFBundleStringEncodingsArray)/sizeof(CFStringEncoding)); i++) {
1510 if (__CFBundleStringEncodingsArray[i] == stringEncoding) encodingLanguage = i;
1511 }
1512 localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(language);
1513 if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(encodingLanguage);
1514 if (!localizationName) localizationName = _CFBundleCopyLanguageAbbreviationForLanguageCode(scriptLanguage);
1515 }
1516 return localizationName;
1517 }
1518
1519 extern void *__CFAppleLanguages;
1520
1521 #if DEPLOYMENT_TARGET_WINDOWS
1522
1523 extern CFStringRef copyLocaleLanguageName(void);
1524 extern CFStringRef copyLocaleCountryName(void);
1525
1526 static CFArrayRef copyWindowsLanguagePrefsArray() {
1527 CFArrayRef result;
1528 CFStringRef locales[4];
1529 CFStringRef languageName = copyLocaleLanguageName(), countryName = copyLocaleCountryName();
1530 if (!languageName) languageName = CFSTR("en");
1531 if (!countryName) countryName = CFSTR("");
1532 CFIndex i, localesCount = 0;
1533 if (CFStringGetLength(countryName) > 0) locales[localesCount++] = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@_%@"), languageName, countryName);
1534 if (CFStringGetLength(languageName) != 0) {
1535 // special-case for zh since we don't have a generic zh localization
1536 if (CFStringCompare(languageName, CFSTR("zh"), kCFCompareCaseInsensitive) != 0) {
1537 locales[localesCount++] = CFStringCreateCopy(kCFAllocatorSystemDefault, languageName);//languageName;
1538 } else {
1539 CFStringRef moreSpecificLanguageName;
1540
1541 // See http://intrigue-build.apple.com/changeset/14948 for the details on the change. Copied below is the snippet of the code change.
1542 // According to http://www.microsoft.com/globaldev/reference/win2k/setup/lcid.mspx, the locales that use
1543 // 126 // simplified chinese are CN (PRC) and SG (Singapore). The rest use traditional chinese.
1544 // 127 languageName = (countryName == TEXT("CN") || countryName == TEXT("SG")) ? TEXT("zh_CN") : TEXT("zh_TW");
1545
1546 // Compare for CN or SG
1547 if (CFStringCompare(countryName, CFSTR("CN"), kCFCompareCaseInsensitive) == 0 || CFStringCompare(countryName, CFSTR("SG"), kCFCompareCaseInsensitive) == 0) {
1548 moreSpecificLanguageName = CFSTR("zh_CN");
1549 } else {
1550 moreSpecificLanguageName = CFSTR("zh_TW");
1551 }
1552 locales[localesCount++] = CFStringCreateCopy(kCFAllocatorSystemDefault, moreSpecificLanguageName);
1553 }
1554 // Don't need this now
1555 if (languageName) CFRelease(languageName);
1556 if (countryName) CFRelease(countryName);
1557 }
1558 if (localesCount == 0) locales[localesCount++] = CFStringCreateCopy(kCFAllocatorSystemDefault, CFSTR("en"));
1559 result = CFArrayCreate(kCFAllocatorDefault, (const void **)locales, localesCount, &kCFTypeArrayCallBacks);
1560 for (i = 0; i < localesCount; i++) CFRelease(locales[i]);
1561 return result;
1562 }
1563
1564 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
1565 #else
1566 #error Unknown or unspecified DEPLOYMENT_TARGET
1567 #endif
1568
1569 static CFArrayRef _CFBundleUserLanguages = NULL;
1570
1571 __private_extern__ CFArrayRef _CFBundleCopyUserLanguages(Boolean useBackstops) {
1572 CFArrayRef result = NULL;
1573 static Boolean didit = false;
1574 CFArrayRef preferencesArray = NULL;
1575 // This is a temporary solution, until the argument domain is moved down into CFPreferences
1576 __CFSpinLock(&CFBundleResourceGlobalDataLock);
1577 if (!didit) {
1578 if (__CFAppleLanguages) {
1579 CFDataRef data;
1580 CFIndex length = strlen((const char *)__CFAppleLanguages);
1581 if (length > 0) {
1582 data = CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault, (const UInt8 *)__CFAppleLanguages, length, kCFAllocatorNull);
1583 if (data) {
1584 _CFBundleUserLanguages = (CFArrayRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListImmutable, NULL);
1585 CFRelease(data);
1586 }
1587 }
1588 }
1589 if (!_CFBundleUserLanguages && preferencesArray) _CFBundleUserLanguages = (CFArrayRef)CFRetain(preferencesArray);
1590 Boolean useEnglishAsBackstop = true;
1591 // could perhaps read out of LANG environment variable
1592 if (useEnglishAsBackstop && !_CFBundleUserLanguages) {
1593 CFStringRef english = CFSTR("en");
1594 _CFBundleUserLanguages = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&english, 1, &kCFTypeArrayCallBacks);
1595 }
1596 if (_CFBundleUserLanguages && CFGetTypeID(_CFBundleUserLanguages) != CFArrayGetTypeID()) {
1597 CFRelease(_CFBundleUserLanguages);
1598 _CFBundleUserLanguages = NULL;
1599 }
1600 didit = true;
1601 }
1602 __CFSpinUnlock(&CFBundleResourceGlobalDataLock);
1603 if (preferencesArray) CFRelease(preferencesArray);
1604 if (!result && _CFBundleUserLanguages) result = (CFArrayRef)CFRetain(_CFBundleUserLanguages);
1605 return result;
1606 }
1607
1608 CF_EXPORT void _CFBundleGetLanguageAndRegionCodes(SInt32 *languageCode, SInt32 *regionCode) {
1609 // an attempt to answer the question, "what language are we running in?"
1610 // note that the question cannot be answered fully since it may depend on the bundle
1611 SInt32 language = -1, region = -1;
1612 CFBundleRef mainBundle = CFBundleGetMainBundle();
1613 CFArrayRef languages = NULL;
1614 if (mainBundle) {
1615 languages = _CFBundleGetLanguageSearchList(mainBundle);
1616 if (languages) CFRetain(languages);
1617 }
1618 if (!languages) languages = _CFBundleCopyUserLanguages(false);
1619 if (languages && CFArrayGetCount(languages) > 0) {
1620 CFStringRef localizationName = (CFStringRef)CFArrayGetValueAtIndex(languages, 0);
1621 Boolean retval = false;
1622 LangCode langCode = -1;
1623 RegionCode regCode = -1;
1624 retval = CFLocaleGetLanguageRegionEncodingForLocaleIdentifier(localizationName, &langCode, &regCode, NULL, NULL);
1625 if (retval) {
1626 language = langCode;
1627 region = regCode;
1628 }
1629 if (!retval) {
1630 language = _CFBundleGetLanguageCodeForLocalization(localizationName);
1631 region = _CFBundleGetRegionCodeForLocalization(localizationName);
1632 }
1633 } else {
1634 language = 0;
1635 region = 0;
1636 }
1637 if (language == -1 && region != -1) language = _CFBundleGetLanguageCodeForRegionCode(region);
1638 if (region == -1 && language != -1) region = _CFBundleGetRegionCodeForLanguageCode(language);
1639 if (languages) CFRelease(languages);
1640 if (languageCode) *languageCode = language;
1641 if (regionCode) *regionCode = region;
1642 }
1643
1644
1645 static Boolean _CFBundleTryOnePreferredLprojNameInDirectory(CFAllocatorRef alloc, UniChar *pathUniChars, CFIndex pathLen, uint8_t version, CFDictionaryRef infoDict, CFStringRef curLangStr, CFMutableArrayRef lprojNames, Boolean fallBackToLanguage) {
1646 CFIndex curLangLen = CFStringGetLength(curLangStr), savedPathLen;
1647 UniChar curLangUniChars[255];
1648 CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL, canonicalLanguageAbbreviation = NULL;
1649 CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL, predefinedCanonicalLanguageIdentifiers = NULL;
1650 Boolean foundOne = false, specifiesScript = false;
1651 CFArrayRef predefinedLocalizations = NULL;
1652 CFRange predefinedLocalizationsRange;
1653 CFMutableStringRef cheapStr, tmpString;
1654 #if READ_DIRECTORIES
1655 CFArrayRef contents;
1656 CFRange contentsRange;
1657 #else /* READ_DIRECTORIES */
1658 Boolean isDir = false;
1659 #endif /* READ_DIRECTORIES */
1660
1661 // both of these used for temp string operations, for slightly
1662 // different purposes, where each type is appropriate
1663 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
1664 _CFStrSetDesiredCapacity(cheapStr, CFMaxPathSize);
1665 tmpString = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, kCFAllocatorNull);
1666
1667 #if READ_DIRECTORIES
1668 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1669 CFStringReplaceAll(cheapStr, tmpString);
1670 contents = _CFBundleCopySortedDirectoryContentsAtPath(cheapStr, _CFBundleAllContents);
1671 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
1672 #endif /* READ_DIRECTORIES */
1673
1674 if (infoDict) {
1675 predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
1676 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) {
1677 predefinedLocalizations = NULL;
1678 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey);
1679 }
1680 }
1681 predefinedLocalizationsRange = CFRangeMake(0, predefinedLocalizations ? CFArrayGetCount(predefinedLocalizations) : 0);
1682
1683 if (curLangLen > 255) curLangLen = 255;
1684 CFStringGetCharacters(curLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1685 savedPathLen = pathLen;
1686 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1687 #if READ_DIRECTORIES
1688 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1689 CFStringReplaceAll(cheapStr, tmpString);
1690 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1691 #else /* READ_DIRECTORIES */
1692 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1693 CFStringReplaceAll(cheapStr, tmpString);
1694 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, curLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) {
1695 #endif /* READ_DIRECTORIES */
1696 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr);
1697 foundOne = true;
1698 if (CFStringGetLength(curLangStr) <= 2) {
1699 CFRelease(cheapStr);
1700 CFRelease(tmpString);
1701 #if READ_DIRECTORIES
1702 CFRelease(contents);
1703 #endif /* READ_DIRECTORIES */
1704 return foundOne;
1705 }
1706 }
1707 }
1708 #if defined(__CONSTANT_CFSTRINGS__)
1709 if (!altLangStr) {
1710 CFIndex idx;
1711 for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) {
1712 if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx];
1713 else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx];
1714 }
1715 }
1716 #endif /* __CONSTANT_CFSTRINGS__ */
1717 if (foundOne && altLangStr) {
1718 CFRelease(cheapStr);
1719 CFRelease(tmpString);
1720 #if READ_DIRECTORIES
1721 CFRelease(contents);
1722 #endif /* READ_DIRECTORIES */
1723 return foundOne;
1724 }
1725 if (altLangStr) {
1726 curLangLen = CFStringGetLength(altLangStr);
1727 if (curLangLen > 255) curLangLen = 255;
1728 CFStringGetCharacters(altLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1729 pathLen = savedPathLen;
1730 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1731 #if READ_DIRECTORIES
1732 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1733 CFStringReplaceAll(cheapStr, tmpString);
1734 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1735 #else /* READ_DIRECTORIES */
1736 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1737 CFStringReplaceAll(cheapStr, tmpString);
1738 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, altLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) {
1739 #endif /* READ_DIRECTORIES */
1740 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr);
1741 foundOne = true;
1742 CFRelease(cheapStr);
1743 CFRelease(tmpString);
1744 #if READ_DIRECTORIES
1745 CFRelease(contents);
1746 #endif /* READ_DIRECTORIES */
1747 return foundOne;
1748 }
1749 }
1750 }
1751 #if READ_DIRECTORIES
1752 if (!foundOne && (!predefinedLocalizations || CFArrayGetCount(predefinedLocalizations) == 0)) {
1753 Boolean hasLocalizations = false;
1754 CFIndex idx;
1755 for (idx = 0; !hasLocalizations && idx < contentsRange.length; idx++) {
1756 CFStringRef name = CFArrayGetValueAtIndex(contents, idx);
1757 if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) hasLocalizations = true;
1758 }
1759 if (!hasLocalizations) {
1760 CFRelease(cheapStr);
1761 CFRelease(tmpString);
1762 CFRelease(contents);
1763 return foundOne;
1764 }
1765 }
1766 #endif /* READ_DIRECTORIES */
1767 if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) {
1768 curLangLen = CFStringGetLength(modifiedLangStr);
1769 if (curLangLen > 255) curLangLen = 255;
1770 CFStringGetCharacters(modifiedLangStr, CFRangeMake(0, curLangLen), curLangUniChars);
1771 pathLen = savedPathLen;
1772 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1773 #if READ_DIRECTORIES
1774 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1775 CFStringReplaceAll(cheapStr, tmpString);
1776 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1777 #else /* READ_DIRECTORIES */
1778 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1779 CFStringReplaceAll(cheapStr, tmpString);
1780 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, modifiedLangStr)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) {
1781 #endif /* READ_DIRECTORIES */
1782 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr);
1783 foundOne = true;
1784 }
1785 }
1786 }
1787 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) {
1788 curLangLen = CFStringGetLength(languageAbbreviation);
1789 if (curLangLen > 255) curLangLen = 255;
1790 CFStringGetCharacters(languageAbbreviation, CFRangeMake(0, curLangLen), curLangUniChars);
1791 pathLen = savedPathLen;
1792 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1793 #if READ_DIRECTORIES
1794 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1795 CFStringReplaceAll(cheapStr, tmpString);
1796 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1797 #else /* READ_DIRECTORIES */
1798 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1799 CFStringReplaceAll(cheapStr, tmpString);
1800 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageAbbreviation)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) {
1801 #endif /* READ_DIRECTORIES */
1802 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation);
1803 foundOne = true;
1804 }
1805 }
1806 }
1807 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) {
1808 curLangLen = CFStringGetLength(languageName);
1809 if (curLangLen > 255) curLangLen = 255;
1810 CFStringGetCharacters(languageName, CFRangeMake(0, curLangLen), curLangUniChars);
1811 pathLen = savedPathLen;
1812 if (_CFAppendPathComponent(pathUniChars, &pathLen, CFMaxPathSize, curLangUniChars, curLangLen) && _CFAppendPathExtension(pathUniChars, &pathLen, CFMaxPathSize, _LprojUniChars, _LprojLen)) {
1813 #if READ_DIRECTORIES
1814 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars + savedPathLen + 1, pathLen - savedPathLen - 1, pathLen - savedPathLen - 1);
1815 CFStringReplaceAll(cheapStr, tmpString);
1816 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFBundleSortedArrayContains(contents, cheapStr))) {
1817 #else /* READ_DIRECTORIES */
1818 CFStringSetExternalCharactersNoCopy(tmpString, pathUniChars, pathLen, pathLen);
1819 CFStringReplaceAll(cheapStr, tmpString);
1820 if ((predefinedLocalizations && CFArrayContainsValue(predefinedLocalizations, predefinedLocalizationsRange, languageName)) || (version != 4 && _CFIsResourceAtPath(cheapStr, &isDir) && isDir)) {
1821 #endif /* READ_DIRECTORIES */
1822 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName);
1823 foundOne = true;
1824 }
1825 }
1826 }
1827 if (modifiedLangStr) CFRelease(modifiedLangStr);
1828 if (languageAbbreviation) CFRelease(languageAbbreviation);
1829 if (languageName) CFRelease(languageName);
1830 if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier);
1831 if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers);
1832 if (predefinedCanonicalLanguageIdentifiers) CFRelease(predefinedCanonicalLanguageIdentifiers);
1833 if (canonicalLanguageAbbreviation) CFRelease(canonicalLanguageAbbreviation);
1834 CFRelease(cheapStr);
1835 CFRelease(tmpString);
1836 #if READ_DIRECTORIES
1837 CFRelease(contents);
1838 #endif /* READ_DIRECTORIES */
1839 return foundOne;
1840 }
1841
1842 static Boolean CFBundleAllowMixedLocalizations(void) {
1843 static Boolean allowMixed = false, examinedMain = false;
1844 if (!examinedMain) {
1845 CFBundleRef mainBundle = CFBundleGetMainBundle();
1846 CFDictionaryRef infoDict = mainBundle ? CFBundleGetInfoDictionary(mainBundle) : NULL;
1847 CFTypeRef allowMixedValue = infoDict ? CFDictionaryGetValue(infoDict, _kCFBundleAllowMixedLocalizationsKey) : NULL;
1848 if (allowMixedValue) {
1849 CFTypeID typeID = CFGetTypeID(allowMixedValue);
1850 if (typeID == CFBooleanGetTypeID()) {
1851 allowMixed = CFBooleanGetValue((CFBooleanRef)allowMixedValue);
1852 } else if (typeID == CFStringGetTypeID()) {
1853 allowMixed = (CFStringCompare((CFStringRef)allowMixedValue, CFSTR("true"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)allowMixedValue, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo);
1854 } else if (typeID == CFNumberGetTypeID()) {
1855 SInt32 val = 0;
1856 if (CFNumberGetValue((CFNumberRef)allowMixedValue, kCFNumberSInt32Type, &val)) allowMixed = (val != 0);
1857 }
1858 }
1859 examinedMain = true;
1860 }
1861 return allowMixed;
1862 }
1863
1864 static Boolean _CFBundleLocalizationsHaveCommonPrefix(CFStringRef loc1, CFStringRef loc2) {
1865 Boolean result = false;
1866 CFIndex length1 = CFStringGetLength(loc1), length2 = CFStringGetLength(loc2), idx;
1867 if (length1 > 3 && length2 > 3) {
1868 for (idx = 0; idx < length1 && idx < length2; idx++) {
1869 UniChar c1 = CFStringGetCharacterAtIndex(loc1, idx), c2 = CFStringGetCharacterAtIndex(loc2, idx);
1870 if (idx >= 2 && (c1 == '-' || c1 == '_') && (c2 == '-' || c2 == '_')) {
1871 result = true;
1872 break;
1873 } else if (c1 != c2) {
1874 break;
1875 }
1876 }
1877 }
1878 return result;
1879 }
1880
1881 __private_extern__ void _CFBundleAddPreferredLprojNamesInDirectory(CFAllocatorRef alloc, CFURLRef bundleURL, uint8_t version, CFDictionaryRef infoDict, CFMutableArrayRef lprojNames, CFStringRef devLang) {
1882 // This function will add zero, one or two elements to the lprojNames array.
1883 // 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.
1884 // 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.
1885 CFURLRef resourcesURL = _CFBundleCopyResourcesDirectoryURLInDirectory(bundleURL, version);
1886 CFURLRef absoluteURL;
1887 CFIndex idx, startIdx;
1888 CFIndex count;
1889 CFStringRef resourcesPath;
1890 UniChar pathUniChars[CFMaxPathSize];
1891 CFIndex pathLen;
1892 CFStringRef curLangStr, nextLangStr;
1893 Boolean foundOne = false;
1894 CFArrayRef userLanguages;
1895
1896 // Init the one-time-only unichar buffers.
1897 _CFEnsureStaticBuffersInited();
1898
1899 // Get the path to the resources and extract into a buffer.
1900 absoluteURL = CFURLCopyAbsoluteURL(resourcesURL);
1901 resourcesPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
1902 CFRelease(absoluteURL);
1903 pathLen = CFStringGetLength(resourcesPath);
1904 if (pathLen > CFMaxPathSize) pathLen = CFMaxPathSize;
1905 CFStringGetCharacters(resourcesPath, CFRangeMake(0, pathLen), pathUniChars);
1906 CFRelease(resourcesURL);
1907 CFRelease(resourcesPath);
1908
1909 // First check the main bundle.
1910 if (!CFBundleAllowMixedLocalizations()) {
1911 CFBundleRef mainBundle = CFBundleGetMainBundle();
1912 if (mainBundle) {
1913 CFURLRef mainBundleURL = CFBundleCopyBundleURL(mainBundle);
1914 if (mainBundleURL) {
1915 if (!CFEqual(bundleURL, mainBundleURL)) {
1916 // If there is a main bundle, and it isn't this one, try to use the language it prefers.
1917 CFArrayRef mainBundleLangs = _CFBundleGetLanguageSearchList(mainBundle);
1918 if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) {
1919 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0);
1920 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
1921 }
1922 }
1923 CFRelease(mainBundleURL);
1924 }
1925 }
1926 }
1927
1928 if (!foundOne) {
1929 // If we didn't find the main bundle's preferred language, look at the users' prefs again and find the best one.
1930 userLanguages = _CFBundleCopyUserLanguages(true);
1931 count = (userLanguages ? CFArrayGetCount(userLanguages) : 0);
1932 for (idx = 0, startIdx = -1; !foundOne && idx < count; idx++) {
1933 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx);
1934 nextLangStr = (idx + 1 < count) ? (CFStringRef)CFArrayGetValueAtIndex(userLanguages, idx + 1) : NULL;
1935 if (nextLangStr && _CFBundleLocalizationsHaveCommonPrefix(curLangStr, nextLangStr)) {
1936 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, false);
1937 if (startIdx < 0) startIdx = idx;
1938 } else if (startIdx >= 0 && startIdx <= idx) {
1939 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, false);
1940 for (; !foundOne && startIdx <= idx; startIdx++) {
1941 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(userLanguages, startIdx);
1942 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
1943 }
1944 startIdx = -1;
1945 } else {
1946 foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, curLangStr, lprojNames, true);
1947 startIdx = -1;
1948 }
1949 }
1950 // use development region and U.S. English as backstops
1951 if (!foundOne && devLang) foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, devLang, lprojNames, true);
1952 if (!foundOne) foundOne = _CFBundleTryOnePreferredLprojNameInDirectory(kCFAllocatorSystemDefault, pathUniChars, pathLen, version, infoDict, CFSTR("en_US"), lprojNames, true);
1953 if (userLanguages) CFRelease(userLanguages);
1954 }
1955 }
1956
1957 static Boolean _CFBundleTryOnePreferredLprojNameInArray(CFArrayRef array, CFStringRef curLangStr, CFMutableArrayRef lprojNames, Boolean fallBackToLanguage) {
1958 Boolean foundOne = false, specifiesScript = false;
1959 CFRange range = CFRangeMake(0, CFArrayGetCount(array));
1960 CFStringRef altLangStr = NULL, modifiedLangStr = NULL, languageAbbreviation = NULL, languageName = NULL, canonicalLanguageIdentifier = NULL, canonicalLanguageAbbreviation = NULL;
1961 CFMutableDictionaryRef canonicalLanguageIdentifiers = NULL;
1962
1963 if (range.length == 0) return foundOne;
1964 if (CFArrayContainsValue(array, range, curLangStr)) {
1965 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), curLangStr)) CFArrayAppendValue(lprojNames, curLangStr);
1966 foundOne = true;
1967 if (range.length == 1 || CFStringGetLength(curLangStr) <= 2) return foundOne;
1968 }
1969 if (range.length == 1 && CFArrayContainsValue(array, range, CFSTR("default"))) return foundOne;
1970 #if defined(__CONSTANT_CFSTRINGS__)
1971 if (!altLangStr) {
1972 CFIndex idx;
1973 for (idx = 0; !altLangStr && idx < NUM_COMMON_LANGUAGE_NAMES; idx++) {
1974 if (CFEqual(curLangStr, __CFBundleCommonLanguageAbbreviationsArray[idx])) altLangStr = __CFBundleCommonLanguageNamesArray[idx];
1975 else if (CFEqual(curLangStr, __CFBundleCommonLanguageNamesArray[idx])) altLangStr = __CFBundleCommonLanguageAbbreviationsArray[idx];
1976 }
1977 }
1978 #endif /* __CONSTANT_CFSTRINGS__ */
1979 if (foundOne && altLangStr) return foundOne;
1980 if (altLangStr && CFArrayContainsValue(array, range, altLangStr)) {
1981 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), altLangStr)) CFArrayAppendValue(lprojNames, altLangStr);
1982 foundOne = true;
1983 return foundOne;
1984 }
1985 if (!altLangStr && (modifiedLangStr = _CFBundleCopyModifiedLocalization(curLangStr))) {
1986 if (CFArrayContainsValue(array, range, modifiedLangStr)) {
1987 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), modifiedLangStr)) CFArrayAppendValue(lprojNames, modifiedLangStr);
1988 foundOne = true;
1989 }
1990 }
1991 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageAbbreviation = _CFBundleCopyLanguageAbbreviationForLocalization(curLangStr)) && !CFEqual(curLangStr, languageAbbreviation)) {
1992 if (CFArrayContainsValue(array, range, languageAbbreviation)) {
1993 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageAbbreviation)) CFArrayAppendValue(lprojNames, languageAbbreviation);
1994 foundOne = true;
1995 }
1996 }
1997 if (!specifiesScript && (foundOne || fallBackToLanguage) && !altLangStr && (languageName = _CFBundleCopyLanguageNameForLocalization(curLangStr)) && !CFEqual(curLangStr, languageName)) {
1998 if (CFArrayContainsValue(array, range, languageName)) {
1999 if (!CFArrayContainsValue(lprojNames, CFRangeMake(0, CFArrayGetCount(lprojNames)), languageName)) CFArrayAppendValue(lprojNames, languageName);
2000 foundOne = true;
2001 }
2002 }
2003 if (modifiedLangStr) CFRelease(modifiedLangStr);
2004 if (languageAbbreviation) CFRelease(languageAbbreviation);
2005 if (languageName) CFRelease(languageName);
2006 if (canonicalLanguageIdentifier) CFRelease(canonicalLanguageIdentifier);
2007 if (canonicalLanguageIdentifiers) CFRelease(canonicalLanguageIdentifiers);
2008 if (canonicalLanguageAbbreviation) CFRelease(canonicalLanguageAbbreviation);
2009 return foundOne;
2010 }
2011
2012 static CFArrayRef _CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray, Boolean considerMain) {
2013 CFMutableArrayRef lprojNames = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
2014 Boolean foundOne = false, releasePrefArray = false;
2015 CFIndex idx, count, startIdx;
2016
2017 if (considerMain && !CFBundleAllowMixedLocalizations()) {
2018 CFBundleRef mainBundle = CFBundleGetMainBundle();
2019 if (mainBundle) {
2020 // If there is a main bundle, try to use the language it prefers.
2021 CFArrayRef mainBundleLangs = _CFBundleGetLanguageSearchList(mainBundle);
2022 if (mainBundleLangs && (CFArrayGetCount(mainBundleLangs) > 0)) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(mainBundleLangs, 0), lprojNames, true);
2023 }
2024 }
2025
2026 if (!foundOne) {
2027 CFStringRef curLangStr, nextLangStr;
2028 if (!prefArray) {
2029 prefArray = _CFBundleCopyUserLanguages(true);
2030 if (prefArray) releasePrefArray = true;
2031 }
2032 count = (prefArray ? CFArrayGetCount(prefArray) : 0);
2033 for (idx = 0, startIdx = -1; !foundOne && idx < count; idx++) {
2034 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(prefArray, idx);
2035 nextLangStr = (idx + 1 < count) ? (CFStringRef)CFArrayGetValueAtIndex(prefArray, idx + 1) : NULL;
2036 if (nextLangStr && _CFBundleLocalizationsHaveCommonPrefix(curLangStr, nextLangStr)) {
2037 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, false);
2038 if (startIdx < 0) startIdx = idx;
2039 } else if (startIdx >= 0 && startIdx <= idx) {
2040 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, false);
2041 for (; !foundOne && startIdx <= idx; startIdx++) {
2042 curLangStr = (CFStringRef)CFArrayGetValueAtIndex(prefArray, startIdx);
2043 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, true);
2044 }
2045 startIdx = -1;
2046 } else {
2047 foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, curLangStr, lprojNames, true);
2048 startIdx = -1;
2049 }
2050 }
2051 // use U.S. English as backstop
2052 if (!foundOne) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, CFSTR("en_US"), lprojNames, true);
2053 // use random entry as backstop
2054 if (!foundOne && CFArrayGetCount(locArray) > 0) foundOne = _CFBundleTryOnePreferredLprojNameInArray(locArray, (CFStringRef)CFArrayGetValueAtIndex(locArray, 0), lprojNames, true);
2055 }
2056 if (CFArrayGetCount(lprojNames) == 0) {
2057 // Total backstop behavior to avoid having an empty array.
2058 CFArrayAppendValue(lprojNames, CFSTR("en"));
2059 }
2060 if (releasePrefArray) {
2061 CFRelease(prefArray);
2062 }
2063 return lprojNames;
2064 }
2065
2066 CF_EXPORT CFArrayRef CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray, CFArrayRef prefArray) {
2067 return _CFBundleCopyLocalizationsForPreferences(locArray, prefArray, false);
2068 }
2069
2070 CF_EXPORT CFArrayRef CFBundleCopyPreferredLocalizationsFromArray(CFArrayRef locArray) {
2071 return _CFBundleCopyLocalizationsForPreferences(locArray, NULL, true);
2072 }
2073
2074 __private_extern__ CFArrayRef _CFBundleCopyLanguageSearchListInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) {
2075 CFMutableArrayRef langs = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
2076 uint8_t localVersion = 0;
2077 CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, &localVersion);
2078 CFStringRef devLang = NULL;
2079 if (infoDict) devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey);
2080 if (devLang && (CFGetTypeID(devLang) != CFStringGetTypeID() || CFStringGetLength(devLang) == 0)) devLang = NULL;
2081
2082 _CFBundleAddPreferredLprojNamesInDirectory(alloc, url, localVersion, infoDict, langs, devLang);
2083
2084 if (devLang && CFArrayGetFirstIndexOfValue(langs, CFRangeMake(0, CFArrayGetCount(langs)), devLang) < 0) CFArrayAppendValue(langs, devLang);
2085
2086 // Total backstop behavior to avoid having an empty array.
2087 if (CFArrayGetCount(langs) == 0) CFArrayAppendValue(langs, CFSTR("en"));
2088
2089 if (infoDict && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
2090 if (version) *version = localVersion;
2091 return langs;
2092 }
2093
2094 CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) {
2095 Boolean result = false;
2096 CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault, url);
2097 if (bundle) {
2098 result = true;
2099 CFRelease(bundle);
2100 }
2101 return result;
2102 }
2103
2104 // Note that subDirName is expected to be the string for a URL
2105 CF_INLINE Boolean _CFBundleURLHasSubDir(CFURLRef url, CFStringRef subDirName) {
2106 Boolean isDir = false, result = false;
2107 CFURLRef dirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, subDirName, url);
2108 if (dirURL) {
2109 if (_CFIsResourceAtURL(dirURL, &isDir) && isDir) result = true;
2110 CFRelease(dirURL);
2111 }
2112 return result;
2113 }
2114
2115 __private_extern__ Boolean _CFBundleURLLooksLikeBundleVersion(CFURLRef url, uint8_t *version) {
2116 // check for existence of "Resources" or "Contents" or "Support Files"
2117 // but check for the most likely one first
2118 // version 0: old-style "Resources" bundles
2119 // version 1: obsolete "Support Files" bundles
2120 // version 2: modern "Contents" bundles
2121 // version 3: none of the above (see below)
2122 // version 4: not a bundle (for main bundle only)
2123 uint8_t localVersion = 3;
2124 #if READ_DIRECTORIES
2125 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url);
2126 CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2127 CFArrayRef contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
2128 if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) {
2129 if (_CFBundleSortedArrayContains(contents, _CFBundleResourcesDirectoryName)) localVersion = 0;
2130 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName2)) localVersion = 2;
2131 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName1)) localVersion = 1;
2132 } else {
2133 if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName2)) localVersion = 2;
2134 else if (_CFBundleSortedArrayContains(contents, _CFBundleResourcesDirectoryName)) localVersion = 0;
2135 else if (_CFBundleSortedArrayContains(contents, _CFBundleSupportFilesDirectoryName1)) localVersion = 1;
2136 }
2137 CFRelease(contents);
2138 CFRelease(directoryPath);
2139 CFRelease(absoluteURL);
2140 #endif /* READ_DIRECTORIES */
2141 if (localVersion == 3) {
2142 #if DEPLOYMENT_TARGET_EMBEDDED
2143 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
2144 #if DEPLOYMENT_TARGET_WINDOWS
2145 if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/")) || CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework\\"))) {
2146 #else
2147 if (CFStringHasSuffix(CFURLGetString(url), CFSTR(".framework/"))) {
2148 #endif
2149 if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0;
2150 else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2;
2151 else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1;
2152 } else {
2153 if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase2)) localVersion = 2;
2154 else if (_CFBundleURLHasSubDir(url, _CFBundleResourcesURLFromBase0)) localVersion = 0;
2155 else if (_CFBundleURLHasSubDir(url, _CFBundleSupportFilesURLFromBase1)) localVersion = 1;
2156 }
2157 #endif
2158 }
2159 if (version) *version = localVersion;
2160 return (localVersion != 3);
2161 }
2162
2163 static Boolean _isValidPlatformSuffix(CFStringRef suffix) {
2164 for (CFIndex idx = 0; idx < _CFBundleNumberOfPlatforms; idx++) {
2165 if (CFEqual(suffix, _CFBundleSupportedPlatforms[idx])) return true;
2166 }
2167 return false;
2168 }
2169
2170 static Boolean _isValidProductSuffix(CFStringRef suffix) {
2171 for (CFIndex idx = 0; idx < _CFBundleNumberOfProducts; idx++) {
2172 if (CFEqual(suffix, _CFBundleSupportedProducts[idx])) return true;
2173 }
2174 return false;
2175 }
2176
2177 static Boolean _isValidiPhoneOSPlatformProductSuffix(CFStringRef suffix) {
2178 for (CFIndex idx = 0; idx < _CFBundleNumberOfiPhoneOSPlatformProducts; idx++) {
2179 if (CFEqual(suffix, _CFBundleSupportediPhoneOSPlatformProducts[idx])) return true;
2180 }
2181 return false;
2182 }
2183
2184 static Boolean _isValidPlatformAndProductSuffixPair(CFStringRef platform, CFStringRef product) {
2185 if (!platform && !product) return true;
2186 if (!platform) {
2187 return _isValidProductSuffix(product);
2188 }
2189 if (!product) {
2190 return _isValidPlatformSuffix(platform);
2191 }
2192 if (CFEqual(platform, _CFBundleiPhoneOSPlatformName)) {
2193 return _isValidiPhoneOSPlatformProductSuffix(product);
2194 }
2195 return false;
2196 }
2197
2198 static Boolean _isBlacklistedKey(CFStringRef keyName) {
2199 #define _CFBundleNumberOfBlacklistedInfoDictionaryKeys 2
2200 static const CFStringRef _CFBundleBlacklistedInfoDictionaryKeys[_CFBundleNumberOfBlacklistedInfoDictionaryKeys] = { CFSTR("CFBundleExecutable"), CFSTR("CFBundleIdentifier") };
2201
2202 for (CFIndex idx = 0; idx < _CFBundleNumberOfBlacklistedInfoDictionaryKeys; idx++) {
2203 if (CFEqual(keyName, _CFBundleBlacklistedInfoDictionaryKeys[idx])) return true;
2204 }
2205 return false;
2206 }
2207
2208 static Boolean _isOverrideKey(CFStringRef fullKey, CFStringRef *outBaseKey, CFStringRef *outPlatformSuffix, CFStringRef *outProductSuffix) {
2209 if (outBaseKey) {
2210 *outBaseKey = NULL;
2211 }
2212 if (outPlatformSuffix) {
2213 *outPlatformSuffix = NULL;
2214 }
2215 if (outProductSuffix) {
2216 *outProductSuffix = NULL;
2217 }
2218 if (!fullKey)
2219 return false;
2220 CFRange minusRange = CFStringFind(fullKey, CFSTR("-"), kCFCompareBackwards);
2221 CFRange tildeRange = CFStringFind(fullKey, CFSTR("~"), kCFCompareBackwards);
2222 if (minusRange.location == kCFNotFound && tildeRange.location == kCFNotFound) return false;
2223 // minus must come before tilde if both are present
2224 if (minusRange.location != kCFNotFound && tildeRange.location != kCFNotFound && tildeRange.location <= minusRange.location) return false;
2225
2226 CFIndex strLen = CFStringGetLength(fullKey);
2227 CFRange baseKeyRange = (minusRange.location != kCFNotFound) ? CFRangeMake(0, minusRange.location) : CFRangeMake(0, tildeRange.location);
2228 CFRange platformRange = CFRangeMake(kCFNotFound, 0);
2229 CFRange productRange = CFRangeMake(kCFNotFound, 0);
2230 if (minusRange.location != kCFNotFound) {
2231 platformRange.location = minusRange.location + minusRange.length;
2232 platformRange.length = ((tildeRange.location != kCFNotFound) ? tildeRange.location : strLen) - platformRange.location;
2233 }
2234 if (tildeRange.location != kCFNotFound) {
2235 productRange.location = tildeRange.location + tildeRange.length;
2236 productRange.length = strLen - productRange.location;
2237 }
2238 if (baseKeyRange.length < 1) return false;
2239 if (platformRange.location != kCFNotFound && platformRange.length < 1) return false;
2240 if (productRange.location != kCFNotFound && productRange.length < 1) return false;
2241
2242 CFStringRef platform = (platformRange.location != kCFNotFound) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefaultGCRefZero, fullKey, platformRange) : NULL;
2243 CFStringRef product = (productRange.location != kCFNotFound) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefaultGCRefZero, fullKey, productRange) : NULL;
2244 Boolean result = _isValidPlatformAndProductSuffixPair(platform, product);
2245
2246 if (result) {
2247 if (outBaseKey) {
2248 *outBaseKey = CFStringCreateWithSubstring(kCFAllocatorSystemDefaultGCRefZero, fullKey, baseKeyRange);
2249 }
2250 if (outPlatformSuffix) {
2251 *outPlatformSuffix = platform;
2252 } else {
2253 if (platform && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(platform);
2254 }
2255 if (outProductSuffix) {
2256 *outProductSuffix = product;
2257 } else {
2258 if (product && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(product);
2259 }
2260 } else {
2261 if (platform && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(platform);
2262 if (product && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(product);
2263 }
2264 return result;
2265 }
2266
2267 static Boolean _isCurrentPlatformAndProduct(CFStringRef platform, CFStringRef product) {
2268 if (!platform && !product) return true;
2269 if (!platform) {
2270 return CFEqual(_CFGetProductName(), product);
2271 }
2272 if (!product) {
2273 return CFEqual(_CFGetPlatformName(), platform);
2274 }
2275
2276 return CFEqual(_CFGetProductName(), product) && CFEqual(_CFGetPlatformName(), platform);
2277 }
2278
2279 static CFArrayRef _CopySortedOverridesForBaseKey(CFStringRef keyName, CFDictionaryRef dict) {
2280 CFMutableArrayRef overrides = CFArrayCreateMutable(kCFAllocatorSystemDefaultGCRefZero, 0, &kCFTypeArrayCallBacks);
2281 CFStringRef keyNameWithBoth = CFStringCreateWithFormat(kCFAllocatorSystemDefaultGCRefZero, NULL, CFSTR("%@-%@~%@"), keyName, _CFGetPlatformName(), _CFGetProductName());
2282 CFStringRef keyNameWithProduct = CFStringCreateWithFormat(kCFAllocatorSystemDefaultGCRefZero, NULL, CFSTR("%@~%@"), keyName, _CFGetProductName());
2283 CFStringRef keyNameWithPlatform = CFStringCreateWithFormat(kCFAllocatorSystemDefaultGCRefZero, NULL, CFSTR("%@-%@"), keyName, _CFGetPlatformName());
2284
2285 CFIndex count = CFDictionaryGetCount(dict);
2286
2287 if (count > 0) {
2288 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), 0);
2289 CFTypeRef *values = &(keys[count]);
2290
2291 CFDictionaryGetKeysAndValues(dict, keys, values);
2292 for (CFIndex idx = 0; idx < count; idx++) {
2293 if (CFEqual(keys[idx], keyNameWithBoth)) {
2294 CFArrayAppendValue(overrides, keys[idx]);
2295 break;
2296 }
2297 }
2298 for (CFIndex idx = 0; idx < count; idx++) {
2299 if (CFEqual(keys[idx], keyNameWithProduct)) {
2300 CFArrayAppendValue(overrides, keys[idx]);
2301 break;
2302 }
2303 }
2304 for (CFIndex idx = 0; idx < count; idx++) {
2305 if (CFEqual(keys[idx], keyNameWithPlatform)) {
2306 CFArrayAppendValue(overrides, keys[idx]);
2307 break;
2308 }
2309 }
2310 for (CFIndex idx = 0; idx < count; idx++) {
2311 if (CFEqual(keys[idx], keyName)) {
2312 CFArrayAppendValue(overrides, keys[idx]);
2313 break;
2314 }
2315 }
2316
2317 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2318 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, keys);
2319 }
2320 }
2321
2322 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2323 CFRelease(keyNameWithProduct);
2324 CFRelease(keyNameWithPlatform);
2325 CFRelease(keyNameWithBoth);
2326 }
2327
2328 return overrides;
2329 }
2330
2331 __private_extern__ void _processInfoDictionary(CFMutableDictionaryRef dict, CFStringRef platformSuffix, CFStringRef productSuffix) {
2332 CFIndex count = CFDictionaryGetCount(dict);
2333
2334 if (count > 0) {
2335 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefaultGCRefZero, 2 * count * sizeof(CFTypeRef), 0);
2336 CFTypeRef *values = &(keys[count]);
2337 CFMutableArrayRef guard = CFArrayCreateMutable(kCFAllocatorSystemDefaultGCRefZero, 0, &kCFTypeArrayCallBacks);
2338
2339 CFDictionaryGetKeysAndValues(dict, keys, values);
2340 for (CFIndex idx = 0; idx < count; idx++) {
2341 CFStringRef keyPlatformSuffix, keyProductSuffix, keyName;
2342 if (_isOverrideKey((CFStringRef)keys[idx], &keyName, &keyPlatformSuffix, &keyProductSuffix)) {
2343 CFArrayRef keysForBaseKey = NULL;
2344 if (_isCurrentPlatformAndProduct(keyPlatformSuffix, keyProductSuffix) && !_isBlacklistedKey(keyName) && CFDictionaryContainsKey(dict, keys[idx])) {
2345 keysForBaseKey = _CopySortedOverridesForBaseKey(keyName, dict);
2346 CFIndex keysForBaseKeyCount = CFArrayGetCount(keysForBaseKey);
2347
2348 //make sure the other keys for this base key don't get released out from under us until we're done
2349 CFArrayAppendValue(guard, keysForBaseKey);
2350
2351 //the winner for this base key will be sorted to the front, do the override with it
2352 CFTypeRef highestPriorityKey = CFArrayGetValueAtIndex(keysForBaseKey, 0);
2353 CFDictionarySetValue(dict, keyName, CFDictionaryGetValue(dict, highestPriorityKey));
2354
2355 //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
2356 for (CFIndex presentKeysIdx = 0; presentKeysIdx < keysForBaseKeyCount; presentKeysIdx++) {
2357 CFStringRef currentKey = (CFStringRef)CFArrayGetValueAtIndex(keysForBaseKey, presentKeysIdx);
2358 if (!CFEqual(currentKey, keyName))
2359 CFDictionaryRemoveValue(dict, currentKey);
2360 }
2361 } else {
2362 CFDictionaryRemoveValue(dict, keys[idx]);
2363 }
2364
2365
2366 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2367 if (keyPlatformSuffix) CFRelease(keyPlatformSuffix);
2368 if (keyProductSuffix) CFRelease(keyProductSuffix);
2369 CFRelease(keyName);
2370 if (keysForBaseKey) CFRelease(keysForBaseKey);
2371 }
2372 }
2373 }
2374
2375 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) {
2376 CFAllocatorDeallocate(kCFAllocatorSystemDefaultGCRefZero, keys);
2377 CFRelease(guard);
2378 }
2379 }
2380 }
2381
2382 // returns zero-ref dictionary under GC if given kCFAllocatorSystemDefaultGCRefZero
2383 __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) {
2384 CFDictionaryRef dict = NULL;
2385 unsigned char buff[CFMaxPathSize];
2386 uint8_t localVersion = 0;
2387
2388 if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) {
2389 CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
2390 if (!newURL) newURL = (CFURLRef)CFRetain(url);
2391
2392 // version 3 is for flattened pseudo-bundles with no Contents, Support Files, or Resources directories
2393 if (!_CFBundleURLLooksLikeBundleVersion(newURL, &localVersion)) localVersion = 3;
2394
2395 dict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc, newURL, localVersion);
2396 CFRelease(newURL);
2397 }
2398 if (version) *version = localVersion;
2399 return dict;
2400 }
2401
2402 // returns zero-ref dictionary under GC if given kCFAllocatorSystemDefaultGCRefZero
2403 __private_extern__ CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, uint8_t version) {
2404 CFDictionaryRef result = NULL;
2405 if (url) {
2406 CFURLRef infoURL = NULL, rawInfoURL = NULL;
2407 CFDataRef infoData = NULL;
2408 UniChar buff[CFMaxPathSize];
2409 CFIndex len;
2410 CFMutableStringRef cheapStr;
2411 CFStringRef infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0, infoURLFromBase = _CFBundleInfoURLFromBase0;
2412 Boolean tryPlatformSpecific = true, tryGlobal = true;
2413 #if READ_DIRECTORIES
2414 CFURLRef directoryURL = NULL, absoluteURL;
2415 CFStringRef directoryPath;
2416 CFArrayRef contents = NULL;
2417 CFRange contentsRange = CFRangeMake(0, 0);
2418 #endif /* READ_DIRECTORIES */
2419
2420 _CFEnsureStaticBuffersInited();
2421
2422 if (0 == version) {
2423 #if READ_DIRECTORIES
2424 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, url);
2425 #endif /* READ_DIRECTORIES */
2426 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension0;
2427 infoURLFromBase = _CFBundleInfoURLFromBase0;
2428 } else if (1 == version) {
2429 #if READ_DIRECTORIES
2430 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, url);
2431 #endif /* READ_DIRECTORIES */
2432 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension1;
2433 infoURLFromBase = _CFBundleInfoURLFromBase1;
2434 } else if (2 == version) {
2435 #if READ_DIRECTORIES
2436 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, url);
2437 #endif /* READ_DIRECTORIES */
2438 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension2;
2439 infoURLFromBase = _CFBundleInfoURLFromBase2;
2440 } else if (3 == version) {
2441 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
2442 // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle
2443 if (path) {
2444 if (!(CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(path, _CFBundleResourcesDirectoryName))) {
2445 #if READ_DIRECTORIES
2446 directoryURL = CFRetain(url);
2447 #endif /* READ_DIRECTORIES */
2448 infoURLFromBaseNoExtension = _CFBundleInfoURLFromBaseNoExtension3;
2449 infoURLFromBase = _CFBundleInfoURLFromBase3;
2450 }
2451 CFRelease(path);
2452 }
2453 }
2454 #if READ_DIRECTORIES
2455 if (directoryURL) {
2456 absoluteURL = CFURLCopyAbsoluteURL(directoryURL);
2457 directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2458 contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
2459 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
2460 CFRelease(directoryPath);
2461 CFRelease(absoluteURL);
2462 CFRelease(directoryURL);
2463 }
2464 #endif /* READ_DIRECTORIES */
2465
2466 len = CFStringGetLength(infoURLFromBaseNoExtension);
2467 CFStringGetCharacters(infoURLFromBaseNoExtension, CFRangeMake(0, len), buff);
2468 buff[len++] = (UniChar)'-';
2469 memmove(buff + len, _PlatformUniChars, _PlatformLen * sizeof(UniChar));
2470 len += _PlatformLen;
2471 _CFAppendPathExtension(buff, &len, CFMaxPathSize, _InfoExtensionUniChars, _InfoExtensionLen);
2472 cheapStr = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
2473 CFStringAppendCharacters(cheapStr, buff, len);
2474 infoURL = CFURLCreateWithString(kCFAllocatorSystemDefault, cheapStr, url);
2475 #if READ_DIRECTORIES
2476 if (contents) {
2477 CFIndex resourcesLen, idx;
2478 for (resourcesLen = len; resourcesLen > 0; resourcesLen--) if (buff[resourcesLen - 1] == '/') break;
2479 CFStringDelete(cheapStr, CFRangeMake(0, CFStringGetLength(cheapStr)));
2480 CFStringAppendCharacters(cheapStr, buff + resourcesLen, len - resourcesLen);
2481 for (tryPlatformSpecific = false, idx = 0; !tryPlatformSpecific && idx < contentsRange.length; idx++) {
2482 // Need to do this case-insensitive to accommodate Palm
2483 if (kCFCompareEqualTo == CFStringCompare(cheapStr, CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryPlatformSpecific = true;
2484 }
2485 }
2486 #endif /* READ_DIRECTORIES */
2487 if (tryPlatformSpecific) CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoURL, &infoData, NULL, NULL, NULL);
2488 //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryPlatformSpecific ? "missed it\n" : "skipped it\n"));
2489 CFRelease(cheapStr);
2490 if (!infoData) {
2491 // Check for global Info.plist
2492 CFRelease(infoURL);
2493 infoURL = CFURLCreateWithString(kCFAllocatorSystemDefault, infoURLFromBase, url);
2494 #if READ_DIRECTORIES
2495 if (contents) {
2496 CFIndex idx;
2497 for (tryGlobal = false, idx = 0; !tryGlobal && idx < contentsRange.length; idx++) {
2498 // Need to do this case-insensitive to accommodate Palm
2499 if (kCFCompareEqualTo == CFStringCompare(_CFBundleInfoFileName, CFArrayGetValueAtIndex(contents, idx), kCFCompareCaseInsensitive)) tryGlobal = true;
2500 }
2501 }
2502 #endif /* READ_DIRECTORIES */
2503 if (tryGlobal) CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoURL, &infoData, NULL, NULL, NULL);
2504 //fprintf(stderr, "looking for ");CFShow(infoURL);fprintf(stderr, infoData ? "found it\n" : (tryGlobal ? "missed it\n" : "skipped it\n"));
2505 }
2506
2507 if (infoData) {
2508 result = (CFDictionaryRef)CFPropertyListCreateFromXMLData(alloc, infoData, kCFPropertyListMutableContainers, NULL);
2509 if (result) {
2510 if (CFDictionaryGetTypeID() == CFGetTypeID(result)) {
2511 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, infoURL);
2512 } else {
2513 if (!_CFAllocatorIsGCRefZero(alloc)) CFRelease(result);
2514 result = NULL;
2515 }
2516 }
2517 if (!result) rawInfoURL = infoURL;
2518 CFRelease(infoData);
2519 }
2520 if (!result) {
2521 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2522 if (rawInfoURL) CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleRawInfoPlistURLKey, rawInfoURL);
2523 }
2524
2525 CFRelease(infoURL);
2526 #if READ_DIRECTORIES
2527 if (contents) CFRelease(contents);
2528 #endif /* READ_DIRECTORIES */
2529 }
2530 _processInfoDictionary((CFMutableDictionaryRef)result, _CFGetPlatformName(), _CFGetProductName());
2531 return result;
2532 }
2533
2534 static Boolean _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc, CFURLRef url, CFDictionaryRef infoDict, UInt32 *packageType, UInt32 *packageCreator) {
2535 Boolean retVal = false, hasType = false, hasCreator = false, releaseInfoDict = false;
2536 CFURLRef tempURL;
2537 CFDataRef pkgInfoData = NULL;
2538
2539 // Check for a "real" new bundle
2540 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase2, url);
2541 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
2542 CFRelease(tempURL);
2543 if (!pkgInfoData) {
2544 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase1, url);
2545 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
2546 CFRelease(tempURL);
2547 }
2548 if (!pkgInfoData) {
2549 // Check for a "pseudo" new bundle
2550 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePseudoPkgInfoURLFromBase, url);
2551 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
2552 CFRelease(tempURL);
2553 }
2554
2555 // 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.
2556 // 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.
2557 // 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.
2558
2559 if (pkgInfoData && CFDataGetLength(pkgInfoData) >= (int)(sizeof(UInt32) * 2)) {
2560 UInt32 *pkgInfo = (UInt32 *)CFDataGetBytePtr(pkgInfoData);
2561 if (packageType) *packageType = CFSwapInt32BigToHost(pkgInfo[0]);
2562 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(pkgInfo[1]);
2563 retVal = hasType = hasCreator = true;
2564 }
2565 if (pkgInfoData) CFRelease(pkgInfoData);
2566 if (!retVal) {
2567 if (!infoDict) {
2568 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, NULL);
2569 releaseInfoDict = true;
2570 }
2571 if (infoDict) {
2572 CFStringRef typeString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleSignatureKey);
2573 UInt32 tmp;
2574 CFIndex usedBufLen = 0;
2575 if (typeString && CFGetTypeID(typeString) == CFStringGetTypeID() && CFStringGetLength(typeString) == 4 && 4 == CFStringGetBytes(typeString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) {
2576 if (packageType) *packageType = CFSwapInt32BigToHost(tmp);
2577 retVal = hasType = true;
2578 }
2579 if (creatorString && CFGetTypeID(creatorString) == CFStringGetTypeID() && CFStringGetLength(creatorString) == 4 && 4 == CFStringGetBytes(creatorString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) {
2580 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(tmp);
2581 retVal = hasCreator = true;
2582 }
2583 if (releaseInfoDict && !_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
2584 }
2585 }
2586 if (!hasType || !hasCreator) {
2587 // If this looks like a bundle then manufacture the type and creator.
2588 if (retVal || _CFBundleURLLooksLikeBundle(url)) {
2589 if (packageCreator && !hasCreator) *packageCreator = 0x3f3f3f3f; // '????'
2590 if (packageType && !hasType) {
2591 CFStringRef urlStr;
2592 UniChar buff[CFMaxPathSize];
2593 CFIndex strLen, startOfExtension;
2594 CFURLRef absoluteURL;
2595
2596 // Detect "app", "debug", "profile", or "framework" extensions
2597 absoluteURL = CFURLCopyAbsoluteURL(url);
2598 urlStr = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2599 CFRelease(absoluteURL);
2600 strLen = CFStringGetLength(urlStr);
2601 if (strLen > CFMaxPathSize) strLen = CFMaxPathSize;
2602 CFStringGetCharacters(urlStr, CFRangeMake(0, strLen), buff);
2603 CFRelease(urlStr);
2604 startOfExtension = _CFStartOfPathExtension(buff, strLen);
2605 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)'/')) {
2606 // This is an app
2607 *packageType = 0x4150504c; // 'APPL'
2608 } 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)'/')) {
2609 // This is an app (debug version)
2610 *packageType = 0x4150504c; // 'APPL'
2611 } 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)'/')) {
2612 // This is an app (profile version)
2613 *packageType = 0x4150504c; // 'APPL'
2614 } 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)'/')) {
2615 // This is a service
2616 *packageType = 0x4150504c; // 'APPL'
2617 } 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)'/')) {
2618 // This is a framework
2619 *packageType = 0x464d574b; // 'FMWK'
2620 } else {
2621 // Default to BNDL for generic bundle
2622 *packageType = 0x424e444c; // 'BNDL'
2623 }
2624 }
2625 retVal = true;
2626 }
2627 }
2628 return retVal;
2629 }
2630
2631 CF_EXPORT Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
2632 return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc, url, NULL, packageType, packageCreator);
2633 }
2634
2635 CF_EXPORT void CFBundleGetPackageInfo(CFBundleRef bundle, UInt32 *packageType, UInt32 *packageCreator) {
2636 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle);
2637 if (!_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(kCFAllocatorSystemDefault, bundleURL, CFBundleGetInfoDictionary(bundle), packageType, packageCreator)) {
2638 if (packageType) *packageType = 0x424e444c; // 'BNDL'
2639 if (packageCreator) *packageCreator = 0x3f3f3f3f; // '????'
2640 }
2641 if (bundleURL) CFRelease(bundleURL);
2642 }
2643
2644 CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
2645 return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault, url, packageType, packageCreator);
2646 }
2647
2648 static void _CFBundleCheckSupportedPlatform(CFMutableArrayRef mutableArray, UniChar *buff, CFIndex startLen, CFStringRef platformName, CFStringRef platformIdentifier) {
2649 CFIndex buffLen = startLen, platformLen = CFStringGetLength(platformName), extLen = CFStringGetLength(_CFBundleInfoExtension);
2650 CFMutableStringRef str;
2651 Boolean isDir;
2652 if (buffLen + platformLen + extLen < CFMaxPathSize) {
2653 CFStringGetCharacters(platformName, CFRangeMake(0, platformLen), buff + buffLen);
2654 buffLen += platformLen;
2655 buff[buffLen++] = (UniChar)'.';
2656 CFStringGetCharacters(_CFBundleInfoExtension, CFRangeMake(0, extLen), buff + buffLen);
2657 buffLen += extLen;
2658 str = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
2659 CFStringAppendCharacters(str, buff, buffLen);
2660 if (_CFIsResourceAtPath(str, &isDir) && !isDir && CFArrayGetFirstIndexOfValue(mutableArray, CFRangeMake(0, CFArrayGetCount(mutableArray)), platformIdentifier) < 0) CFArrayAppendValue(mutableArray, platformIdentifier);
2661 CFRelease(str);
2662 }
2663 }
2664
2665 CF_EXPORT CFArrayRef _CFBundleGetSupportedPlatforms(CFBundleRef bundle) {
2666 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
2667 CFArrayRef platformArray = infoDict ? (CFArrayRef)CFDictionaryGetValue(infoDict, _kCFBundleSupportedPlatformsKey) : NULL;
2668 if (platformArray && CFGetTypeID(platformArray) != CFArrayGetTypeID()) {
2669 platformArray = NULL;
2670 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleSupportedPlatformsKey);
2671 }
2672 if (!platformArray) {
2673 CFURLRef infoPlistURL = infoDict ? (CFURLRef)CFDictionaryGetValue(infoDict, _kCFBundleInfoPlistURLKey) : NULL, absoluteURL;
2674 CFStringRef infoPlistPath;
2675 UniChar buff[CFMaxPathSize];
2676 CFIndex buffLen, infoLen = CFStringGetLength(_CFBundleInfoURLFromBaseNoExtension3), startLen, extLen = CFStringGetLength(_CFBundleInfoExtension);
2677 if (infoPlistURL) {
2678 CFMutableArrayRef mutableArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
2679 absoluteURL = CFURLCopyAbsoluteURL(infoPlistURL);
2680 infoPlistPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2681 CFRelease(absoluteURL);
2682 buffLen = CFStringGetLength(infoPlistPath);
2683 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize;
2684 CFStringGetCharacters(infoPlistPath, CFRangeMake(0, buffLen), buff);
2685 CFRelease(infoPlistPath);
2686 if (buffLen > 0) {
2687 buffLen = _CFStartOfLastPathComponent(buff, buffLen);
2688 if (buffLen > 0 && buffLen + infoLen + extLen < CFMaxPathSize) {
2689 CFStringGetCharacters(_CFBundleInfoURLFromBaseNoExtension3, CFRangeMake(0, infoLen), buff + buffLen);
2690 buffLen += infoLen;
2691 buff[buffLen++] = (UniChar)'-';
2692 startLen = buffLen;
2693 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("macos"), CFSTR("MacOS"));
2694 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("macosx"), CFSTR("MacOS"));
2695 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("iphoneos"), CFSTR("iPhoneOS"));
2696 _CFBundleCheckSupportedPlatform(mutableArray, buff, startLen, CFSTR("windows"), CFSTR("Windows"));
2697 }
2698 }
2699 if (CFArrayGetCount(mutableArray) > 0) {
2700 platformArray = (CFArrayRef)mutableArray;
2701 CFDictionarySetValue((CFMutableDictionaryRef)infoDict, _kCFBundleSupportedPlatformsKey, platformArray);
2702 }
2703 CFRelease(mutableArray);
2704 }
2705 }
2706 return platformArray;
2707 }
2708
2709 CF_EXPORT CFStringRef _CFBundleGetCurrentPlatform(void) {
2710 #if DEPLOYMENT_TARGET_MACOSX
2711 return CFSTR("MacOS");
2712 #elif DEPLOYMENT_TARGET_EMBEDDED
2713 return CFSTR("iPhoneOS");
2714 #elif DEPLOYMENT_TARGET_WINDOWS
2715 return CFSTR("Windows");
2716 #elif DEPLOYMENT_TARGET_SOLARIS
2717 return CFSTR("Solaris");
2718 #elif DEPLOYMENT_TARGET_HPUX
2719 return CFSTR("HPUX");
2720 #elif DEPLOYMENT_TARGET_LINUX
2721 return CFSTR("Linux");
2722 #elif DEPLOYMENT_TARGET_FREEBSD
2723 return CFSTR("FreeBSD");
2724 #else
2725 #error Unknown or unspecified DEPLOYMENT_TARGET
2726 #endif
2727 }
2728
2729 __private_extern__ CFStringRef _CFBundleGetPlatformExecutablesSubdirectoryName(void) {
2730 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2731 return CFSTR("MacOS");
2732 #elif DEPLOYMENT_TARGET_WINDOWS
2733 return CFSTR("Windows");
2734 #elif DEPLOYMENT_TARGET_SOLARIS
2735 return CFSTR("Solaris");
2736 #elif DEPLOYMENT_TARGET_HPUX
2737 return CFSTR("HPUX");
2738 #elif DEPLOYMENT_TARGET_LINUX
2739 return CFSTR("Linux");
2740 #elif DEPLOYMENT_TARGET_FREEBSD
2741 return CFSTR("FreeBSD");
2742 #else
2743 #error Unknown or unspecified DEPLOYMENT_TARGET
2744 #endif
2745 }
2746
2747 __private_extern__ CFStringRef _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(void) {
2748 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2749 return CFSTR("Mac OS X");
2750 #elif DEPLOYMENT_TARGET_WINDOWS
2751 return CFSTR("WinNT");
2752 #elif DEPLOYMENT_TARGET_SOLARIS
2753 return CFSTR("Solaris");
2754 #elif DEPLOYMENT_TARGET_HPUX
2755 return CFSTR("HP-UX");
2756 #elif DEPLOYMENT_TARGET_LINUX
2757 return CFSTR("Linux");
2758 #elif DEPLOYMENT_TARGET_FREEBSD
2759 return CFSTR("FreeBSD");
2760 #else
2761 #error Unknown or unspecified DEPLOYMENT_TARGET
2762 #endif
2763 }
2764
2765 __private_extern__ CFStringRef _CFBundleGetOtherPlatformExecutablesSubdirectoryName(void) {
2766 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2767 return CFSTR("MacOSClassic");
2768 #elif DEPLOYMENT_TARGET_WINDOWS
2769 return CFSTR("Other");
2770 #elif DEPLOYMENT_TARGET_HPUX
2771 return CFSTR("Other");
2772 #elif DEPLOYMENT_TARGET_SOLARIS
2773 return CFSTR("Other");
2774 #elif DEPLOYMENT_TARGET_LINUX
2775 return CFSTR("Other");
2776 #elif DEPLOYMENT_TARGET_FREEBSD
2777 return CFSTR("Other");
2778 #else
2779 #error Unknown or unspecified DEPLOYMENT_TARGET
2780 #endif
2781 }
2782
2783 __private_extern__ CFStringRef _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(void) {
2784 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
2785 return CFSTR("Mac OS 8");
2786 #elif DEPLOYMENT_TARGET_WINDOWS
2787 return CFSTR("Other");
2788 #elif DEPLOYMENT_TARGET_HPUX
2789 return CFSTR("Other");
2790 #elif DEPLOYMENT_TARGET_SOLARIS
2791 return CFSTR("Other");
2792 #elif DEPLOYMENT_TARGET_LINUX
2793 return CFSTR("Other");
2794 #elif DEPLOYMENT_TARGET_FREEBSD
2795 return CFSTR("Other");
2796 #else
2797 #error Unknown or unspecified DEPLOYMENT_TARGET
2798 #endif
2799 }
2800
2801 __private_extern__ CFArrayRef _CFBundleCopyBundleRegionsArray(CFBundleRef bundle) {
2802 return CFBundleCopyBundleLocalizations(bundle);
2803 }
2804
2805 CF_EXPORT CFArrayRef CFBundleCopyBundleLocalizations(CFBundleRef bundle) {
2806 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
2807 CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle);
2808 #if READ_DIRECTORIES
2809 CFURLRef absoluteURL;
2810 CFStringRef directoryPath;
2811 CFArrayRef contents;
2812 CFRange contentsRange;
2813 CFIndex idx;
2814 #else /* READ_DIRECTORIES */
2815 CFArrayRef urls = ((_CFBundleLayoutVersion(bundle) != 4) ? _CFContentsOfDirectory(CFGetAllocator(bundle), NULL, NULL, resourcesURL, _CFBundleLprojExtension) : NULL);
2816 #endif /* READ_DIRECTORIES */
2817 CFArrayRef predefinedLocalizations = NULL;
2818 CFMutableArrayRef result = NULL;
2819
2820 if (infoDict) {
2821 predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
2822 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) != CFArrayGetTypeID()) {
2823 predefinedLocalizations = NULL;
2824 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, kCFBundleLocalizationsKey);
2825 }
2826 if (predefinedLocalizations) {
2827 CFIndex i, c = CFArrayGetCount(predefinedLocalizations);
2828 if (c > 0 && !result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2829 for (i = 0; i < c; i++) CFArrayAppendValue(result, CFArrayGetValueAtIndex(predefinedLocalizations, i));
2830 }
2831 }
2832
2833 #if READ_DIRECTORIES
2834 if (resourcesURL) {
2835 absoluteURL = CFURLCopyAbsoluteURL(resourcesURL);
2836 directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
2837 contents = _CFBundleCopySortedDirectoryContentsAtPath(directoryPath, _CFBundleAllContents);
2838 contentsRange = CFRangeMake(0, CFArrayGetCount(contents));
2839 for (idx = 0; idx < contentsRange.length; idx++) {
2840 CFStringRef name = CFArrayGetValueAtIndex(contents, idx);
2841 if (CFStringHasSuffix(name, _CFBundleLprojExtensionWithDot)) {
2842 CFStringRef localization = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, name, CFRangeMake(0, CFStringGetLength(name) - 6));
2843 if (!result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2844 CFArrayAppendValue(result, localization);
2845 CFRelease(localization);
2846 }
2847 }
2848 CFRelease(contents);
2849 CFRelease(directoryPath);
2850 CFRelease(absoluteURL);
2851 }
2852 #else /* READ_DIRECTORIES */
2853 if (urls) {
2854 CFIndex i, c = CFArrayGetCount(urls);
2855 CFURLRef curURL, curAbsoluteURL;
2856 CFStringRef curStr, regionStr;
2857 UniChar buff[CFMaxPathSize];
2858 CFIndex strLen, startOfLastPathComponent, regionLen;
2859
2860 if (c > 0 && !result) result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2861 for (i = 0; i < c; i++) {
2862 curURL = (CFURLRef)CFArrayGetValueAtIndex(urls, i);
2863 curAbsoluteURL = CFURLCopyAbsoluteURL(curURL);
2864 curStr = CFURLCopyFileSystemPath(curAbsoluteURL, PLATFORM_PATH_STYLE);
2865 CFRelease(curAbsoluteURL);
2866 strLen = CFStringGetLength(curStr);
2867 if (strLen > CFMaxPathSize) strLen = CFMaxPathSize;
2868 CFStringGetCharacters(curStr, CFRangeMake(0, strLen), buff);
2869
2870 startOfLastPathComponent = _CFStartOfLastPathComponent(buff, strLen);
2871 regionLen = _CFLengthAfterDeletingPathExtension(&(buff[startOfLastPathComponent]), strLen - startOfLastPathComponent);
2872 regionStr = CFStringCreateWithCharacters(CFGetAllocator(bundle), &(buff[startOfLastPathComponent]), regionLen);
2873 CFArrayAppendValue(result, regionStr);
2874 CFRelease(regionStr);
2875 CFRelease(curStr);
2876 }
2877 CFRelease(urls);
2878 }
2879 #endif /* READ_DIRECTORIES */
2880
2881 if (!result) {
2882 CFStringRef developmentLocalization = CFBundleGetDevelopmentRegion(bundle);
2883 if (developmentLocalization) {
2884 result = CFArrayCreateMutable(CFGetAllocator(bundle), 0, &kCFTypeArrayCallBacks);
2885 CFArrayAppendValue(result, developmentLocalization);
2886 }
2887 }
2888 if (resourcesURL) CFRelease(resourcesURL);
2889 return result;
2890 }
2891
2892
2893 CF_EXPORT CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) {
2894 CFDictionaryRef result = NULL;
2895 Boolean isDir = false;
2896 if (_CFIsResourceAtURL(url, &isDir)) {
2897 if (isDir) {
2898 result = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefaultGCRefZero, url, NULL);
2899 } else {
2900 result = _CFBundleCopyInfoDictionaryInExecutable(url); // return zero-ref dictionary under GC
2901 }
2902 }
2903 if (result && _CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRetain(result); // conditionally put on a retain for a Copy function
2904 return result;
2905 }
2906
2907 CFArrayRef CFBundleCopyExecutableArchitecturesForURL(CFURLRef url) {
2908 CFArrayRef result = NULL;
2909 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
2910 if (bundle) {
2911 result = CFBundleCopyExecutableArchitectures(bundle);
2912 CFRelease(bundle);
2913 } else {
2914 result = _CFBundleCopyArchitecturesForExecutable(url);
2915 }
2916 return result;
2917 }
2918
2919 CFArrayRef CFBundleCopyLocalizationsForURL(CFURLRef url) {
2920 CFArrayRef result = NULL;
2921 CFBundleRef bundle = CFBundleCreate(kCFAllocatorSystemDefault, url);
2922 CFStringRef devLang = NULL;
2923 if (bundle) {
2924 result = CFBundleCopyBundleLocalizations(bundle);
2925 CFRelease(bundle);
2926 } else {
2927 CFDictionaryRef infoDict = _CFBundleCopyInfoDictionaryInExecutable(url); // return zero-ref dictionary under GC
2928 if (infoDict) {
2929 CFArrayRef predefinedLocalizations = (CFArrayRef)CFDictionaryGetValue(infoDict, kCFBundleLocalizationsKey);
2930 if (predefinedLocalizations && CFGetTypeID(predefinedLocalizations) == CFArrayGetTypeID()) result = (CFArrayRef)CFRetain(predefinedLocalizations);
2931 if (!result) {
2932 devLang = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey);
2933 if (devLang && (CFGetTypeID(devLang) == CFStringGetTypeID() && CFStringGetLength(devLang) > 0)) result = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)&devLang, 1, &kCFTypeArrayCallBacks);
2934 }
2935 if (!_CFAllocatorIsGCRefZero(kCFAllocatorSystemDefaultGCRefZero)) CFRelease(infoDict);
2936 }
2937 }
2938 return result;
2939 }