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