2 * Copyright (c) 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 /* CFBundle_Resources.c
24 Copyright (c) 1999-2007 Apple Inc. All rights reserved.
25 Responsibility: Doug Davidson
28 #if DEPLOYMENT_TARGET_MACOSX
29 #define READ_DIRECTORIES 1
32 #define READ_DIRECTORIES_CACHE_CAPACITY 128
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>
40 #include "CFInternal.h"
47 #if DEPLOYMENT_TARGET_MACOSX
48 #include <CoreFoundation/CFPreferences.h>
54 #endif /* READ_DIRECTORIES */
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
;
71 #error Unknown or unspecified DEPLOYMENT_TARGET
75 CF_INLINE CFStringRef
_CFGetAlternatePlatformName(void) {
76 #if DEPLOYMENT_TARGET_MACOSX
77 return _CFBundleAlternateMacOSXPlatformName
;
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;
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
;
109 CFAllocatorRef alloc
= __CFGetDefaultAllocator();
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
);
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
;
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
);
139 CF_INLINE
void _CFEnsureStaticBuffersInited(void) {
140 __CFSpinLock(&CFBundleResourceGlobalDataLock
);
141 if (!_AppSupportUniChars1
) _CFBundleInitStaticUniCharBuffers();
142 __CFSpinUnlock(&CFBundleResourceGlobalDataLock
);
147 static CFMutableDictionaryRef contentsCache
= NULL
;
148 static CFMutableDictionaryRef directoryContentsCache
= NULL
;
149 static CFMutableDictionaryRef unknownContentsCache
= NULL
;
152 _CFBundleAllContents
= 0,
153 _CFBundleDirectoryContents
= 1,
154 _CFBundleUnknownContents
= 2
155 } _CFBundleDirectoryContentsType
;
157 static CFArrayRef
_CFBundleCopyDirectoryContentsAtPath(CFStringRef path
, _CFBundleDirectoryContentsType contentsType
) {
158 CFArrayRef result
= NULL
;
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
);
166 if (contentsCache
) result
= (CFMutableArrayRef
)CFDictionaryGetValue(contentsCache
, path
);
168 if (result
) CFRetain(result
);
169 __CFSpinUnlock(&CFBundleResourceGlobalDataLock
);
172 Boolean tryToOpen
= false, allDots
= true;
173 char cpathBuff
[CFMaxPathSize
];
174 CFIndex cpathLen
= 0, idx
, lastSlashIdx
= 0;
177 CFMutableArrayRef contents
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
), directoryContents
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
), unknownContents
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
178 CFStringRef dirName
, name
;
182 if (CFStringGetFileSystemRepresentation(path
, cpathBuff
, CFMaxPathSize
)) {
184 cpathLen
= strlen(cpathBuff
);
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;
191 if (lastSlashIdx
> 0 && lastSlashIdx
+ 1 < cpathLen
&& !allDots
) {
192 cpathBuff
[lastSlashIdx
] = '\0';
193 dirName
= CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault
, cpathBuff
);
195 name
= CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault
, cpathBuff
+ lastSlashIdx
+ 1);
197 // ??? we might like to use directoryContentsCache rather than contentsCache here, but we cannot unless we resolve DT_LNKs below
198 CFArrayRef dirDirContents
= NULL
;
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;
208 __CFSpinUnlock(&CFBundleResourceGlobalDataLock
);
214 cpathBuff
[lastSlashIdx
] = '/';
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
);
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
);
235 (void)closedir(dirp
);
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
);
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
);
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
);
251 if (contentsType
== _CFBundleUnknownContents
) {
252 result
= CFRetain(unknownContents
);
253 } else if (contentsType
== _CFBundleDirectoryContents
) {
254 result
= CFRetain(directoryContents
);
256 result
= CFRetain(contents
);
260 CFRelease(directoryContents
);
261 CFRelease(unknownContents
);
262 __CFSpinUnlock(&CFBundleResourceGlobalDataLock
);
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
);
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
]);
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
);
294 #endif /* READ_DIRECTORIES */
296 CF_EXPORT
void _CFBundleFlushCachesForURL(CFURLRef url
) {
298 CFURLRef absoluteURL
= CFURLCopyAbsoluteURL(url
);
299 CFStringRef path
= CFURLCopyFileSystemPath(absoluteURL
, PLATFORM_PATH_STYLE
);
300 _CFBundleFlushContentsCachesForPath(path
);
302 CFRelease(absoluteURL
);
303 #endif /* READ_DIRECTORIES */
306 CF_EXPORT
void _CFBundleFlushCaches(void) {
308 _CFBundleFlushContentsCaches();
309 #endif /* READ_DIRECTORIES */
312 __private_extern__ Boolean
_CFIsResourceAtURL(CFURLRef url
, Boolean
*isDir
) {
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));
323 __private_extern__ Boolean
_CFIsResourceAtPath(CFStringRef path
, Boolean
*isDir
) {
324 Boolean result
= false;
325 CFURLRef url
= CFURLCreateWithFileSystemPath(CFGetAllocator(path
), path
, PLATFORM_PATH_STYLE
, false);
327 result
= _CFIsResourceAtURL(url
, isDir
);
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
);
337 CFRange contentsRange
, resultRange
= CFRangeMake(0, 0);
338 CFIndex dirPathLen
= pathLen
, numResTypes
= CFArrayGetCount(resTypes
), i
, j
;
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
));
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
);
361 //fprintf(stderr, "result ");CFShow(result);
365 #endif /* READ_DIRECTORIES */
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
;
379 CFIndex dirPathLen
= pathLen
;
380 CFArrayRef contents
, directoryContents
, unknownContents
;
381 CFRange contentsRange
, directoryContentsRange
, unknownContentsRange
;
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 */
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
) {
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");
411 #else /* READ_DIRECTORIES */
412 CFStringSetExternalCharactersNoCopy(tmpString
, pathUniChars
, pathLen
, pathLen
);
413 CFStringReplaceAll(cheapStr
, tmpString
);
414 platformGenericFound
= _CFIsResourceAtPath(cheapStr
, &platformGenericIsDir
);
415 #endif /* READ_DIRECTORIES */
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
) {
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");
441 #else /* READ_DIRECTORIES */
442 CFStringSetExternalCharactersNoCopy(tmpString
, pathUniChars
, pathLen
, pathLen
);
443 CFStringReplaceAll(cheapStr
, tmpString
);
444 platformSpecificFound
= _CFIsResourceAtPath(cheapStr
, &platformSpecificIsDir
);
445 #endif /* READ_DIRECTORIES */
449 if (platformSpecificFound
) {
450 CFURLRef url
= CFURLCreateWithFileSystemPath(alloc
, cheapStr
, PLATFORM_PATH_STYLE
, platformSpecificIsDir
);
451 CFArrayAppendValue(result
, url
);
453 } else if (platformGenericFound
) {
454 CFURLRef url
= CFURLCreateWithFileSystemPath(alloc
, platformGenericStr
? platformGenericStr
: cheapStr
, PLATFORM_PATH_STYLE
, platformGenericIsDir
);
455 CFArrayAppendValue(result
, url
);
458 if (platformGenericStr
) CFRelease(platformGenericStr
);
461 CFRelease(directoryContents
);
462 CFRelease(unknownContents
);
463 #endif /* READ_DIRECTORIES */
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
) {
468 // If we have a resName, just call the search API. We may have to loop over the resTypes.
470 _CFSearchBundleDirectory(alloc
, result
, workingUniChars
, workingLen
, nameUniChars
, nameLen
, NULL
, 0, cheapStr
, tmpString
, version
);
472 CFArrayRef subResTypes
= resTypes
;
473 Boolean releaseSubResTypes
= false;
474 CFIndex i
, c
= CFArrayGetCount(resTypes
);
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;
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;
492 if (releaseSubResTypes
) CFRelease(subResTypes
);
495 // If we have no resName, do it by hand. We may have to loop over the resTypes.
496 char cpathBuff
[CFMaxPathSize
];
498 CFMutableArrayRef children
;
500 CFStringSetExternalCharactersNoCopy(tmpString
, workingUniChars
, workingLen
, workingLen
);
501 if (!CFStringGetFileSystemRepresentation(tmpString
, cpathBuff
, CFMaxPathSize
)) return;
502 cpathLen
= strlen(cpathBuff
);
505 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
506 children
= _CFContentsOfDirectory(alloc
, cpathBuff
, NULL
, NULL
, NULL
);
508 CFIndex childIndex
, childCount
= CFArrayGetCount(children
);
509 for (childIndex
= 0; childIndex
< childCount
; childIndex
++) CFArrayAppendValue(result
, CFArrayGetValueAtIndex(children
, childIndex
));
513 CFIndex i
, c
= CFArrayGetCount(resTypes
);
514 for (i
= 0; i
< c
; i
++) {
515 CFStringRef curType
= (CFStringRef
)CFArrayGetValueAtIndex(resTypes
, i
);
517 // ??? should this use _CFBundleCopyDirectoryContentsAtPath?
518 children
= _CFContentsOfDirectory(alloc
, cpathBuff
, NULL
, NULL
, curType
);
520 CFIndex childIndex
, childCount
= CFArrayGetCount(children
);
521 for (childIndex
= 0; childIndex
< childCount
; childIndex
++) CFArrayAppendValue(result
, CFArrayGetValueAtIndex(children
, childIndex
));
524 if (limit
<= CFArrayGetCount(result
)) break;
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
;
533 // Look directly in the directory specified in workingUniChars. as if it is a Resources directory.
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
;
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
);
548 // Now search the local resources.
549 workingLen
= savedWorkingLen
;
550 if (CFArrayGetCount(result
) < limit
) {
552 CFIndex langCount
= (searchLanguages
? CFArrayGetCount(searchLanguages
) : 0);
553 CFStringRef curLangStr
;
555 // MF:??? OK to hard-wire this length?
556 UniChar curLangUniChars
[255];
557 CFIndex numResults
= CFArrayGetCount(result
);
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
;
569 if (!_CFAppendPathExtension(workingUniChars
, &workingLen
, CFMaxPathSize
, _LprojUniChars
, _LprojLen
)) {
570 workingLen
= savedWorkingLen
;
574 if (!_CFAppendPathComponent(workingUniChars
, &workingLen
, CFMaxPathSize
, subDirUniChars
, subDirLen
)) {
575 workingLen
= savedWorkingLen
;
579 _CFFindBundleResourcesInRawDir(alloc
, workingUniChars
, workingLen
, nameUniChars
, nameLen
, resTypes
, limit
, version
, cheapStr
, tmpString
, result
);
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.
592 extern void _CFStrSetDesiredCapacity(CFMutableStringRef str
, CFIndex len
);
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
;
605 result
= CFArrayCreateMutable(alloc
, 0, &kCFTypeArrayCallBacks
);
606 // Init the one-time-only unichar buffers.
607 _CFEnsureStaticBuffersInited();
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
;
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
;
628 _CFAppendPathComponent(workingUniChars
, &workingLen
, CFMaxPathSize
, _AppSupportUniChars1
, _AppSupportLen1
);
629 } else if (2 == version
) {
630 _CFAppendPathComponent(workingUniChars
, &workingLen
, CFMaxPathSize
, _AppSupportUniChars2
, _AppSupportLen2
);
632 if (0 == version
|| 1 == version
|| 2 == version
) {
633 _CFAppendPathComponent(workingUniChars
, &workingLen
, CFMaxPathSize
, _ResourcesUniChars
, _ResourcesLen
);
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
);
642 _CFFindBundleResourcesInResourcesDir(alloc
, workingUniChars
, workingLen
, subDirUniChars
, subDirLen
, searchLanguages
, nameUniChars
, nameLen
, resTypes
, limit
, version
, cheapStr
, tmpString
, result
);
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
);
652 CFRelease(tmpString
);
653 CFAllocatorDeallocate(alloc
, nameUniChars
);
654 if (!bundle
) CFRelease(alloc
);
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
);
664 array
= _CFFindBundleResources(bundle
, NULL
, subDirName
, languages
, resourceName
, types
, 1, _CFBundleLayoutVersion(bundle
));
666 if (types
) CFRelease(types
);
669 if (CFArrayGetCount(array
) > 0) result
= (CFURLRef
)CFRetain(CFArrayGetValueAtIndex(array
, 0));
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
);
679 // MF:!!! Better "limit" than 1,000,000?
680 array
= _CFFindBundleResources(bundle
, NULL
, subDirName
, languages
, NULL
, types
, 1000000, _CFBundleLayoutVersion(bundle
));
682 if (types
) CFRelease(types
);
687 CF_EXPORT CFURLRef
_CFBundleCopyResourceURLForLanguage(CFBundleRef bundle
, CFStringRef resourceName
, CFStringRef resourceType
, CFStringRef subDirName
, CFStringRef language
) {return CFBundleCopyResourceURLForLocalization(bundle
, resourceName
, resourceType
, subDirName
, language
);}
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
;
693 if (localizationName
) languages
= CFArrayCreate(CFGetAllocator(bundle
), (const void **)&localizationName
, 1, &kCFTypeArrayCallBacks
);
694 if (resourceType
) types
= CFArrayCreate(CFGetAllocator(bundle
), (const void **)&resourceType
, 1, &kCFTypeArrayCallBacks
);
696 array
= _CFFindBundleResources(bundle
, NULL
, subDirName
, languages
, resourceName
, types
, 1, _CFBundleLayoutVersion(bundle
));
698 if (CFArrayGetCount(array
) > 0) result
= (CFURLRef
)CFRetain(CFArrayGetValueAtIndex(array
, 0));
702 if (types
) CFRelease(types
);
703 if (languages
) CFRelease(languages
);
708 CF_EXPORT CFArrayRef
_CFBundleCopyResourceURLsOfTypeForLanguage(CFBundleRef bundle
, CFStringRef resourceType
, CFStringRef subDirName
, CFStringRef language
) {return CFBundleCopyResourceURLsOfTypeForLocalization(bundle
, resourceType
, subDirName
, language
);}
710 CF_EXPORT CFArrayRef
CFBundleCopyResourceURLsOfTypeForLocalization(CFBundleRef bundle
, CFStringRef resourceType
, CFStringRef subDirName
, CFStringRef localizationName
) {
711 CFArrayRef languages
= NULL
, types
= NULL
, array
;
713 if (localizationName
) languages
= CFArrayCreate(CFGetAllocator(bundle
), (const void **)&localizationName
, 1, &kCFTypeArrayCallBacks
);
714 if (resourceType
) types
= CFArrayCreate(CFGetAllocator(bundle
), (const void **)&resourceType
, 1, &kCFTypeArrayCallBacks
);
716 // MF:!!! Better "limit" than 1,000,000?
717 array
= _CFFindBundleResources(bundle
, NULL
, subDirName
, languages
, NULL
, types
, 1000000, _CFBundleLayoutVersion(bundle
));
719 if (types
) CFRelease(types
);
720 if (languages
) CFRelease(languages
);
725 CF_EXPORT CFStringRef
CFBundleCopyLocalizedString(CFBundleRef bundle
, CFStringRef key
, CFStringRef value
, CFStringRef tableName
) {
726 CFStringRef result
= NULL
;
727 CFDictionaryRef stringTable
= NULL
;
729 if (!key
) return (value
? (CFStringRef
)CFRetain(value
) : (CFStringRef
)CFRetain(CFSTR("")));
731 if (!tableName
|| CFEqual(tableName
, CFSTR(""))) tableName
= _CFBundleDefaultStringTableName
;
732 if (__CFBundleGetResourceData(bundle
)->_stringTableCache
) stringTable
= (CFDictionaryRef
)CFDictionaryGetValue(__CFBundleGetResourceData(bundle
)->_stringTableCache
, tableName
);
734 // Go load the table.
735 CFURLRef tableURL
= CFBundleCopyResourceURL(bundle
, tableName
, _CFBundleStringTableType
, NULL
);
737 CFStringRef nameForSharing
= NULL
;
739 CFDataRef tableData
= NULL
;
742 if (CFURLCreateDataAndPropertiesFromResource(CFGetAllocator(bundle
), tableURL
, &tableData
, NULL
, NULL
, &errCode
)) {
743 stringTable
= (CFDictionaryRef
)CFPropertyListCreateFromXMLData(CFGetAllocator(bundle
), tableData
, kCFPropertyListImmutable
, &errStr
);
748 if (stringTable
&& CFDictionaryGetTypeID() != CFGetTypeID(stringTable
)) {
749 CFRelease(stringTable
);
752 CFRelease(tableData
);
755 if (nameForSharing
) CFRelease(nameForSharing
);
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
);
764 result
= (CFStringRef
)CFDictionaryGetValue(stringTable
, key
);
766 static int capitalize
= -1;
768 result
= (CFStringRef
)CFRetain(key
);
769 } else if (CFEqual(value
, CFSTR(""))) {
770 result
= (CFStringRef
)CFRetain(key
);
772 result
= (CFStringRef
)CFRetain(value
);
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
);
780 result
= capitalizedResult
;
786 if (CFStringHasSuffix(tableName
, CFSTR(".nocache")) && __CFBundleGetResourceData(bundle
)->_stringTableCache
&& _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard
)) CFDictionaryRemoveValue(__CFBundleGetResourceData(bundle
)->_stringTableCache
, tableName
);
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
;
796 if (!CFURLGetFileSystemRepresentation(bundleURL
, true, buff
, CFMaxPathSize
)) return NULL
;
798 newURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, buff
, strlen((char *)buff
), true);
799 if (!newURL
) newURL
= (CFURLRef
)CFRetain(bundleURL
);
800 if (_CFBundleCouldBeBundle(newURL
)) {
802 CFArrayRef languages
= _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault
, newURL
, &version
), types
= NULL
, array
;
803 if (resourceType
) types
= CFArrayCreate(kCFAllocatorSystemDefault
, (const void **)&resourceType
, 1, &kCFTypeArrayCallBacks
);
805 array
= _CFFindBundleResources(NULL
, newURL
, subDirName
, languages
, resourceName
, types
, 1, version
);
807 if (types
) CFRelease(types
);
808 if (languages
) CFRelease(languages
);
811 if (CFArrayGetCount(array
) > 0) result
= (CFURLRef
)CFRetain(CFArrayGetValueAtIndex(array
, 0));
815 if (newURL
) CFRelease(newURL
);
819 CF_EXPORT CFArrayRef
CFBundleCopyResourceURLsOfTypeInDirectory(CFURLRef bundleURL
, CFStringRef resourceType
, CFStringRef subDirName
) {
820 CFArrayRef array
= NULL
;
821 unsigned char buff
[CFMaxPathSize
];
822 CFURLRef newURL
= NULL
;
824 if (!CFURLGetFileSystemRepresentation(bundleURL
, true, buff
, CFMaxPathSize
)) return NULL
;
826 newURL
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, buff
, strlen((char *)buff
), true);
827 if (!newURL
) newURL
= (CFURLRef
)CFRetain(bundleURL
);
828 if (_CFBundleCouldBeBundle(newURL
)) {
830 CFArrayRef languages
= _CFBundleCopyLanguageSearchListInDirectory(kCFAllocatorSystemDefault
, newURL
, &version
), types
= NULL
;
831 if (resourceType
) types
= CFArrayCreate(kCFAllocatorSystemDefault
, (const void **)&resourceType
, 1, &kCFTypeArrayCallBacks
);
833 // MF:!!! Better "limit" than 1,000,000?
834 array
= _CFFindBundleResources(NULL
, newURL
, subDirName
, languages
, NULL
, types
, 1000000, version
);
836 if (types
) CFRelease(types
);
837 if (languages
) CFRelease(languages
);
839 if (newURL
) CFRelease(newURL
);
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";
860 #define NUM_LOCALE_ABBREVIATIONS 109
861 #define LOCALE_ABBREVIATION_LENGTH 6
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"
885 #define NUM_LANGUAGE_NAMES 152
886 #define LANGUAGE_NAME_LENGTH 13
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";
910 #define NUM_LANGUAGE_ABBREVIATIONS 152
911 #define LANGUAGE_ABBREVIATION_LENGTH 3
913 #if defined(__CONSTANT_CFSTRINGS__)
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")};
920 #define NUM_COMMON_LANGUAGE_NAMES 7
922 #endif /* __CONSTANT_CFSTRINGS__ */
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
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
950 static SInt32
_CFBundleGetLanguageCodeForLocalization(CFStringRef localizationName
) {
951 SInt32 result
= -1, i
;
953 CFIndex length
= CFStringGetLength(localizationName
);
954 if (length
>= LANGUAGE_ABBREVIATION_LENGTH
- 1 && length
<= 255 && CFStringGetCString(localizationName
, buff
, 255, kCFStringEncodingASCII
)) {
956 for (i
= 0; -1 == result
&& i
< NUM_LANGUAGE_NAMES
; i
++) {
957 if (0 == strcmp(buff
, __CFBundleLanguageNamesArray
[i
])) result
= i
;
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
;
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
);
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
);
989 CF_INLINE CFStringRef
_CFBundleCopyLanguageAbbreviationForLocalization(CFStringRef localizationName
) {
990 CFStringRef result
= NULL
;
991 SInt32 languageCode
= _CFBundleGetLanguageCodeForLocalization(localizationName
);
992 if (languageCode
>= 0) {
993 result
= _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode
);
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));
1003 CF_INLINE CFStringRef
_CFBundleCopyModifiedLocalization(CFStringRef localizationName
) {
1004 CFMutableStringRef result
= NULL
;
1005 CFIndex length
= CFStringGetLength(localizationName
);
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("-"));
1016 CF_INLINE CFStringRef
_CFBundleCopyLanguageNameForLocalization(CFStringRef localizationName
) {
1017 CFStringRef result
= NULL
;
1018 SInt32 languageCode
= _CFBundleGetLanguageCodeForLocalization(localizationName
);
1019 if (languageCode
>= 0) {
1020 result
= _CFBundleCopyLanguageNameForLanguageCode(languageCode
);
1022 result
= (CFStringRef
)CFStringCreateCopy(kCFAllocatorSystemDefault
, localizationName
);
1027 static SInt32
_CFBundleGetLanguageCodeForRegionCode(SInt32 regionCode
) {
1028 SInt32 result
= -1, i
;
1029 if (52 == regionCode
) { // hack for mixed-up Chinese language codes
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
;
1042 static SInt32
_CFBundleGetRegionCodeForLanguageCode(SInt32 languageCode
) {
1043 SInt32 result
= -1, i
;
1044 if (19 == languageCode
) { // hack for mixed-up Chinese language codes
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
;
1054 if (25 == result
) result
= 68;
1055 if (28 == result
) result
= 82;
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
;
1069 if (25 == result
) result
= 68;
1070 if (28 == result
) result
= 82;
1071 if (37 == result
) result
= 0;
1073 SInt32 languageCode
= _CFBundleGetLanguageCodeForLocalization(localizationName
);
1074 result
= _CFBundleGetRegionCodeForLanguageCode(languageCode
);
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
);
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
;
1098 languages
= _CFBundleGetLanguageSearchList(mainBundle
);
1099 if (languages
) CFRetain(languages
);
1101 if (!languages
) languages
= _CFBundleCopyUserLanguages(false);
1102 if (languages
&& CFArrayGetCount(languages
) > 0) localizationName
= (CFStringRef
)CFArrayGetValueAtIndex(languages
, 0);
1105 if (localizationName
) {
1106 language
= _CFBundleGetLanguageCodeForLocalization(localizationName
);
1107 region
= _CFBundleGetRegionCodeForLocalization(localizationName
);
1109 _CFBundleGetLanguageAndRegionCodes(&language
, ®ion
);
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
];
1116 if (language
>= 0 && language
< (int)(sizeof(__CFBundleStringEncodingsArray
)/sizeof(CFStringEncoding
))) {
1117 encoding
= __CFBundleStringEncodingsArray
[language
];
1119 retval
= (language
!= -1 || region
!= -1);
1121 if (languageCode
) *languageCode
= language
;
1122 if (regionCode
) *regionCode
= region
;
1123 if (scriptCode
) *scriptCode
= script
;
1124 if (stringEncoding
) *stringEncoding
= encoding
;
1128 CFStringRef
CFBundleCopyLocalizationForLocalizationInfo(SInt32 languageCode
, SInt32 regionCode
, SInt32 scriptCode
, CFStringEncoding stringEncoding
) {
1129 CFStringRef localizationName
= NULL
;
1130 if (!localizationName
) {
1131 localizationName
= _CFBundleCopyLocaleAbbreviationForRegionCode(regionCode
);
1133 if (!localizationName
&& 0 <= languageCode
&& languageCode
< SHRT_MAX
) {
1134 localizationName
= CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(kCFAllocatorSystemDefault
, (LangCode
)languageCode
, (RegionCode
)-1);
1136 if (!localizationName
) {
1137 localizationName
= _CFBundleCopyLanguageAbbreviationForLanguageCode(languageCode
);
1139 if (!localizationName
) {
1140 SInt32 language
= -1, scriptLanguage
= -1, encodingLanguage
= -1;
1142 for (i
= 0; language
== -1 && i
< (sizeof(__CFBundleScriptCodesArray
)/sizeof(SInt32
)); i
++) {
1143 if (__CFBundleScriptCodesArray
[i
] == scriptCode
&& __CFBundleStringEncodingsArray
[i
] == stringEncoding
) language
= i
;
1145 for (i
= 0; scriptLanguage
== -1 && i
< (sizeof(__CFBundleScriptCodesArray
)/sizeof(SInt32
)); i
++) {
1146 if (__CFBundleScriptCodesArray
[i
] == scriptCode
) scriptLanguage
= i
;
1148 for (i
= 0; encodingLanguage
== -1 && i
< (sizeof(__CFBundleStringEncodingsArray
)/sizeof(CFStringEncoding
)); i
++) {
1149 if (__CFBundleStringEncodingsArray
[i
] == stringEncoding
) encodingLanguage
= i
;
1151 localizationName
= _CFBundleCopyLanguageAbbreviationForLanguageCode(language
);
1152 if (!localizationName
) localizationName
= _CFBundleCopyLanguageAbbreviationForLanguageCode(encodingLanguage
);
1153 if (!localizationName
) localizationName
= _CFBundleCopyLanguageAbbreviationForLanguageCode(scriptLanguage
);
1155 return localizationName
;
1158 extern void *__CFAppleLanguages
;
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
);
1168 if (__CFAppleLanguages
) {
1170 CFIndex length
= strlen((const char *)__CFAppleLanguages
);
1172 data
= CFDataCreateWithBytesNoCopy(kCFAllocatorSystemDefault
, (const UInt8
*)__CFAppleLanguages
, length
, kCFAllocatorNull
);
1174 userLanguages
= (CFArrayRef
)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault
, data
, kCFPropertyListImmutable
, NULL
);
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
);
1186 if (userLanguages
&& CFGetTypeID(userLanguages
) != CFArrayGetTypeID()) {
1187 CFRelease(userLanguages
);
1188 userLanguages
= NULL
;
1192 __CFSpinUnlock(&CFBundleResourceGlobalDataLock
);
1193 if (preferencesArray
) CFRelease(preferencesArray
);
1194 if (!result
&& userLanguages
) result
= (CFArrayRef
)CFRetain(userLanguages
);
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
;
1205 languages
= _CFBundleGetLanguageSearchList(mainBundle
);
1206 if (languages
) CFRetain(languages
);
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;
1215 language
= _CFBundleGetLanguageCodeForLocalization(localizationName
);
1216 region
= _CFBundleGetRegionCodeForLocalization(localizationName
);
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
;
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 */
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
);
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 */
1260 predefinedLocalizations
= (CFArrayRef
)CFDictionaryGetValue(infoDict
, kCFBundleLocalizationsKey
);
1261 if (predefinedLocalizations
&& CFGetTypeID(predefinedLocalizations
) != CFArrayGetTypeID()) {
1262 predefinedLocalizations
= NULL
;
1263 CFDictionaryRemoveValue((CFMutableDictionaryRef
)infoDict
, kCFBundleLocalizationsKey
);
1266 predefinedLocalizationsRange
= CFRangeMake(0, predefinedLocalizations
? CFArrayGetCount(predefinedLocalizations
) : 0);
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
);
1283 if (CFStringGetLength(curLangStr
) <= 2) {
1284 CFRelease(cheapStr
);
1285 CFRelease(tmpString
);
1286 #if READ_DIRECTORIES
1287 CFRelease(contents
);
1288 #endif /* READ_DIRECTORIES */
1293 #if defined(__CONSTANT_CFSTRINGS__)
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
];
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 */
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
);
1327 CFRelease(cheapStr
);
1328 CFRelease(tmpString
);
1329 #if READ_DIRECTORIES
1330 CFRelease(contents
);
1331 #endif /* READ_DIRECTORIES */
1336 #if READ_DIRECTORIES
1337 if (!foundOne
&& (!predefinedLocalizations
|| CFArrayGetCount(predefinedLocalizations
) == 0)) {
1338 Boolean hasLocalizations
= false;
1340 for (idx
= 0; !hasLocalizations
&& idx
< contentsRange
.length
; idx
++) {
1341 CFStringRef name
= CFArrayGetValueAtIndex(contents
, idx
);
1342 if (CFStringHasSuffix(name
, _CFBundleLprojExtensionWithDot
)) hasLocalizations
= true;
1344 if (!hasLocalizations
) {
1345 CFRelease(cheapStr
);
1346 CFRelease(tmpString
);
1347 CFRelease(contents
);
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
);
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
);
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
);
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 */
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()) {
1441 if (CFNumberGetValue((CFNumberRef
)allowMixedValue
, kCFNumberSInt32Type
, &val
)) allowMixed
= (val
!= 0);
1444 examinedMain
= true;
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
;
1457 CFStringRef resourcesPath
;
1458 UniChar pathUniChars
[CFMaxPathSize
];
1460 CFStringRef curLangStr
;
1461 Boolean foundOne
= false;
1463 CFArrayRef userLanguages
;
1465 // Init the one-time-only unichar buffers.
1466 _CFEnsureStaticBuffersInited();
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
);
1478 // First check the main bundle.
1479 if (!CFBundleAllowMixedLocalizations()) {
1480 CFBundleRef mainBundle
= CFBundleGetMainBundle();
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
);
1491 CFRelease(mainBundleURL
);
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
);
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
);
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
;
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
);
1520 if (range
.length
== 1 || CFStringGetLength(curLangStr
) <= 2) return foundOne
;
1522 if (range
.length
== 1 && CFArrayContainsValue(array
, range
, CFSTR("default"))) return foundOne
;
1523 #if defined(__CONSTANT_CFSTRINGS__)
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
];
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
);
1538 if (!altLangStr
&& (modifiedLangStr
= _CFBundleCopyModifiedLocalization(curLangStr
))) {
1539 if (CFArrayContainsValue(array
, range
, modifiedLangStr
)) {
1540 if (!CFArrayContainsValue(lprojNames
, CFRangeMake(0, CFArrayGetCount(lprojNames
)), modifiedLangStr
)) CFArrayAppendValue(lprojNames
, modifiedLangStr
);
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
);
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
);
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
);
1565 static CFArrayRef
_CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray
, CFArrayRef prefArray
, Boolean considerMain
) {
1566 CFMutableArrayRef lprojNames
= CFArrayCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeArrayCallBacks
);
1567 Boolean foundOne
= false, releasePrefArray
= false;
1570 if (considerMain
&& !CFBundleAllowMixedLocalizations()) {
1571 CFBundleRef mainBundle
= CFBundleGetMainBundle();
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
);
1582 prefArray
= _CFBundleCopyUserLanguages(true);
1583 if (prefArray
) releasePrefArray
= true;
1585 count
= (prefArray
? CFArrayGetCount(prefArray
) : 0);
1586 for (idx
= 0; !foundOne
&& idx
< count
; idx
++) {
1587 foundOne
= _CFBundleTryOnePreferredLprojNameInArray(locArray
, (CFStringRef
)CFArrayGetValueAtIndex(prefArray
, idx
), lprojNames
);
1589 // use U.S. English as backstop
1591 foundOne
= _CFBundleTryOnePreferredLprojNameInArray(locArray
, CFSTR("en_US"), lprojNames
);
1593 // use random entry as backstop
1594 if (!foundOne
&& CFArrayGetCount(lprojNames
) > 0) {
1595 foundOne
= _CFBundleTryOnePreferredLprojNameInArray(locArray
, (CFStringRef
)CFArrayGetValueAtIndex(locArray
, 0), lprojNames
);
1598 if (CFArrayGetCount(lprojNames
) == 0) {
1599 // Total backstop behavior to avoid having an empty array.
1600 CFArrayAppendValue(lprojNames
, CFSTR("en"));
1602 if (releasePrefArray
) {
1603 CFRelease(prefArray
);
1608 CF_EXPORT CFArrayRef
CFBundleCopyLocalizationsForPreferences(CFArrayRef locArray
, CFArrayRef prefArray
) {return _CFBundleCopyLocalizationsForPreferences(locArray
, prefArray
, false);}
1610 CF_EXPORT CFArrayRef
CFBundleCopyPreferredLocalizationsFromArray(CFArrayRef locArray
) {return _CFBundleCopyLocalizationsForPreferences(locArray
, NULL
, true);}
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
;
1620 _CFBundleAddPreferredLprojNamesInDirectory(alloc
, url
, localVersion
, infoDict
, langs
, devLang
);
1622 if (devLang
&& CFArrayGetFirstIndexOfValue(langs
, CFRangeMake(0, CFArrayGetCount(langs
)), devLang
) < 0) CFArrayAppendValue(langs
, devLang
);
1624 // Total backstop behavior to avoid having an empty array.
1625 if (CFArrayGetCount(langs
) == 0) CFArrayAppendValue(langs
, CFSTR("en"));
1627 if (infoDict
) CFRelease(infoDict
);
1628 if (version
) *version
= localVersion
;
1632 CF_EXPORT Boolean
_CFBundleURLLooksLikeBundle(CFURLRef url
) {
1633 Boolean result
= false;
1634 CFBundleRef bundle
= _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault
, url
);
1642 // Note that subDirName is expected to be the string for a URL
1643 CF_INLINE Boolean
_CFBundleURLHasSubDir(CFURLRef url
, CFStringRef subDirName
) {
1645 Boolean isDir
= false, result
= false;
1647 dirURL
= CFURLCreateWithString(kCFAllocatorSystemDefault
, subDirName
, url
);
1649 if (_CFIsResourceAtURL(dirURL
, &isDir
) && isDir
) result
= true;
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;
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;
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;
1689 if (_CFBundleURLHasSubDir(url
, _CFBundleSupportFilesURLFromBase2
)) localVersion
= 2;
1690 else if (_CFBundleURLHasSubDir(url
, _CFBundleResourcesURLFromBase0
)) localVersion
= 0;
1691 else if (_CFBundleURLHasSubDir(url
, _CFBundleSupportFilesURLFromBase1
)) localVersion
= 1;
1695 if (version
) *version
= localVersion
;
1696 return !(localVersion
== 3);
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;
1704 if (CFURLGetFileSystemRepresentation(url
, true, buff
, CFMaxPathSize
)) {
1705 CFURLRef newURL
= CFURLCreateFromFileSystemRepresentation(alloc
, buff
, strlen((char *)buff
), true);
1706 if (!newURL
) newURL
= (CFURLRef
)CFRetain(url
);
1708 // version 3 is for flattened pseudo-bundles with no Contents, Support Files, or Resources directories
1709 if (!_CFBundleURLLooksLikeBundleVersion(newURL
, &localVersion
)) localVersion
= 3;
1711 dict
= _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc
, newURL
, localVersion
);
1714 if (version
) *version
= localVersion
;
1718 __private_extern__ CFDictionaryRef
_CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc
, CFURLRef url
, uint8_t version
) {
1719 CFDictionaryRef result
= NULL
;
1721 CFURLRef infoURL
= NULL
, rawInfoURL
= NULL
;
1722 CFDataRef infoData
= NULL
;
1723 UniChar buff
[CFMaxPathSize
];
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 */
1735 _CFEnsureStaticBuffersInited();
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
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
;
1767 CFRelease(posixPath
);
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
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
;
1780 CFRelease(windowsPath
);
1783 #error Unknown or unspecified DEPLOYMENT_TARGET
1786 #if READ_DIRECTORIES
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
);
1796 #endif /* READ_DIRECTORIES */
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
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;
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
);
1823 // Check for global Info.plist
1825 infoURL
= CFURLCreateWithString(alloc
, infoURLFromBase
, url
);
1826 #if READ_DIRECTORIES
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;
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"));
1840 result
= (CFDictionaryRef
)CFPropertyListCreateFromXMLData(alloc
, infoData
, kCFPropertyListMutableContainers
, NULL
);
1842 if (CFDictionaryGetTypeID() == CFGetTypeID(result
)) {
1843 CFDictionarySetValue((CFMutableDictionaryRef
)result
, _kCFBundleInfoPlistURLKey
, infoURL
);
1849 if (!result
) rawInfoURL
= infoURL
;
1850 CFRelease(infoData
);
1853 result
= CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1854 if (rawInfoURL
) CFDictionarySetValue((CFMutableDictionaryRef
)result
, _kCFBundleRawInfoPlistURLKey
, rawInfoURL
);
1858 #if READ_DIRECTORIES
1859 if (contents
) CFRelease(contents
);
1860 #endif /* READ_DIRECTORIES */
1865 static Boolean
_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc
, CFURLRef url
, CFDictionaryRef infoDict
, UInt32
*packageType
, UInt32
*packageCreator
) {
1866 Boolean retVal
= false, hasType
= false, hasCreator
= false, releaseInfoDict
= false;
1868 CFDataRef pkgInfoData
= NULL
;
1870 // Check for a "real" new bundle
1871 tempURL
= CFURLCreateWithString(alloc
, _CFBundlePkgInfoURLFromBase2
, url
);
1872 CFURLCreateDataAndPropertiesFromResource(alloc
, tempURL
, &pkgInfoData
, NULL
, NULL
, NULL
);
1875 tempURL
= CFURLCreateWithString(alloc
, _CFBundlePkgInfoURLFromBase1
, url
);
1876 CFURLCreateDataAndPropertiesFromResource(alloc
, tempURL
, &pkgInfoData
, NULL
, NULL
, NULL
);
1880 // Check for a "pseudo" new bundle
1881 tempURL
= CFURLCreateWithString(alloc
, _CFBundlePseudoPkgInfoURLFromBase
, url
);
1882 CFURLCreateDataAndPropertiesFromResource(alloc
, tempURL
, &pkgInfoData
, NULL
, NULL
, NULL
);
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.
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;
1896 if (pkgInfoData
) CFRelease(pkgInfoData
);
1899 infoDict
= _CFBundleCopyInfoDictionaryInDirectory(alloc
, url
, NULL
);
1900 releaseInfoDict
= true;
1903 CFStringRef typeString
= (CFStringRef
)CFDictionaryGetValue(infoDict
, _kCFBundlePackageTypeKey
), creatorString
= (CFStringRef
)CFDictionaryGetValue(infoDict
, _kCFBundleSignatureKey
);
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;
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;
1914 if (releaseInfoDict
) CFRelease(infoDict
);
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
) {
1923 UniChar buff
[CFMaxPathSize
];
1924 CFIndex strLen
, startOfExtension
;
1925 CFURLRef absoluteURL
;
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
);
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
)'/'))) {
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'
1952 // Default to BNDL for generic bundle
1953 *packageType
= 0x424e444c; // 'BNDL'
1962 CF_EXPORT Boolean
_CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc
, CFURLRef url
, UInt32
*packageType
, UInt32
*packageCreator
) {return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc
, url
, NULL
, packageType
, packageCreator
);}
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; // '????'
1970 if (bundleURL
) CFRelease(bundleURL
);
1973 CF_EXPORT Boolean
CFBundleGetPackageInfoInDirectory(CFURLRef url
, UInt32
*packageType
, UInt32
*packageCreator
) {return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault
, url
, packageType
, packageCreator
);}
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");
1987 #error Unknown or unspecified DEPLOYMENT_TARGET
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");
2003 #error Unknown or unspecified DEPLOYMENT_TARGET
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");
2019 #error Unknown or unspecified DEPLOYMENT_TARGET
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");
2035 #error Unknown or unspecified DEPLOYMENT_TARGET
2039 __private_extern__ CFArrayRef
_CFBundleCopyBundleRegionsArray(CFBundleRef bundle
) {return CFBundleCopyBundleLocalizations(bundle
);}
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
;
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
;
2057 predefinedLocalizations
= (CFArrayRef
)CFDictionaryGetValue(infoDict
, kCFBundleLocalizationsKey
);
2058 if (predefinedLocalizations
&& CFGetTypeID(predefinedLocalizations
) != CFArrayGetTypeID()) {
2059 predefinedLocalizations
= NULL
;
2060 CFDictionaryRemoveValue((CFMutableDictionaryRef
)infoDict
, kCFBundleLocalizationsKey
);
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
));
2069 #if READ_DIRECTORIES
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
);
2084 CFRelease(contents
);
2085 CFRelease(directoryPath
);
2086 CFRelease(absoluteURL
);
2088 #else /* READ_DIRECTORIES */
2090 CFIndex i
, c
= CFArrayGetCount(urls
);
2091 CFURLRef curURL
, curAbsoluteURL
;
2092 CFStringRef curStr
, regionStr
;
2093 UniChar buff
[CFMaxPathSize
];
2094 CFIndex strLen
, startOfLastPathComponent
, regionLen
;
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
);
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
);
2115 #endif /* READ_DIRECTORIES */
2118 CFStringRef developmentLocalization
= CFBundleGetDevelopmentRegion(bundle
);
2119 if (developmentLocalization
) {
2120 result
= CFArrayCreateMutable(CFGetAllocator(bundle
), 0, &kCFTypeArrayCallBacks
);
2121 CFArrayAppendValue(result
, developmentLocalization
);
2124 if (resourcesURL
) CFRelease(resourcesURL
);
2130 CF_EXPORT CFDictionaryRef
CFBundleCopyInfoDictionaryForURL(CFURLRef url
) {
2131 CFDictionaryRef result
= NULL
;
2132 Boolean isDir
= false;
2133 if (_CFIsResourceAtURL(url
, &isDir
)) {
2135 result
= _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault
, url
, NULL
);
2137 result
= _CFBundleCopyInfoDictionaryInExecutable(url
);
2143 CFArrayRef
CFBundleCopyExecutableArchitecturesForURL(CFURLRef url
) {
2144 CFArrayRef result
= NULL
;
2145 CFBundleRef bundle
= CFBundleCreate(kCFAllocatorSystemDefault
, url
);
2147 result
= CFBundleCopyExecutableArchitectures(bundle
);
2150 result
= _CFBundleCopyArchitecturesForExecutable(url
);
2155 CFArrayRef
CFBundleCopyLocalizationsForURL(CFURLRef url
) {
2156 CFArrayRef result
= NULL
;
2157 CFBundleRef bundle
= CFBundleCreate(kCFAllocatorSystemDefault
, url
);
2158 CFStringRef devLang
= NULL
;
2160 result
= CFBundleCopyBundleLocalizations(bundle
);
2163 CFDictionaryRef infoDict
= _CFBundleCopyInfoDictionaryInExecutable(url
);
2165 CFArrayRef predefinedLocalizations
= (CFArrayRef
)CFDictionaryGetValue(infoDict
, kCFBundleLocalizationsKey
);
2166 if (predefinedLocalizations
&& CFGetTypeID(predefinedLocalizations
) == CFArrayGetTypeID()) result
= (CFArrayRef
)CFRetain(predefinedLocalizations
);
2168 devLang
= (CFStringRef
)CFDictionaryGetValue(infoDict
, kCFBundleDevelopmentRegionKey
);
2169 if (devLang
&& (CFGetTypeID(devLang
) == CFStringGetTypeID() && CFStringGetLength(devLang
) > 0)) result
= CFArrayCreate(kCFAllocatorSystemDefault
, (const void **)&devLang
, 1, &kCFTypeArrayCallBacks
);
2171 CFRelease(infoDict
);