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