1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ******************************************************************************
5 * Copyright (C) 1997-2016, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ******************************************************************************
11 * Modification History:
13 * Date Name Description
14 * 04/01/97 aliu Creation.
15 * 06/14/99 stephen Removed functions taking a filename suffix.
16 * 07/20/99 stephen Changed for UResourceBundle typedef'd to void*
17 * 11/09/99 weiv Added ures_getLocale()
18 * March 2000 weiv Total overhaul - using data in DLLs
19 * 06/20/2000 helena OS/400 port changes; mostly typecast.
20 * 06/24/02 weiv Added support for resource sharing
21 ******************************************************************************
24 #include "unicode/ures.h"
25 #include "unicode/ustring.h"
26 #include "unicode/ucnv.h"
36 #include "unicode/uenum.h"
44 #include <stdio.h> /* for sprintf */
49 Static cache for already opened resource bundles - mostly for keeping fallback info
50 TODO: This cache should probably be removed when the deprecated code is
53 static UHashtable
*cache
= NULL
;
54 static icu::UInitOnce gCacheInitOnce
= U_INITONCE_INITIALIZER
;
56 static UMutex resbMutex
;
58 /* INTERNAL: hashes an entry */
59 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
60 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
61 UHashTok namekey
, pathkey
;
62 namekey
.pointer
= b
->fName
;
63 pathkey
.pointer
= b
->fPath
;
64 return uhash_hashChars(namekey
)+37u*uhash_hashChars(pathkey
);
67 /* INTERNAL: compares two entries */
68 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
69 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
70 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
71 UHashTok name1
, name2
, path1
, path2
;
72 name1
.pointer
= b1
->fName
;
73 name2
.pointer
= b2
->fName
;
74 path1
.pointer
= b1
->fPath
;
75 path2
.pointer
= b2
->fPath
;
76 return (UBool
)(uhash_compareChars(name1
, name2
) &&
77 uhash_compareChars(path1
, path2
));
82 * Internal function, gets parts of locale name according
83 * to the position of '_' character
85 static UBool
chopLocale(char *name
) {
86 char *i
= uprv_strrchr(name
, '_');
96 static icu::UInitOnce parentLocaleInitOnce
= U_INITONCE_INITIALIZER
;
97 static UHashtable
* parentLocaleTable
= NULL
;
98 static char* parentLocaleStrings
= NULL
;
99 static const int32_t parentLocaleStringsCapacity
= 2350; // actual size is 2292-- leave a little extra space in case the table changes
101 static void doInitParentLocaleTable() {
102 UErrorCode err
= U_ZERO_ERROR
;
103 parentLocaleTable
= uhash_open(uhash_hashIChars
, uhash_compareIChars
, uhash_compareIChars
, &err
);
105 UResourceBundle
* curBundle
= ures_openDirect(NULL
, "supplementalData", &err
);
106 curBundle
= ures_getByKey(curBundle
, "parentLocales", curBundle
, &err
);
107 parentLocaleStrings
= (char*)uprv_malloc(parentLocaleStringsCapacity
);
108 const char* parentLocaleStringsEnd
= parentLocaleStrings
+ parentLocaleStringsCapacity
;
109 if (U_FAILURE(err
) || parentLocaleStrings
== NULL
) {
110 U_ASSERT(FALSE
); // we should never end up in here; make sure
111 uhash_close(parentLocaleTable
);
112 parentLocaleTable
= NULL
;
113 ures_close(curBundle
);
114 uprv_free(parentLocaleStrings
);
118 char* nextString
= parentLocaleStrings
;
119 UResourceBundle
* localesForParent
= NULL
; // localesForParent in the next line is an in/out parameter
120 localesForParent
= ures_getNextResource(curBundle
, localesForParent
, &err
);
121 while (U_SUCCESS(err
) && localesForParent
!= NULL
) {
122 const char* parentID
= ures_getKey(localesForParent
);
123 if (nextString
+ uprv_strlen(parentID
) + 1 >= parentLocaleStringsEnd
) {
124 // if we can't build this whole table, we're in trouble
128 uprv_strcpy(nextString
, parentID
);
129 nextString
+= uprv_strlen(parentID
) + 1;
131 int32_t numChildIDs
= ures_getSize(localesForParent
);
132 for (int32_t i
= 0; i
< numChildIDs
; i
++) {
133 int32_t childLength
= parentLocaleStringsEnd
- nextString
- 1;
134 const char* childID
= ures_getUTF8StringByIndex(localesForParent
, i
, nextString
, &childLength
, TRUE
, &err
);
135 nextString
+= childLength
+ 1;
137 if (U_SUCCESS(err
)) {
138 uhash_put(parentLocaleTable
, (void*)childID
, (void*)parentID
, &err
);
140 // again, if we can't build this whole table, we're in trouble
145 localesForParent
= ures_getNextResource(curBundle
, localesForParent
, &err
);
148 ures_close(localesForParent
);
149 ures_close(curBundle
);
152 static void initParentLocaleTable() {
153 umtx_initOnce(parentLocaleInitOnce
, &doInitParentLocaleTable
);
157 * <rdar://problem/63880069>
158 * Currently internal function which should eventually be moved (with new name) to ulocimp.h, or perhaps uloc.h.
159 * Somewhat like uloc_getParent, but only returns a parent from parentLocales data.
161 U_CAPI
int32_t U_EXPORT2
162 ures_getLocParent(const char* localeID
,
164 int32_t parentCapacity
,
169 if (localeID
== NULL
)
170 localeID
= uloc_getDefault();
172 initParentLocaleTable();
173 U_ASSERT(parentLocaleTable
!= NULL
); // should not get NULL here
174 if (parentLocaleTable
!= NULL
) {
175 const char* parentID
= (const char*)uhash_get(parentLocaleTable
, localeID
);
176 if (parentID
!= NULL
) {
177 int32_t parentLen
= uprv_strlen(parentID
);
178 uprv_memcpy(parent
, parentID
, uprv_min(parentLen
, parentCapacity
));
179 return u_terminateChars(parent
, parentCapacity
, parentLen
, err
);
183 // A more general version of this might do the following instead:
184 // return uloc_getParent(localeID, parent, parentCapacity, err);
188 * Internal function, determines the search path for resource bundle files.
189 * Currently, this function is used only by findFirstExisting() to help search for resource bundle files when a bundle for the specified
190 * locale doesn't exist. The code that supports inheritance of resources between existing resource bundle files continues to
191 * use chopLocale() below.
192 * @param name In-out parameter: On input, the locale ID to get a parent locale ID for (this is a locale's base name, without keywords); on output, the
193 * requested parent locale ID.
194 * @param savedRegionCode Pointer to a character array where this function can store the region code from the input locale ID for use by future
195 * calls to this function. The caller doesn't usually use the contents of this array itself; it's just temporary storage for this function. The caller can pass NULL,
196 * but this isn't recommended.
198 static UBool
getParentLocaleID(char *name
, char* savedRegionCode
) {
199 // first look the locale ID up in the parent locale table (if it exists); if that table specifies a parent
200 // for it, return that
201 if (parentLocaleTable
!= NULL
) {
202 const char* parentID
= (const char*)uhash_get(parentLocaleTable
, name
);
203 if (parentID
!= NULL
) {
204 uprv_strcpy(name
, parentID
);
209 // if the parent locale table didn't specify a locale ID for our locale, derive it algorithmically
210 char language
[ULOC_LANG_CAPACITY
];
211 char script
[ULOC_SCRIPT_CAPACITY
];
212 char country
[ULOC_COUNTRY_CAPACITY
];
213 char variant
[ULOC_KEYWORD_AND_VALUES_CAPACITY
];
214 UErrorCode err
= U_ZERO_ERROR
;
216 uloc_getLanguage(name
, language
, ULOC_LANG_CAPACITY
, &err
);
217 uloc_getScript(name
, script
, ULOC_SCRIPT_CAPACITY
, &err
);
218 uloc_getCountry(name
, country
, ULOC_COUNTRY_CAPACITY
, &err
);
219 uloc_getVariant(name
, variant
, ULOC_KEYWORD_AND_VALUES_CAPACITY
, &err
);
221 if (U_SUCCESS(err
)) {
222 // if the locale ID has both script and country codes (and no variant), save the country code in
223 // savedRegionField and chop it off of the locale ID
224 if (variant
[0] == '\0' && script
[0] != '\0' && country
[0] != '\0') {
225 uprv_strcpy(savedRegionCode
, country
);
226 return chopLocale(name
);
229 // if the locale ID has language and script and we have a saved region code, replace the script code
230 // in the locale ID with the saved region code
231 if (variant
[0] == '\0' && country
[0] == '\0' && script
[0] != '\0') {
232 sprintf(name
, "%s_%s", language
, savedRegionCode
);
237 // if we have any other configuration of fields (or couldn't get fields for some reason), clear out the saved
238 // region code and just use chopLocale()
239 *savedRegionCode
= '\0';
240 return chopLocale(name
);
246 static void entryIncrease(UResourceDataEntry
*entry
) {
247 Mutex
lock(&resbMutex
);
248 entry
->fCountExisting
++;
249 while(entry
->fParent
!= NULL
) {
250 entry
= entry
->fParent
;
251 entry
->fCountExisting
++;
256 * Internal function. Tries to find a resource in given Resource
257 * Bundle, as well as in its parents
259 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
260 UResourceDataEntry
*resB
= resBundle
->fData
;
265 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
266 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
269 if(resBundle
->fHasFallback
== TRUE
) {
270 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
271 resB
= resB
->fParent
;
272 if(resB
->fBogus
== U_ZERO_ERROR
) {
274 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
279 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
281 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
282 *status
= U_USING_DEFAULT_WARNING
;
284 *status
= U_USING_FALLBACK_WARNING
;
288 return (&(resB
->fData
));
289 } else { /* If resource is not found, we need to give an error */
290 *status
= U_MISSING_RESOURCE_ERROR
;
294 *status
= U_MISSING_RESOURCE_ERROR
;
300 free_entry(UResourceDataEntry
*entry
) {
301 UResourceDataEntry
*alias
;
302 res_unload(&(entry
->fData
));
303 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
304 uprv_free(entry
->fName
);
306 if(entry
->fPath
!= NULL
) {
307 uprv_free(entry
->fPath
);
309 if(entry
->fPool
!= NULL
) {
310 --entry
->fPool
->fCountExisting
;
312 alias
= entry
->fAlias
;
314 while(alias
->fAlias
!= NULL
) {
315 alias
= alias
->fAlias
;
317 --alias
->fCountExisting
;
322 /* Works just like ucnv_flushCache() */
323 static int32_t ures_flushCache()
325 UResourceDataEntry
*resB
;
327 int32_t rbDeletedNum
= 0;
328 const UHashElement
*e
;
331 /*if shared data hasn't even been lazy evaluated yet
334 Mutex
lock(&resbMutex
);
341 /*creates an enumeration to iterate through every element in the table */
343 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
345 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
346 /* Deletes only if reference counter == 0
347 * Don't worry about the children of this node.
348 * Those will eventually get deleted too, if not already.
349 * Don't worry about the parents of this node.
350 * Those will eventually get deleted too, if not already.
352 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
353 /* some resource bundles are still open somewhere. */
355 if (resB
->fCountExisting
== 0) {
358 uhash_removeElement(cache
, e
);
363 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
364 * got decremented by free_entry().
366 } while(deletedMore
);
374 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
375 UBool cacheNotEmpty
= FALSE
;
376 int32_t pos
= UHASH_FIRST
;
377 const UHashElement
*e
;
378 UResourceDataEntry
*resB
;
380 Mutex
lock(&resbMutex
);
382 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
386 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
388 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
389 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
391 (void*)resB
, resB
->fCountExisting
,
392 resB
->fName
?resB
->fName
:"NULL",
393 resB
->fPath
?resB
->fPath
:"NULL",
396 (void*)resB
->fParent
);
399 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
400 return cacheNotEmpty
;
405 static UBool U_CALLCONV
ures_cleanup(void)
412 if (parentLocaleTable
!= NULL
) {
413 uhash_close(parentLocaleTable
);
414 uprv_free(parentLocaleStrings
);
415 parentLocaleTable
= NULL
;
416 parentLocaleStrings
= NULL
;
417 parentLocaleInitOnce
.reset();
419 gCacheInitOnce
.reset();
423 /** INTERNAL: Initializes the cache for resources */
424 static void U_CALLCONV
createCache(UErrorCode
&status
) {
425 U_ASSERT(cache
== NULL
);
426 cache
= uhash_open(hashEntry
, compareEntries
, NULL
, &status
);
427 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
430 static void initCache(UErrorCode
*status
) {
431 umtx_initOnce(gCacheInitOnce
, &createCache
, *status
);
434 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
436 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
437 int32_t len
= (int32_t)uprv_strlen(name
);
438 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
439 uprv_free(res
->fName
);
441 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
442 res
->fName
= res
->fNameBuffer
;
445 res
->fName
= (char *)uprv_malloc(len
+1);
447 if(res
->fName
== NULL
) {
448 *status
= U_MEMORY_ALLOCATION_ERROR
;
450 uprv_strcpy(res
->fName
, name
);
454 static UResourceDataEntry
*
455 getPoolEntry(const char *path
, UErrorCode
*status
);
458 * INTERNAL: Inits and opens an entry from a data DLL.
459 * CAUTION: resbMutex must be locked when calling this function.
461 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
462 UResourceDataEntry
*r
= NULL
;
463 UResourceDataEntry find
;
464 /*int32_t hashValue;*/
466 char aliasName
[100] = { 0 };
467 int32_t aliasLen
= 0;
468 /*UBool isAlias = FALSE;*/
469 /*UHashTok hashkey; */
471 if(U_FAILURE(*status
)) {
475 /* here we try to deduce the right locale name */
476 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
477 name
= uloc_getDefault();
478 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
479 name
= kRootLocaleName
;
480 } else { /* otherwise, we'll open what we're given */
484 find
.fName
= (char *)name
;
485 find
.fPath
= (char *)path
;
487 /* calculate the hash value of the entry */
488 /*hashkey.pointer = (void *)&find;*/
489 /*hashValue = hashEntry(hashkey);*/
491 /* check to see if we already have this entry */
492 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
494 /* if the entry is not yet in the hash table, we'll try to construct a new one */
495 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
497 *status
= U_MEMORY_ALLOCATION_ERROR
;
501 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
502 /*r->fHashKey = hashValue;*/
504 setEntryName(r
, name
, status
);
505 if (U_FAILURE(*status
)) {
511 r
->fPath
= (char *)uprv_strdup(path
);
512 if(r
->fPath
== NULL
) {
513 *status
= U_MEMORY_ALLOCATION_ERROR
;
519 /* this is the actual loading */
520 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
522 if (U_FAILURE(*status
)) {
523 /* if we failed to load due to an out-of-memory error, exit early. */
524 if (*status
== U_MEMORY_ALLOCATION_ERROR
) {
528 /* we have no such entry in dll, so it will always use fallback */
529 *status
= U_USING_FALLBACK_WARNING
;
530 r
->fBogus
= U_USING_FALLBACK_WARNING
;
531 } else { /* if we have a regular entry */
533 if (r
->fData
.usesPoolBundle
) {
534 r
->fPool
= getPoolEntry(r
->fPath
, status
);
535 if (U_SUCCESS(*status
)) {
536 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
537 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
538 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
539 r
->fData
.poolBundleStrings
= r
->fPool
->fData
.p16BitUnits
;
541 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
547 if (U_SUCCESS(*status
)) {
548 /* handle the alias by trying to get out the %%Alias tag.*/
549 /* We'll try to get alias string from the bundle */
550 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
551 if (aliasres
!= RES_BOGUS
) {
552 // No tracing: called during initial data loading
553 const UChar
*alias
= res_getStringNoTrace(&(r
->fData
), aliasres
, &aliasLen
);
554 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
555 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
556 r
->fAlias
= init_entry(aliasName
, path
, status
);
563 UResourceDataEntry
*oldR
= NULL
;
564 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
565 /* just insert it in the cache */
566 UErrorCode cacheStatus
= U_ZERO_ERROR
;
567 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
568 if (U_FAILURE(cacheStatus
)) {
569 *status
= cacheStatus
;
574 /* somebody have already inserted it while we were working, discard newly opened data */
575 /* Also, we could get here IF we opened an alias */
583 /* return the real bundle */
584 while(r
->fAlias
!= NULL
) {
587 r
->fCountExisting
++; /* we increase its reference count */
588 /* if the resource has a warning */
589 /* we don't want to overwrite a status with no error */
590 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
591 *status
= r
->fBogus
; /* set the returning status */
597 static UResourceDataEntry
*
598 getPoolEntry(const char *path
, UErrorCode
*status
) {
599 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
600 if( U_SUCCESS(*status
) &&
601 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
603 *status
= U_INVALID_FORMAT_ERROR
;
609 /* CAUTION: resbMutex must be locked when calling this function! */
610 static UResourceDataEntry
*
611 findFirstExisting(const char* path
, char* name
,
612 UBool
*isRoot
, UBool
*foundParent
, UBool
*isDefault
, UErrorCode
* status
) {
613 UResourceDataEntry
*r
= NULL
;
614 UBool hasRealData
= FALSE
;
615 const char *defaultLoc
= uloc_getDefault();
616 *foundParent
= TRUE
; /* we're starting with a fresh name */
617 char savedRegionCode
[ULOC_COUNTRY_CAPACITY
] = "";
619 while(*foundParent
&& !hasRealData
) {
620 r
= init_entry(name
, path
, status
);
621 /* Null pointer test */
622 if (U_FAILURE(*status
)) {
625 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
626 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
628 /* this entry is not real. We will discard it. */
629 /* However, the parent line for this entry is */
630 /* not to be used - as there might be parent */
631 /* lines in cache from previous openings that */
632 /* are not updated yet. */
634 /*entryCloseInt(r);*/
636 *status
= U_USING_FALLBACK_WARNING
;
638 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
641 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
643 /*Fallback data stuff*/
645 *foundParent
= getParentLocaleID(name
, savedRegionCode
);
647 // we've already found a real resource file; what we return to the caller is the parent
648 // locale ID for inheritance, which should come from chopLocale(), not getParentLocaleID()
649 *foundParent
= chopLocale(name
);
651 if (*foundParent
&& *name
== '\0') {
652 uprv_strcpy(name
, "und");
658 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
663 resB
->fMagic1
= MAGIC1
;
664 resB
->fMagic2
= MAGIC2
;
668 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
669 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
673 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
674 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
675 ures_setIsStackObject(resB
, TRUE
);
680 StackUResourceBundle::StackUResourceBundle() {
681 ures_initStackObject(&bundle
);
684 StackUResourceBundle::~StackUResourceBundle() {
690 static UBool
// returns U_SUCCESS(*status)
691 loadParentsExceptRoot(UResourceDataEntry
*&t1
,
692 char name
[], int32_t nameCapacity
,
693 UBool usingUSRData
, char usrDataPath
[], UErrorCode
*status
) {
694 if (U_FAILURE(*status
)) { return FALSE
; }
695 UBool hasChopped
= TRUE
;
696 while (hasChopped
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
&&
697 res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
698 Resource parentRes
= res_getResource(&t1
->fData
, "%%Parent");
699 if (parentRes
!= RES_BOGUS
) { // An explicit parent was found.
700 int32_t parentLocaleLen
= 0;
701 // No tracing: called during initial data loading
702 const UChar
*parentLocaleName
= res_getStringNoTrace(&(t1
->fData
), parentRes
, &parentLocaleLen
);
703 if(parentLocaleName
!= NULL
&& 0 < parentLocaleLen
&& parentLocaleLen
< nameCapacity
) {
704 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+ 1);
705 if (uprv_strcmp(name
, kRootLocaleName
) == 0) {
710 // Insert regular parents.
711 UErrorCode parentStatus
= U_ZERO_ERROR
;
712 UResourceDataEntry
*t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
713 if (U_FAILURE(parentStatus
)) {
714 *status
= parentStatus
;
717 UResourceDataEntry
*u2
= NULL
;
718 UErrorCode usrStatus
= U_ZERO_ERROR
;
719 if (usingUSRData
) { // This code inserts user override data into the inheritance chain.
720 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
721 // If we failed due to out-of-memory, report that to the caller and exit early.
722 if (usrStatus
== U_MEMORY_ALLOCATION_ERROR
) {
728 if (usingUSRData
&& U_SUCCESS(usrStatus
) && u2
->fBogus
== U_ZERO_ERROR
) {
734 // The USR override data wasn't found, set it to be deleted.
735 u2
->fCountExisting
= 0;
739 hasChopped
= chopLocale(name
);
744 static UBool
// returns U_SUCCESS(*status)
745 insertRootBundle(UResourceDataEntry
*&t1
, UErrorCode
*status
) {
746 if (U_FAILURE(*status
)) { return FALSE
; }
747 UErrorCode parentStatus
= U_ZERO_ERROR
;
748 UResourceDataEntry
*t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
749 if (U_FAILURE(parentStatus
)) {
750 *status
= parentStatus
;
760 * Open a resource bundle for the locale;
761 * if there is not even a base language bundle, then fall back to the default locale;
762 * if there is no bundle for that either, then load the root bundle.
764 * This is the default bundle loading behavior.
766 URES_OPEN_LOCALE_DEFAULT_ROOT
,
767 // TODO: ICU ticket #11271 "consistent default locale across locale trees"
768 // Add an option to look at the main locale tree for whether to
769 // fall back to root directly (if the locale has main data) or
770 // fall back to the default locale first (if the locale does not even have main data).
772 * Open a resource bundle for the locale;
773 * if there is not even a base language bundle, then load the root bundle;
774 * never fall back to the default locale.
776 * This is used for algorithms that have good pan-Unicode default behavior,
777 * such as case mappings, collation, and segmentation (BreakIterator).
779 URES_OPEN_LOCALE_ROOT
,
781 * Open a resource bundle for the exact bundle name as requested;
782 * no fallbacks, do not load parent bundles.
784 * This is used for supplemental (non-locale) data.
788 typedef enum UResOpenType UResOpenType
;
790 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
,
791 UResOpenType openType
, UErrorCode
* status
) {
792 U_ASSERT(openType
!= URES_OPEN_DIRECT
);
793 UErrorCode intStatus
= U_ZERO_ERROR
;
794 UResourceDataEntry
*r
= NULL
;
795 UResourceDataEntry
*t1
= NULL
;
796 UBool isDefault
= FALSE
;
797 UBool isRoot
= FALSE
;
798 UBool hasRealData
= FALSE
;
799 UBool hasChopped
= TRUE
;
800 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
802 char name
[ULOC_FULLNAME_CAPACITY
];
803 char usrDataPath
[96];
806 initParentLocaleTable();
808 if(U_FAILURE(*status
)) {
812 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
813 name
[sizeof(name
) - 1] = 0;
815 if ( usingUSRData
) {
816 if ( path
== NULL
) {
817 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
819 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
820 usrDataPath
[0] = 'u';
821 usrDataPath
[1] = 's';
822 usrDataPath
[2] = 'r';
823 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
827 Mutex
lock(&resbMutex
); // Lock resbMutex until the end of this function.
829 /* We're going to skip all the locales that do not have any data */
830 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
832 // If we failed due to out-of-memory, report the failure and exit early.
833 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
838 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
841 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
842 UErrorCode usrStatus
= U_ZERO_ERROR
;
843 UResourceDataEntry
*u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
844 // If we failed due to out-of-memory, report the failure and exit early.
845 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
850 if(u1
->fBogus
== U_ZERO_ERROR
) {
854 /* the USR override data wasn't found, set it to be deleted */
855 u1
->fCountExisting
= 0;
859 if (hasChopped
&& !isRoot
) {
860 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
866 /* we could have reached this point without having any real data */
867 /* if that is the case, we need to chain in the default locale */
868 if(r
==NULL
&& openType
== URES_OPEN_LOCALE_DEFAULT_ROOT
&& !isDefault
&& !isRoot
) {
869 /* insert default locale */
870 uprv_strcpy(name
, uloc_getDefault());
871 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
872 // If we failed due to out-of-memory, report the failure and exit early.
873 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
877 intStatus
= U_USING_DEFAULT_WARNING
;
878 if(r
!= NULL
) { /* the default locale exists */
882 // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
883 if (hasChopped
&& !isRoot
) {
884 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
891 /* we could still have r == NULL at this point - maybe even default locale is not */
894 uprv_strcpy(name
, kRootLocaleName
);
895 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
896 // If we failed due to out-of-memory, report the failure and exit early.
897 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
903 intStatus
= U_USING_DEFAULT_WARNING
;
905 } else { /* we don't even have the root locale */
906 *status
= U_MISSING_RESOURCE_ERROR
;
909 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 &&
910 t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
911 if (!insertRootBundle(t1
, status
)) {
915 r
->fBogus
= U_USING_DEFAULT_WARNING
;
919 // TODO: Does this ever loop?
920 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
921 t1
->fParent
->fCountExisting
++;
926 if(U_SUCCESS(*status
)) {
927 if(intStatus
!= U_ZERO_ERROR
) {
937 * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
939 * Parent and root locale bundles are loaded if
940 * the requested bundle does not have the "nofallback" flag.
942 static UResourceDataEntry
*
943 entryOpenDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
945 if(U_FAILURE(*status
)) {
949 Mutex
lock(&resbMutex
);
950 // findFirstExisting() without fallbacks.
951 UResourceDataEntry
*r
= init_entry(localeID
, path
, status
);
952 if(U_SUCCESS(*status
)) {
953 if(r
->fBogus
!= U_ZERO_ERROR
) {
961 // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
962 // unless it is marked with "nofallback".
963 UResourceDataEntry
*t1
= r
;
964 if(r
!= NULL
&& uprv_strcmp(localeID
, kRootLocaleName
) != 0 && // not root
965 r
->fParent
== NULL
&& !r
->fData
.noFallback
&&
966 uprv_strlen(localeID
) < ULOC_FULLNAME_CAPACITY
) {
967 char name
[ULOC_FULLNAME_CAPACITY
];
968 uprv_strcpy(name
, localeID
);
969 if(!chopLocale(name
) || uprv_strcmp(name
, kRootLocaleName
) == 0 ||
970 loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), FALSE
, NULL
, status
)) {
971 if(uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
) {
972 insertRootBundle(t1
, status
);
975 if(U_FAILURE(*status
)) {
981 // TODO: Does this ever loop?
982 while(t1
->fParent
!= NULL
) {
983 t1
->fParent
->fCountExisting
++;
991 * Functions to create and destroy resource bundles.
992 * CAUTION: resbMutex must be locked when calling this function.
995 static void entryCloseInt(UResourceDataEntry
*resB
) {
996 UResourceDataEntry
*p
= resB
;
998 while(resB
!= NULL
) {
1000 resB
->fCountExisting
--;
1002 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
1005 if(resB->fCountExisting <= 0) {
1006 uhash_remove(cache, resB);
1007 if(resB->fBogus == U_ZERO_ERROR) {
1008 res_unload(&(resB->fData));
1010 if(resB->fName != NULL) {
1011 uprv_free(resB->fName);
1013 if(resB->fPath != NULL) {
1014 uprv_free(resB->fPath);
1025 * API: closes a resource bundle and cleans up.
1028 static void entryClose(UResourceDataEntry
*resB
) {
1029 Mutex
lock(&resbMutex
);
1030 entryCloseInt(resB
);
1034 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
1035 if(resB->fResPath == NULL) {
1036 resB->fResPath = resB->fResBuf;
1037 *(resB->fResPath) = 0;
1039 resB->fResPathLen = uprv_strlen(toAdd);
1040 if(RES_BUFSIZE <= resB->fResPathLen+1) {
1041 if(resB->fResPath == resB->fResBuf) {
1042 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
1044 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
1047 uprv_strcpy(resB->fResPath, toAdd);
1050 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
1051 int32_t resPathLenOrig
= resB
->fResPathLen
;
1052 if(resB
->fResPath
== NULL
) {
1053 resB
->fResPath
= resB
->fResBuf
;
1054 *(resB
->fResPath
) = 0;
1055 resB
->fResPathLen
= 0;
1057 resB
->fResPathLen
+= lenToAdd
;
1058 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
1059 if(resB
->fResPath
== resB
->fResBuf
) {
1060 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
1061 /* Check that memory was allocated correctly. */
1062 if (resB
->fResPath
== NULL
) {
1063 *status
= U_MEMORY_ALLOCATION_ERROR
;
1066 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
1068 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
1069 /* Check that memory was reallocated correctly. */
1071 *status
= U_MEMORY_ALLOCATION_ERROR
;
1074 resB
->fResPath
= temp
;
1077 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
1080 static void ures_freeResPath(UResourceBundle
*resB
) {
1081 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
1082 uprv_free(resB
->fResPath
);
1084 resB
->fResPath
= NULL
;
1085 resB
->fResPathLen
= 0;
1089 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
1092 if(resB
->fData
!= NULL
) {
1093 entryClose(resB
->fData
);
1095 if(resB
->fVersion
!= NULL
) {
1096 uprv_free(resB
->fVersion
);
1098 ures_freeResPath(resB
);
1100 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
1105 /* poison the data */
1106 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
1112 U_CAPI
void U_EXPORT2
1113 ures_close(UResourceBundle
* resB
)
1115 ures_closeBundle(resB
, TRUE
);
1118 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
1119 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
1120 const UResourceBundle
*parent
, int32_t noAlias
,
1121 UResourceBundle
*resB
, UErrorCode
*status
)
1123 if(status
== NULL
|| U_FAILURE(*status
)) {
1126 if (parent
== NULL
) {
1127 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1130 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
1131 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
1133 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
1135 /* we have an alias, now let's cut it up */
1136 char stackAlias
[200];
1137 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
1141 * Allocate enough space for both the char * version
1142 * of the alias and parent->fResPath.
1144 * We do this so that res_findResource() can modify the path,
1145 * which allows us to remove redundant _res_findResource() variants
1147 * res_findResource() now NUL-terminates each segment so that table keys
1148 * can always be compared with strcmp() instead of strncmp().
1149 * Saves code there and simplifies testing and code coverage.
1153 ++len
; /* count the terminating NUL */
1154 if(parent
->fResPath
!= NULL
) {
1155 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
1159 if(capacity
< len
) {
1162 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
1163 capacity
= (int32_t)sizeof(stackAlias
);
1164 chAlias
= stackAlias
;
1166 chAlias
= (char *)uprv_malloc(capacity
);
1168 if(chAlias
== NULL
) {
1169 *status
= U_MEMORY_ALLOCATION_ERROR
;
1173 u_UCharsToChars(alias
, chAlias
, len
);
1175 if(*chAlias
== RES_PATH_SEPARATOR
) {
1176 /* there is a path included */
1177 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
1178 if(locale
== NULL
) {
1179 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
1185 if(uprv_strcmp(path
, "LOCALE") == 0) {
1186 /* this is an XPath alias, starting with "/LOCALE/" */
1187 /* it contains the path to a resource which should be looked up */
1188 /* starting in the requested locale */
1190 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
1191 path
= realData
->fPath
; /* we will be looking in the same package */
1193 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
1196 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1203 /* no path, start with a locale */
1205 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1210 path
= realData
->fPath
;
1215 /* got almost everything, let's try to open */
1216 /* first, open the bundle with real data */
1217 UResourceBundle
*result
= resB
;
1218 const char* temp
= NULL
;
1219 UErrorCode intStatus
= U_ZERO_ERROR
;
1220 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
1221 if(U_SUCCESS(intStatus
)) {
1222 if(keyPath
== NULL
) {
1223 /* no key path. This means that we are going to
1224 * to use the corresponding resource from
1227 /* first, we are going to get a corresponding parent
1228 * resource to the one we are searching.
1230 char *aKey
= parent
->fResPath
;
1232 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
1234 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
1239 /* we need to make keyPath from parent's fResPath and
1240 * current key, if there is a key associated
1242 len
= (int32_t)(uprv_strlen(key
) + 1);
1243 if(len
> capacity
) {
1245 if(chAlias
== stackAlias
) {
1246 chAlias
= (char *)uprv_malloc(capacity
);
1248 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
1250 if(chAlias
== NULL
) {
1251 ures_close(mainRes
);
1252 *status
= U_MEMORY_ALLOCATION_ERROR
;
1256 uprv_memcpy(chAlias
, key
, len
);
1258 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
1259 } else if(idx
!= -1) {
1260 /* if there is no key, but there is an index, try to get by the index */
1261 /* here we have either a table or an array, so get the element */
1262 int32_t type
= RES_GET_TYPE(r
);
1263 if(URES_IS_TABLE(type
)) {
1264 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
1265 } else { /* array */
1266 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
1269 if(r
!= RES_BOGUS
) {
1270 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
1272 *status
= U_MISSING_RESOURCE_ERROR
;
1276 /* this one is a bit trickier.
1277 * we start finding keys, but after we resolve one alias, the path might continue.
1279 * aliastest:alias { "testtypes/anotheralias/Sequence" }
1280 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1281 * aliastest resource should finally have the sequence, not collation elements.
1283 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1284 char stackPath
[URES_MAX_BUFFER_SIZE
];
1285 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1286 if(uprv_strlen(keyPath
) >= UPRV_LENGTHOF(stackPath
)) {
1287 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1288 if(pathBuf
== NULL
) {
1289 *status
= U_MEMORY_ALLOCATION_ERROR
;
1290 ures_close(mainRes
);
1294 uprv_strcpy(pathBuf
, keyPath
);
1296 /* now we have fallback following here */
1298 r
= dataEntry
->fData
.rootRes
;
1299 /* this loop handles 'found' resources over several levels */
1300 while(*myPath
&& U_SUCCESS(*status
)) {
1301 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1302 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1303 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1306 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1307 dataEntry
= result
->fData
;
1309 } else { /* no resource found, we don't really want to look anymore on this level */
1313 dataEntry
= dataEntry
->fParent
;
1314 uprv_strcpy(pathBuf
, keyPath
);
1316 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1317 if(r
== RES_BOGUS
) {
1318 *status
= U_MISSING_RESOURCE_ERROR
;
1321 if(pathBuf
!= stackPath
) {
1325 } else { /* we failed to open the resource we're aliasing to */
1326 *status
= intStatus
;
1328 if(chAlias
!= stackAlias
) {
1331 if(mainRes
!= result
) {
1332 ures_close(mainRes
);
1334 ResourceTracer(resB
).maybeTrace("getalias");
1338 /* bad alias, should be an error */
1339 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1343 *status
= U_TOO_MANY_ALIASES_ERROR
;
1348 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1351 *status
= U_MEMORY_ALLOCATION_ERROR
;
1354 ures_setIsStackObject(resB
, FALSE
);
1355 resB
->fResPath
= NULL
;
1356 resB
->fResPathLen
= 0;
1358 if(resB
->fData
!= NULL
) {
1359 entryClose(resB
->fData
);
1361 if(resB
->fVersion
!= NULL
) {
1362 uprv_free(resB
->fVersion
);
1365 weiv: if stack object was passed in, it doesn't really need to be reinited,
1366 since the purpose of initing is to remove stack junk. However, at this point
1367 we would not do anything to an allocated object, so stack object should be
1371 if(ures_isStackObject(resB) != FALSE) {
1372 ures_initStackObject(resB);
1375 if(parent
!= resB
) {
1376 ures_freeResPath(resB
);
1379 resB
->fData
= realData
;
1380 entryIncrease(resB
->fData
);
1381 resB
->fHasFallback
= FALSE
;
1382 resB
->fIsTopLevel
= FALSE
;
1385 /*resB->fParentRes = parent;*/
1386 resB
->fTopLevelData
= parent
->fTopLevelData
;
1387 if(parent
->fResPath
&& parent
!= resB
) {
1388 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1391 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1392 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1393 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1395 } else if(idx
>= 0) {
1397 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1398 ures_appendResPath(resB
, buf
, len
, status
);
1399 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1400 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1403 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1405 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1406 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1409 resB
->fVersion
= NULL
;
1411 /*resB->fParent = parent->fRes;*/
1412 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1413 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1414 ResourceTracer(resB
).trace("get");
1418 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1419 UBool isStackObject
;
1420 if(U_FAILURE(*status
) || r
== original
) {
1423 if(original
!= NULL
) {
1425 isStackObject
= FALSE
;
1426 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1429 *status
= U_MEMORY_ALLOCATION_ERROR
;
1433 isStackObject
= ures_isStackObject(r
);
1434 ures_closeBundle(r
, FALSE
);
1436 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1439 if(original
->fResPath
) {
1440 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1442 ures_setIsStackObject(r
, isStackObject
);
1443 if(r
->fData
!= NULL
) {
1444 entryIncrease(r
->fData
);
1451 * Functions to retrieve data from resource bundles.
1454 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1456 if (status
==NULL
|| U_FAILURE(*status
)) {
1460 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1463 s
= res_getString({resB
}, &(resB
->fResData
), resB
->fRes
, len
);
1465 *status
= U_RESOURCE_TYPE_MISMATCH
;
1471 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1472 char *dest
, int32_t *pLength
,
1474 UErrorCode
*status
) {
1477 if (U_FAILURE(*status
)) {
1480 if (pLength
!= NULL
) {
1481 capacity
= *pLength
;
1485 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1486 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1490 if (length16
== 0) {
1491 /* empty string, return as read-only pointer */
1492 if (pLength
!= NULL
) {
1496 u_terminateChars(dest
, capacity
, 0, status
);
1502 /* We need to transform the string to the destination buffer. */
1503 if (capacity
< length16
) {
1504 /* No chance for the string to fit. Pure preflighting. */
1505 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1507 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1509 * We know the string will fit into dest because each UChar turns
1510 * into at most three UTF-8 bytes. Fill the latter part of dest
1511 * so that callers do not expect to use dest as a string pointer,
1512 * hopefully leading to more robust code for when resource bundles
1513 * may store UTF-8 natively.
1514 * (In which case dest would not be used at all.)
1516 * We do not do this if forceCopy=TRUE because then the caller
1517 * expects the string to start exactly at dest.
1519 * The test above for <= 0x2aaaaaaa prevents overflows.
1520 * The +1 is for the NUL terminator.
1522 int32_t maxLength
= 3 * length16
+ 1;
1523 if (capacity
> maxLength
) {
1524 dest
+= capacity
- maxLength
;
1525 capacity
= maxLength
;
1528 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1532 U_CAPI
const char * U_EXPORT2
1533 ures_getUTF8String(const UResourceBundle
*resB
,
1534 char *dest
, int32_t *pLength
,
1536 UErrorCode
*status
) {
1538 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1539 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1542 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1543 UErrorCode
* status
) {
1545 if (status
==NULL
|| U_FAILURE(*status
)) {
1549 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1552 p
= res_getBinary({resB
}, &(resB
->fResData
), resB
->fRes
, len
);
1554 *status
= U_RESOURCE_TYPE_MISMATCH
;
1559 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1560 UErrorCode
* status
) {
1562 if (status
==NULL
|| U_FAILURE(*status
)) {
1566 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1569 p
= res_getIntVector({resB
}, &(resB
->fResData
), resB
->fRes
, len
);
1571 *status
= U_RESOURCE_TYPE_MISMATCH
;
1576 /* this function returns a signed integer */
1577 /* it performs sign extension */
1578 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1579 if (status
==NULL
|| U_FAILURE(*status
)) {
1583 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1586 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1587 *status
= U_RESOURCE_TYPE_MISMATCH
;
1590 return res_getInt({resB
}, resB
->fRes
);
1593 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1594 if (status
==NULL
|| U_FAILURE(*status
)) {
1598 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1601 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1602 *status
= U_RESOURCE_TYPE_MISMATCH
;
1605 return res_getUInt({resB
}, resB
->fRes
);
1608 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1612 return res_getPublicType(resB
->fRes
);
1615 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1617 // TODO: Trace ures_getKey? I guess not usually.
1619 // We usually get the key string to decide whether we want the value, or to
1620 // make a key-value pair. Tracing the value should suffice.
1622 // However, I believe we have some data (e.g., in res_index) where the key
1623 // strings are the data. Tracing the enclosing table should suffice.
1631 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1639 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1640 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1641 const UChar
* result
= 0;
1642 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1643 result
= ures_getString(tempRes
, len
, status
);
1644 ures_close(tempRes
);
1647 return res_getString({resB
, sIndex
}, &(resB
->fResData
), r
, len
);
1651 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1658 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1662 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1665 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1666 Resource r
= RES_BOGUS
;
1668 if (status
==NULL
|| U_FAILURE(*status
)) {
1672 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1676 if(resB
->fIndex
== resB
->fSize
-1) {
1677 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1680 switch(RES_GET_TYPE(resB
->fRes
)) {
1682 case URES_STRING_V2
:
1683 return res_getString({resB
}, &(resB
->fResData
), resB
->fRes
, len
);
1687 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1688 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1689 /* TODO: do the fallback */
1691 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1694 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1695 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1696 /* TODO: do the fallback */
1698 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1700 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1703 case URES_INT_VECTOR
:
1704 *status
= U_RESOURCE_TYPE_MISMATCH
;
1714 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1715 const char *key
= NULL
;
1716 Resource r
= RES_BOGUS
;
1718 if (status
==NULL
|| U_FAILURE(*status
)) {
1723 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1728 if(resB
->fIndex
== resB
->fSize
-1) {
1729 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1733 switch(RES_GET_TYPE(resB
->fRes
)) {
1737 case URES_STRING_V2
:
1738 case URES_INT_VECTOR
:
1739 return ures_copyResb(fillIn
, resB
, status
);
1743 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1744 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1745 /* TODO: do the fallback */
1747 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1750 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1751 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1752 /* TODO: do the fallback */
1754 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1764 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1765 const char* key
= NULL
;
1766 Resource r
= RES_BOGUS
;
1768 if (status
==NULL
|| U_FAILURE(*status
)) {
1773 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1778 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1779 switch(RES_GET_TYPE(resB
->fRes
)) {
1783 case URES_STRING_V2
:
1784 case URES_INT_VECTOR
:
1785 return ures_copyResb(fillIn
, resB
, status
);
1789 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1790 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1791 /* TODO: do the fallback */
1793 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1796 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1797 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1798 /* TODO: do the fallback */
1800 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1806 *status
= U_MISSING_RESOURCE_ERROR
;
1812 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1813 const char* key
= NULL
;
1814 Resource r
= RES_BOGUS
;
1816 if (status
==NULL
|| U_FAILURE(*status
)) {
1820 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1824 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1825 switch(RES_GET_TYPE(resB
->fRes
)) {
1827 case URES_STRING_V2
:
1828 return res_getString({resB
}, &(resB
->fResData
), resB
->fRes
, len
);
1832 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1833 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1834 /* TODO: do the fallback */
1836 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1839 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1840 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1841 /* TODO: do the fallback */
1843 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1845 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1848 case URES_INT_VECTOR
:
1849 *status
= U_RESOURCE_TYPE_MISMATCH
;
1852 /* must not occur */
1853 *status
= U_INTERNAL_PROGRAM_ERROR
;
1857 *status
= U_MISSING_RESOURCE_ERROR
;
1862 U_CAPI
const char * U_EXPORT2
1863 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1865 char *dest
, int32_t *pLength
,
1867 UErrorCode
*status
) {
1869 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1870 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1873 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1874 return resB->fResPath;
1877 U_CAPI UResourceBundle
* U_EXPORT2
1878 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1880 UResourceBundle
*first
= NULL
;
1881 UResourceBundle
*result
= fillIn
;
1882 char *packageName
= NULL
;
1883 char *pathToResource
= NULL
, *save
= NULL
;
1884 char *locale
= NULL
, *localeEnd
= NULL
;
1887 if(status
== NULL
|| U_FAILURE(*status
)) {
1891 length
= (int32_t)(uprv_strlen(path
)+1);
1892 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1894 if(pathToResource
== NULL
) {
1895 *status
= U_MEMORY_ALLOCATION_ERROR
;
1898 uprv_memcpy(pathToResource
, path
, length
);
1900 locale
= pathToResource
;
1901 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1903 packageName
= pathToResource
;
1904 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1905 if(pathToResource
== NULL
) {
1906 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1908 *pathToResource
= 0;
1909 locale
= pathToResource
+1;
1913 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1914 if(localeEnd
!= NULL
) {
1918 first
= ures_open(packageName
, locale
, status
);
1920 if(U_SUCCESS(*status
)) {
1922 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1924 result
= ures_copyResb(fillIn
, first
, status
);
1932 U_CAPI UResourceBundle
* U_EXPORT2
1933 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1935 Resource res
= RES_BOGUS
;
1936 UResourceBundle
*result
= fillIn
;
1939 if(status
== NULL
|| U_FAILURE(*status
)) {
1943 /* here we do looping and circular alias checking */
1944 /* this loop is here because aliasing is resolved on this level, not on res level */
1945 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1947 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1948 if(res
!= RES_BOGUS
) {
1949 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1952 *status
= U_MISSING_RESOURCE_ERROR
;
1955 } while(*path
); /* there is more stuff in the path */
1959 U_INTERNAL
const UChar
* U_EXPORT2
1960 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1963 UErrorCode
*status
) {
1965 UResourceBundle stack
;
1966 const UChar
* retVal
= NULL
;
1967 ures_initStackObject(&stack
);
1968 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1970 retVal
= ures_getString(&stack
, &length
, status
);
1972 if (U_FAILURE(*status
)) {
1975 if (length
== 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1978 *status
= U_MISSING_RESOURCE_ERROR
;
1987 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1989 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1990 Resource resource
= table
; /* The current resource */
1991 icu::CharString path
;
1992 UErrorCode errorCode
= U_ZERO_ERROR
;
1993 path
.append(key
, errorCode
);
1994 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1995 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1996 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1997 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1998 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1999 if (nextPathPart
!= NULL
) {
2000 *nextPathPart
= 0; /* Terminating null for this part of path. */
2003 nextPathPart
= uprv_strchr(pathPart
, 0);
2006 const char *pathP
= pathPart
;
2007 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
2008 type
= (UResType
)RES_GET_TYPE(resource
);
2009 pathPart
= nextPathPart
;
2017 U_CAPI UResourceBundle
* U_EXPORT2
2018 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
2020 UResourceBundle
*fillIn
,
2021 UErrorCode
*status
) {
2022 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
2023 /*UResourceDataEntry *realData = NULL;*/
2024 UResourceBundle
*helper
= NULL
;
2026 if (status
==NULL
|| U_FAILURE(*status
)) {
2030 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2034 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2035 if(URES_IS_TABLE(type
)) {
2036 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
2037 const char* key
= inKey
;
2038 if(res
== RES_BOGUS
) {
2039 UResourceDataEntry
*dataEntry
= resB
->fData
;
2041 char *myPath
= NULL
;
2042 const char* resPath
= resB
->fResPath
;
2043 int32_t len
= resB
->fResPathLen
;
2044 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
2045 dataEntry
= dataEntry
->fParent
;
2046 rootRes
= dataEntry
->fData
.rootRes
;
2048 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
2051 path
.append(resPath
, len
, *status
);
2053 path
.append(inKey
, *status
);
2054 if (U_FAILURE(*status
)) {
2058 myPath
= path
.data();
2061 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
2062 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
2063 /* We hit an alias, but we didn't finish following the path. */
2064 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
2065 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
2067 dataEntry
= helper
->fData
;
2068 rootRes
= helper
->fRes
;
2069 resPath
= helper
->fResPath
;
2070 len
= helper
->fResPathLen
;
2076 } while(res
!= RES_BOGUS
&& *myPath
); /* Continue until the whole path is consumed */
2079 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
2080 if(res
!= RES_BOGUS
) {
2081 /* check if resB->fResPath gives the right name here */
2082 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
2083 *status
= U_USING_DEFAULT_WARNING
;
2085 *status
= U_USING_FALLBACK_WARNING
;
2088 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
2090 *status
= U_MISSING_RESOURCE_ERROR
;
2093 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
2097 *status
= U_RESOURCE_TYPE_MISMATCH
;
2105 void getAllItemsWithFallback(
2106 const UResourceBundle
*bundle
, ResourceDataValue
&value
,
2108 UErrorCode
&errorCode
) {
2109 if (U_FAILURE(errorCode
)) { return; }
2110 // We recursively enumerate child-first,
2111 // only storing parent items in the absence of child items.
2112 // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
2113 // to prevent a parent item from being stored.
2115 // It would be possible to recursively enumerate parent-first,
2116 // overriding parent items with child items.
2117 // When the sink sees the no-fallback/no-inheritance marker,
2118 // then it would remove the parent's item.
2119 // We would deserialize parent values even though they are overridden in a child bundle.
2120 value
.setData(&bundle
->fResData
);
2121 UResourceDataEntry
*parentEntry
= bundle
->fData
->fParent
;
2122 UBool hasParent
= parentEntry
!= NULL
&& U_SUCCESS(parentEntry
->fBogus
);
2123 value
.setResource(bundle
->fRes
, ResourceTracer(bundle
));
2124 sink
.put(bundle
->fKey
, value
, !hasParent
, errorCode
);
2126 // We might try to query the sink whether
2127 // any fallback from the parent bundle is still possible.
2129 // Turn the parent UResourceDataEntry into a UResourceBundle,
2130 // much like in ures_openWithType().
2131 // TODO: See if we can refactor ures_getByKeyWithFallback()
2132 // and pull out an inner function that takes and returns a UResourceDataEntry
2133 // so that we need not create UResourceBundle objects.
2134 UResourceBundle parentBundle
;
2135 ures_initStackObject(&parentBundle
);
2136 parentBundle
.fTopLevelData
= parentBundle
.fData
= parentEntry
;
2137 // TODO: What is the difference between bundle fData and fTopLevelData?
2138 uprv_memcpy(&parentBundle
.fResData
, &parentEntry
->fData
, sizeof(ResourceData
));
2139 // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
2140 parentBundle
.fHasFallback
= !parentBundle
.fResData
.noFallback
;
2141 parentBundle
.fIsTopLevel
= TRUE
;
2142 parentBundle
.fRes
= parentBundle
.fResData
.rootRes
;
2143 parentBundle
.fSize
= res_countArrayItems(&(parentBundle
.fResData
), parentBundle
.fRes
);
2144 parentBundle
.fIndex
= -1;
2145 entryIncrease(parentEntry
);
2147 // Look up the container item in the parent bundle.
2148 UResourceBundle containerBundle
;
2149 ures_initStackObject(&containerBundle
);
2150 const UResourceBundle
*rb
;
2151 UErrorCode pathErrorCode
= U_ZERO_ERROR
; // Ignore if parents up to root do not have this path.
2152 if (bundle
->fResPath
== NULL
|| *bundle
->fResPath
== 0) {
2155 rb
= ures_getByKeyWithFallback(&parentBundle
, bundle
->fResPath
,
2156 &containerBundle
, &pathErrorCode
);
2158 if (U_SUCCESS(pathErrorCode
)) {
2159 getAllItemsWithFallback(rb
, value
, sink
, errorCode
);
2161 ures_close(&containerBundle
);
2162 ures_close(&parentBundle
);
2168 // Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue.
2169 // Unfortunately, the caller must know which subclass to make and pass in.
2170 // Alternatively, we could make it as polymorphic as in Java by
2171 // returning a ResourceValue pointer (possibly wrapped into a LocalPointer)
2172 // that the caller then owns.
2174 // Also requires a UResourceBundle fill-in, so that the value's ResourceTracer
2175 // can point to a non-local bundle.
2176 // Without tracing, the child bundle could be a function-local object.
2177 U_CAPI
void U_EXPORT2
2178 ures_getValueWithFallback(const UResourceBundle
*bundle
, const char *path
,
2179 UResourceBundle
*tempFillIn
,
2180 ResourceDataValue
&value
, UErrorCode
&errorCode
) {
2181 if (U_FAILURE(errorCode
)) { return; }
2182 if (path
== nullptr) {
2183 errorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2186 const UResourceBundle
*rb
;
2191 rb
= ures_getByKeyWithFallback(bundle
, path
, tempFillIn
, &errorCode
);
2192 if (U_FAILURE(errorCode
)) {
2196 value
.setData(&rb
->fResData
);
2197 value
.setResource(rb
->fRes
, ResourceTracer(rb
));
2200 U_CAPI
void U_EXPORT2
2201 ures_getAllItemsWithFallback(const UResourceBundle
*bundle
, const char *path
,
2202 icu::ResourceSink
&sink
, UErrorCode
&errorCode
) {
2203 if (U_FAILURE(errorCode
)) { return; }
2204 if (path
== nullptr) {
2205 errorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2208 StackUResourceBundle stackBundle
;
2209 const UResourceBundle
*rb
;
2214 rb
= ures_getByKeyWithFallback(bundle
, path
, stackBundle
.getAlias(), &errorCode
);
2215 if (U_FAILURE(errorCode
)) {
2219 // Get all table items with fallback.
2220 ResourceDataValue value
;
2221 getAllItemsWithFallback(rb
, value
, sink
, errorCode
);
2224 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
2225 Resource res
= RES_BOGUS
;
2226 UResourceDataEntry
*realData
= NULL
;
2227 const char *key
= inKey
;
2229 if (status
==NULL
|| U_FAILURE(*status
)) {
2233 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2237 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2238 if(URES_IS_TABLE(type
)) {
2240 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2241 if(res
== RES_BOGUS
) {
2243 if(resB
->fHasFallback
== TRUE
) {
2244 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2245 if(U_SUCCESS(*status
)) {
2246 /* check if resB->fResPath gives the right name here */
2247 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
2249 *status
= U_MISSING_RESOURCE_ERROR
;
2252 *status
= U_MISSING_RESOURCE_ERROR
;
2255 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
2259 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2261 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2262 /* here should go a first attempt to locate the key using index table */
2263 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2264 if(U_SUCCESS(*status
)) {
2265 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
2267 *status
= U_MISSING_RESOURCE_ERROR
;
2272 *status
= U_RESOURCE_TYPE_MISMATCH
;
2277 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
2278 Resource res
= RES_BOGUS
;
2279 UResourceDataEntry
*realData
= NULL
;
2280 const char* key
= inKey
;
2282 if (status
==NULL
|| U_FAILURE(*status
)) {
2286 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2290 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2291 if(URES_IS_TABLE(type
)) {
2294 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2296 if(res
== RES_BOGUS
) {
2298 if(resB
->fHasFallback
== TRUE
) {
2299 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2300 if(U_SUCCESS(*status
)) {
2301 switch (RES_GET_TYPE(res
)) {
2303 case URES_STRING_V2
:
2304 return res_getString({resB
, key
}, rd
, res
, len
);
2307 const UChar
* result
= 0;
2308 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2309 result
= ures_getString(tempRes
, len
, status
);
2310 ures_close(tempRes
);
2314 *status
= U_RESOURCE_TYPE_MISMATCH
;
2317 *status
= U_MISSING_RESOURCE_ERROR
;
2320 *status
= U_MISSING_RESOURCE_ERROR
;
2323 switch (RES_GET_TYPE(res
)) {
2325 case URES_STRING_V2
:
2326 return res_getString({resB
, key
}, &(resB
->fResData
), res
, len
);
2329 const UChar
* result
= 0;
2330 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2331 result
= ures_getString(tempRes
, len
, status
);
2332 ures_close(tempRes
);
2336 *status
= U_RESOURCE_TYPE_MISMATCH
;
2341 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2343 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2344 /* here should go a first attempt to locate the key using index table */
2345 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2346 if(U_SUCCESS(*status
)) {
2348 return res_getString(rd
, res
, len
);
2350 *status
= U_MISSING_RESOURCE_ERROR
;
2355 *status
= U_RESOURCE_TYPE_MISMATCH
;
2360 U_CAPI
const char * U_EXPORT2
2361 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
2363 char *dest
, int32_t *pLength
,
2365 UErrorCode
*status
) {
2367 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
2368 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
2371 /* TODO: clean from here down */
2374 * INTERNAL: Get the name of the first real locale (not placeholder)
2375 * that has resource bundle data.
2377 U_INTERNAL
const char* U_EXPORT2
2378 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
2380 if (status
==NULL
|| U_FAILURE(*status
)) {
2383 if (!resourceBundle
) {
2384 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2387 return resourceBundle
->fData
->fName
;
2391 U_CAPI
const char* U_EXPORT2
2392 ures_getLocale(const UResourceBundle
* resourceBundle
,
2395 return ures_getLocaleInternal(resourceBundle
, status
);
2399 U_CAPI
const char* U_EXPORT2
2400 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
2401 ULocDataLocaleType type
,
2402 UErrorCode
* status
) {
2403 if (status
==NULL
|| U_FAILURE(*status
)) {
2406 if (!resourceBundle
) {
2407 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2411 case ULOC_ACTUAL_LOCALE
:
2412 return resourceBundle
->fData
->fName
;
2413 case ULOC_VALID_LOCALE
:
2414 return resourceBundle
->fTopLevelData
->fName
;
2415 case ULOC_REQUESTED_LOCALE
:
2417 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2423 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
2428 return resB
->fData
->fName
;
2432 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2437 return resB
->fData
->fPath
;
2441 static UResourceBundle
*
2442 ures_openWithType(UResourceBundle
*r
, const char* path
, const char* localeID
,
2443 UResOpenType openType
, UErrorCode
* status
) {
2444 if(U_FAILURE(*status
)) {
2448 UResourceDataEntry
*entry
;
2449 if(openType
!= URES_OPEN_DIRECT
) {
2450 /* first "canonicalize" the locale ID */
2451 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2452 uloc_getBaseName(localeID
, canonLocaleID
, UPRV_LENGTHOF(canonLocaleID
), status
);
2453 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2454 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2457 entry
= entryOpen(path
, canonLocaleID
, openType
, status
);
2459 entry
= entryOpenDirect(path
, localeID
, status
);
2461 if(U_FAILURE(*status
)) {
2465 *status
= U_MISSING_RESOURCE_ERROR
;
2469 UBool isStackObject
;
2471 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2474 *status
= U_MEMORY_ALLOCATION_ERROR
;
2477 isStackObject
= FALSE
;
2479 isStackObject
= ures_isStackObject(r
);
2480 ures_closeBundle(r
, FALSE
);
2482 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2483 ures_setIsStackObject(r
, isStackObject
);
2485 r
->fTopLevelData
= r
->fData
= entry
;
2486 uprv_memcpy(&r
->fResData
, &entry
->fData
, sizeof(ResourceData
));
2487 r
->fHasFallback
= openType
!= URES_OPEN_DIRECT
&& !r
->fResData
.noFallback
;
2488 r
->fIsTopLevel
= TRUE
;
2489 r
->fRes
= r
->fResData
.rootRes
;
2490 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2493 ResourceTracer(r
).traceOpen();
2498 U_CAPI UResourceBundle
* U_EXPORT2
2499 ures_open(const char* path
, const char* localeID
, UErrorCode
* status
) {
2500 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2503 U_CAPI UResourceBundle
* U_EXPORT2
2504 ures_openNoDefault(const char* path
, const char* localeID
, UErrorCode
* status
) {
2505 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_ROOT
, status
);
2509 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2510 * or sought. However, alias substitution will happen!
2512 U_CAPI UResourceBundle
* U_EXPORT2
2513 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2514 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2518 * Internal API: This function is used to open a resource bundle
2519 * proper fallback chaining is executed while initialization.
2520 * The result is stored in cache for later fallback search.
2522 * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle.
2524 U_INTERNAL
void U_EXPORT2
2525 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2526 const char* localeID
, UErrorCode
* status
) {
2527 if(U_SUCCESS(*status
) && r
== NULL
) {
2528 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2531 ures_openWithType(r
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2535 * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle.
2537 U_INTERNAL
void U_EXPORT2
2538 ures_openDirectFillIn(UResourceBundle
*r
, const char* path
, const char* localeID
, UErrorCode
* status
) {
2539 if(U_SUCCESS(*status
) && r
== NULL
) {
2540 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2543 ures_openWithType(r
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2547 * Same as ures_open(), except that if no resource bundle for the specified package name and locale exists,
2548 * and the incoming locale specifies a country, this will fall back to the resource bundle for the specified country
2549 * and the specified country's default language. For example, if caller asks for fr_JP and no bundle for fr_JP exists,
2550 * ures_open() will fall back to fr and this function will fall back to ja_JP.
2552 U_INTERNAL UResourceBundle
* U_EXPORT2
2553 ures_openWithCountryFallback(const char* packageName
,
2555 UBool
* didFallBackByCountry
,
2556 UErrorCode
* status
) {
2557 // First, call ures_open().
2558 UResourceBundle
* result
= ures_open(packageName
, locale
, status
);
2559 if (didFallBackByCountry
!= NULL
) {
2560 *didFallBackByCountry
= FALSE
;
2563 // If the original locale specified a country and the resource bundle we got from ures_open() above
2564 // is a fallback locale that throws away the specified country, create a NEW fallback bundle using
2565 // the originally-specified country code paired with that country's default language code (as given
2566 // by Locale::addLikelySubtags() ). That is, if the user asks for number patterns for ja_US, use
2567 // the patterns for en_US instead of the patterns for ja.
2568 char country
[ULOC_COUNTRY_CAPACITY
];
2569 uloc_getCountry(locale
, country
, ULOC_COUNTRY_CAPACITY
, status
);
2571 // Do the special logic if we got a fallback resource bundle and the original locale specified a country.
2572 if (*status
== U_USING_FALLBACK_WARNING
&& uprv_strlen(country
) > 0) {
2573 // If the fallback bundle's locale *doesn't* specify a country, or specifies a different country
2574 // than we originally asked for, do our special fallback logic.
2575 char receivedCountry
[ULOC_COUNTRY_CAPACITY
];
2576 uloc_getCountry(ures_getLocaleByType(result
, ULOC_ACTUAL_LOCALE
, status
), receivedCountry
, ULOC_COUNTRY_CAPACITY
, status
);
2577 if (uprv_strcmp(country
, receivedCountry
) != 0) {
2578 char language
[ULOC_LANG_CAPACITY
];
2579 char script
[ULOC_SCRIPT_CAPACITY
];
2580 const char* countryAndParameters
= locale
; // this changes below
2581 char countryLocale
[ULOC_FULLNAME_CAPACITY
];
2582 UBool originalLocaleHasScript
= FALSE
;
2584 // Isolate out the fields in the original locale.
2585 uloc_getLanguage(locale
, language
, ULOC_LANG_CAPACITY
, status
);
2586 uloc_getScript(locale
, script
, ULOC_SCRIPT_CAPACITY
, status
);
2587 originalLocaleHasScript
= uprv_strlen(script
) > 0;
2588 countryAndParameters
= locale
+ uprv_strlen(language
) + 1; // +1 for the _ after the language
2589 if (originalLocaleHasScript
) {
2590 countryAndParameters
+= uprv_strlen(script
) + 1; // +1 for the _ after the script
2593 // Get the default language for the specified country by fabricating a locale ID with
2594 // that country code and "und" for the language code and calling uloc_addLikelySubtags().
2595 sprintf(countryLocale
, "und_%s", country
);
2596 uloc_addLikelySubtags(countryLocale
, countryLocale
, ULOC_FULLNAME_CAPACITY
, status
);
2597 uloc_getLanguage(countryLocale
, language
, ULOC_LANG_CAPACITY
, status
);
2598 if (!originalLocaleHasScript
) {
2599 uloc_getScript(countryLocale
, script
, ULOC_SCRIPT_CAPACITY
, status
);
2602 if (U_SUCCESS(*status
)) {
2603 UResourceBundle
* newResource
= NULL
;
2605 // Create a new locale ID using the language from uloc_addLikelySubtags() and the script (if present),
2606 // country, and parameters from the original locale and try opening a resource for it.
2607 *status
= U_ZERO_ERROR
;
2608 sprintf(countryLocale
, "%s_%s_%s", language
, script
, countryAndParameters
);
2609 newResource
= ures_open(packageName
, countryLocale
, status
);
2611 // If we got back a fallback locale of the default locale, we have more work to do...
2612 if (*status
== U_USING_FALLBACK_WARNING
|| *status
== U_USING_DEFAULT_WARNING
) {
2613 char receivedLanguage
[ULOC_LANG_CAPACITY
];
2614 uloc_getLanguage(ures_getLocaleByType(newResource
, ULOC_ACTUAL_LOCALE
, status
), receivedLanguage
, ULOC_LANG_CAPACITY
, status
);
2616 // If we got back a resource for the default locale, or we got back a resource for a locale with
2617 // a different language than the one we asked for, that means uloc_addLikelySubtags() gave us back
2618 // a locale with a language we don't actually have resource bundles for, or it gave us back "und"
2619 // instead of a real language code. For the non-"und" cases, we're getting back the most important
2620 // spoken language for that country, but we only have resource data for that country's "official"
2621 // language. Most of the time, that language is English (we also use English for "und"); the table
2622 // below covers the exceptions. Look up the appropriate language in the table and try again to
2623 // load a resource bundle for that language.
2624 if (*status
== U_USING_DEFAULT_WARNING
|| uprv_strcmp(language
, receivedLanguage
) != 0) {
2625 static const char* substituteLanguageTable
[] = {
2626 "pap_Latn_BQ", "nl",
2627 "pap_Latn_CW", "nl",
2632 uprv_strcpy(language
, "en");
2633 for (int32_t i
= 0; i
< sizeof(substituteLanguageTable
) / sizeof(char*); i
+= 2) {
2634 if (uprv_strncmp(countryLocale
, substituteLanguageTable
[i
], uprv_strlen(substituteLanguageTable
[i
])) == 0) {
2635 uprv_strcpy(language
, substituteLanguageTable
[i
+ 1]);
2639 sprintf(countryLocale
, "%s_%s_%s", language
, script
, countryAndParameters
);
2640 ures_close(newResource
);
2641 newResource
= ures_open(packageName
, countryLocale
, status
);
2645 // If that succeeds, that's what we return as our result.
2646 if (U_SUCCESS(*status
)) {
2647 // If the user passed us a pointer in didFallBackByCountry, set it based on whether our special
2648 // logic actually retrieved a different resource bundle than the ures_open() call at the top
2650 if (didFallBackByCountry
!= NULL
) {
2651 UErrorCode tmpStatus
= U_ZERO_ERROR
;
2652 const char* languageLocale
= ures_getLocaleByType(result
, ULOC_ACTUAL_LOCALE
, &tmpStatus
);
2653 const char* countryLocale
= ures_getLocaleByType(newResource
, ULOC_ACTUAL_LOCALE
, &tmpStatus
);
2654 *didFallBackByCountry
= U_SUCCESS(tmpStatus
) && languageLocale
!= NULL
&& countryLocale
!= NULL
&& uprv_strcmp(languageLocale
, countryLocale
) != 0;
2657 result
= newResource
;
2667 * API: Counts members. For arrays and tables, returns number of resources.
2668 * For strings, returns 1.
2670 U_CAPI
int32_t U_EXPORT2
2671 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2672 const char* resourceKey
,
2675 UResourceBundle resData
;
2676 ures_initStackObject(&resData
);
2677 if (status
==NULL
|| U_FAILURE(*status
)) {
2680 if(resourceBundle
== NULL
) {
2681 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2684 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2686 if(resData
.fResData
.data
!= NULL
) {
2687 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2688 ures_close(&resData
);
2691 *status
= U_MISSING_RESOURCE_ERROR
;
2692 ures_close(&resData
);
2698 * Internal function.
2699 * Return the version number associated with this ResourceBundle as a string.
2701 * @param resourceBundle The resource bundle for which the version is checked.
2702 * @return A version number string as specified in the resource bundle or its parent.
2703 * The caller does not own this string.
2704 * @see ures_getVersion
2707 U_INTERNAL
const char* U_EXPORT2
2708 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2710 if (!resourceBundle
) return NULL
;
2712 if(resourceBundle
->fVersion
== NULL
) {
2714 /* If the version ID has not been built yet, then do so. Retrieve */
2715 /* the minor version from the file. */
2716 UErrorCode status
= U_ZERO_ERROR
;
2717 int32_t minor_len
= 0;
2720 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2722 /* Determine the length of of the final version string. This is */
2723 /* the length of the major part + the length of the separator */
2724 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2727 len
= (minor_len
> 0) ? minor_len
: 1;
2729 /* Allocate the string, and build it up. */
2730 /* + 1 for zero byte */
2733 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2734 /* Check for null pointer. */
2735 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2740 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2741 resourceBundle
->fVersion
[len
] = '\0';
2744 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2748 return resourceBundle
->fVersion
;
2751 U_CAPI
const char* U_EXPORT2
2752 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2754 return ures_getVersionNumberInternal(resourceBundle
);
2757 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2760 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2763 /** Tree support functions *******************************/
2764 #define INDEX_LOCALE_NAME "res_index"
2765 #define INDEX_TAG "InstalledLocales"
2766 #define DEFAULT_TAG "default"
2768 #if defined(URES_TREE_DEBUG)
2772 typedef struct ULocalesContext
{
2773 UResourceBundle installed
;
2774 UResourceBundle curr
;
2777 static void U_CALLCONV
2778 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2779 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2780 ures_close(&ctx
->curr
);
2781 ures_close(&ctx
->installed
);
2783 uprv_free(enumerator
);
2786 static int32_t U_CALLCONV
2787 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2788 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2789 return ures_getSize(&ctx
->installed
);
2795 static const char * U_CALLCONV
2796 ures_loc_nextLocale(UEnumeration
* en
,
2797 int32_t* resultLength
,
2798 UErrorCode
* status
) {
2799 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2800 UResourceBundle
*res
= &(ctx
->installed
);
2801 UResourceBundle
*k
= NULL
;
2802 const char *result
= NULL
;
2804 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
)) != 0) {
2805 result
= ures_getKey(k
);
2806 len
= (int32_t)uprv_strlen(result
);
2809 *resultLength
= len
;
2814 static void U_CALLCONV
2815 ures_loc_resetLocales(UEnumeration
* en
,
2816 UErrorCode
* /*status*/) {
2817 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2818 ures_resetIterator(res
);
2823 static const UEnumeration gLocalesEnum
= {
2826 ures_loc_closeLocales
,
2827 ures_loc_countLocales
,
2829 ures_loc_nextLocale
,
2830 ures_loc_resetLocales
2834 U_CAPI UEnumeration
* U_EXPORT2
2835 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2837 UResourceBundle
*idx
= NULL
;
2838 UEnumeration
*en
= NULL
;
2839 ULocalesContext
*myContext
= NULL
;
2841 if(U_FAILURE(*status
)) {
2844 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2845 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2846 if(!en
|| !myContext
) {
2847 *status
= U_MEMORY_ALLOCATION_ERROR
;
2849 uprv_free(myContext
);
2852 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2854 ures_initStackObject(&myContext
->installed
);
2855 ures_initStackObject(&myContext
->curr
);
2856 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2857 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2858 if(U_SUCCESS(*status
)) {
2859 #if defined(URES_TREE_DEBUG)
2860 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2861 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2863 en
->context
= myContext
;
2865 #if defined(URES_TREE_DEBUG)
2866 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2868 ures_close(&myContext
->installed
);
2869 uprv_free(myContext
);
2879 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2881 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2882 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2889 U_CAPI
int32_t U_EXPORT2
2890 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2891 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2892 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2894 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2895 char defVal
[1024] = ""; /* default value for given locale */
2896 char defLoc
[1024] = ""; /* default value for given locale */
2897 char base
[1024] = ""; /* base locale */
2900 char full
[1024] = "";
2901 UResourceBundle bund1
, bund2
;
2902 UResourceBundle
*res
= NULL
;
2903 UErrorCode subStatus
= U_ZERO_ERROR
;
2905 if(U_FAILURE(*status
)) return 0;
2906 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2907 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2910 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2911 #if defined(URES_TREE_DEBUG)
2912 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2913 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2915 ures_initStackObject(&bund1
);
2916 ures_initStackObject(&bund2
);
2919 uprv_strcpy(parent
, base
);
2920 uprv_strcpy(found
, base
);
2923 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2924 *isAvailable
= TRUE
;
2925 if (U_SUCCESS(subStatus
)) {
2926 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2928 uenum_close(locEnum
);
2931 if(U_FAILURE(subStatus
)) {
2932 *status
= subStatus
;
2937 subStatus
= U_ZERO_ERROR
;
2938 res
= ures_open(path
, parent
, &subStatus
);
2939 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2940 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2942 *isAvailable
= FALSE
;
2944 isAvailable
= NULL
; /* only want to set this the first time around */
2946 #if defined(URES_TREE_DEBUG)
2947 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2949 if(U_FAILURE(subStatus
)) {
2950 *status
= subStatus
;
2951 } else if(subStatus
== U_ZERO_ERROR
) {
2952 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2953 if(subStatus
== U_ZERO_ERROR
) {
2954 const UChar
*defUstr
;
2956 /* look for default item */
2957 #if defined(URES_TREE_DEBUG)
2958 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2959 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2961 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2962 if(U_SUCCESS(subStatus
) && defLen
) {
2963 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2964 #if defined(URES_TREE_DEBUG)
2965 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2966 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2968 uprv_strcpy(defLoc
, parent
);
2970 uprv_strcpy(kwVal
, defVal
);
2971 #if defined(URES_TREE_DEBUG)
2972 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2973 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2980 subStatus
= U_ZERO_ERROR
;
2983 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2986 if (uprv_strcmp(found
, parent
) != 0) {
2987 uprv_strcpy(parent
, found
);
2989 uloc_getParent(found
, parent
, sizeof(parent
), &subStatus
);
2993 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2995 /* Now, see if we can find the kwVal collator.. start the search over.. */
2996 uprv_strcpy(parent
, base
);
2997 uprv_strcpy(found
, base
);
3000 subStatus
= U_ZERO_ERROR
;
3001 res
= ures_open(path
, parent
, &subStatus
);
3002 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
3003 *isAvailable
= FALSE
;
3005 isAvailable
= NULL
; /* only want to set this the first time around */
3007 #if defined(URES_TREE_DEBUG)
3008 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
3009 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
3011 if(U_FAILURE(subStatus
)) {
3012 *status
= subStatus
;
3013 } else if(subStatus
== U_ZERO_ERROR
) {
3014 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
3015 #if defined(URES_TREE_DEBUG)
3016 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
3018 if(subStatus
== U_ZERO_ERROR
) {
3019 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
3020 #if defined(URES_TREE_DEBUG)
3021 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
3023 if(subStatus
== U_ZERO_ERROR
) {
3024 #if defined(URES_TREE_DEBUG)
3025 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
3026 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
3028 uprv_strcpy(full
, parent
);
3030 uprv_strcpy(full
, "root");
3032 /* now, recalculate default kw if need be */
3033 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
3034 const UChar
*defUstr
;
3036 /* look for default item */
3037 #if defined(URES_TREE_DEBUG)
3038 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
3039 path
?path
:"ICUDATA", full
);
3041 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
3042 if(U_SUCCESS(subStatus
) && defLen
) {
3043 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
3044 #if defined(URES_TREE_DEBUG)
3045 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
3046 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
3048 uprv_strcpy(defLoc
, full
);
3050 } /* end of recalculate default KW */
3051 #if defined(URES_TREE_DEBUG)
3053 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
3057 #if defined(URES_TREE_DEBUG)
3058 fprintf(stderr
, "err=%s in %s looking for %s\n",
3059 u_errorName(subStatus
), parent
, kwVal
);
3065 subStatus
= U_ZERO_ERROR
;
3067 UBool haveFound
= FALSE
;
3068 // At least for collations which may be aliased, we need to use the VALID locale
3069 // as the parent instead of just truncating, as long as the VALID locale is not
3070 // root and has a different language than the parent. Use of the VALID locale
3071 // here is similar to the procedure used at the end of the previous do-while loop
3072 // for all resource types. This is for <rdar://problem/31138554>.
3073 // It may be appropriate for all resources here too, filing an ICU ticket.
3074 if (res
!= NULL
&& uprv_strcmp(resName
, "collations") == 0) {
3075 const char *validLoc
= ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
);
3076 if (U_SUCCESS(subStatus
) && validLoc
!= NULL
&& validLoc
[0] != 0 && uprv_strcmp(validLoc
, "root") != 0) {
3077 char validLang
[ULOC_LANG_CAPACITY
];
3078 char parentLang
[ULOC_LANG_CAPACITY
];
3079 uloc_getLanguage(validLoc
, validLang
, ULOC_LANG_CAPACITY
, &subStatus
);
3080 uloc_getLanguage(parent
, parentLang
, ULOC_LANG_CAPACITY
, &subStatus
);
3081 if (U_SUCCESS(subStatus
) && uprv_strcmp(validLang
, parentLang
) != 0) {
3082 // validLoc is not root and has a different language than parent, use it instead
3083 uprv_strcpy(found
, validLoc
);
3087 subStatus
= U_ZERO_ERROR
;
3090 uprv_strcpy(found
, parent
);
3093 uloc_getParent(found
,parent
,1023,&subStatus
);
3095 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
3097 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
3098 #if defined(URES_TREE_DEBUG)
3099 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
3101 uprv_strcpy(kwVal
, defVal
);
3102 uprv_strcpy(parent
, base
);
3103 uprv_strcpy(found
, base
);
3105 do { /* search for 'default' named item */
3106 subStatus
= U_ZERO_ERROR
;
3107 res
= ures_open(path
, parent
, &subStatus
);
3108 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
3109 *isAvailable
= FALSE
;
3111 isAvailable
= NULL
; /* only want to set this the first time around */
3113 #if defined(URES_TREE_DEBUG)
3114 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
3115 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
3117 if(U_FAILURE(subStatus
)) {
3118 *status
= subStatus
;
3119 } else if(subStatus
== U_ZERO_ERROR
) {
3120 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
3121 if(subStatus
== U_ZERO_ERROR
) {
3122 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
3123 if(subStatus
== U_ZERO_ERROR
) {
3124 #if defined(URES_TREE_DEBUG)
3125 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
3126 parent
, keyword
, kwVal
, u_errorName(subStatus
));
3128 uprv_strcpy(full
, parent
);
3130 uprv_strcpy(full
, "root");
3133 /* now, recalculate default kw if need be */
3134 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
3135 const UChar
*defUstr
;
3137 /* look for default item */
3138 #if defined(URES_TREE_DEBUG)
3139 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
3140 path
?path
:"ICUDATA", full
);
3142 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
3143 if(U_SUCCESS(subStatus
) && defLen
) {
3144 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
3145 #if defined(URES_TREE_DEBUG)
3146 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
3147 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
3149 uprv_strcpy(defLoc
, full
);
3151 } /* end of recalculate default KW */
3152 #if defined(URES_TREE_DEBUG)
3154 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
3160 subStatus
= U_ZERO_ERROR
;
3162 uprv_strcpy(found
, parent
);
3163 uloc_getParent(found
,parent
,1023,&subStatus
);
3165 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
3168 if(U_SUCCESS(*status
)) {
3170 #if defined(URES_TREE_DEBUG)
3171 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
3173 *status
= U_MISSING_RESOURCE_ERROR
;
3174 } else if(omitDefault
) {
3175 #if defined(URES_TREE_DEBUG)
3176 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
3178 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
3179 /* found the keyword in a *child* of where the default tag was present. */
3180 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
3181 /* and the default is in or in an ancestor of the current locale */
3182 #if defined(URES_TREE_DEBUG)
3183 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
3189 uprv_strcpy(found
, full
);
3191 uprv_strcat(found
, "@");
3192 uprv_strcat(found
, keyword
);
3193 uprv_strcat(found
, "=");
3194 uprv_strcat(found
, kwVal
);
3195 } else if(!omitDefault
) {
3196 uprv_strcat(found
, "@");
3197 uprv_strcat(found
, keyword
);
3198 uprv_strcat(found
, "=");
3199 uprv_strcat(found
, defVal
);
3202 /* we found the default locale - no need to repeat it.*/
3207 length
= (int32_t)uprv_strlen(found
);
3209 if(U_SUCCESS(*status
)) {
3210 int32_t copyLength
= uprv_min(length
, resultCapacity
);
3212 uprv_strncpy(result
, found
, copyLength
);
3215 *status
= U_MISSING_RESOURCE_ERROR
;
3221 return u_terminateChars(result
, resultCapacity
, length
, status
);
3224 U_CAPI UEnumeration
* U_EXPORT2
3225 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
3227 #define VALUES_BUF_SIZE 2048
3228 #define VALUES_LIST_SIZE 512
3230 char valuesBuf
[VALUES_BUF_SIZE
];
3231 int32_t valuesIndex
= 0;
3232 const char *valuesList
[VALUES_LIST_SIZE
];
3233 int32_t valuesCount
= 0;
3238 UEnumeration
*locs
= NULL
;
3240 UResourceBundle item
;
3241 UResourceBundle subItem
;
3243 ures_initStackObject(&item
);
3244 ures_initStackObject(&subItem
);
3245 locs
= ures_openAvailableLocales(path
, status
);
3247 if(U_FAILURE(*status
)) {
3249 ures_close(&subItem
);
3256 while((locale
= uenum_next(locs
, &locLen
, status
)) != 0) {
3257 UResourceBundle
*bund
= NULL
;
3258 UResourceBundle
*subPtr
= NULL
;
3259 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
3260 bund
= ures_openDirect(path
, locale
, &subStatus
);
3262 #if defined(URES_TREE_DEBUG)
3263 if(!bund
|| U_FAILURE(subStatus
)) {
3264 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
3265 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
3269 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
3271 if(!bund
|| U_FAILURE(subStatus
)) {
3272 #if defined(URES_TREE_DEBUG)
3273 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
3274 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
3281 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
)) != 0
3282 && U_SUCCESS(subStatus
)) {
3285 k
= ures_getKey(subPtr
);
3287 #if defined(URES_TREE_DEBUG)
3288 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
3290 if(k
== NULL
|| *k
== 0 ||
3291 uprv_strcmp(k
, DEFAULT_TAG
) == 0 || uprv_strncmp(k
, "private-", 8) == 0) {
3292 // empty or "default" or unlisted type
3295 for(i
=0; i
<valuesCount
; i
++) {
3296 if(!uprv_strcmp(valuesList
[i
],k
)) {
3297 k
= NULL
; /* found duplicate */
3302 int32_t kLen
= (int32_t)uprv_strlen(k
);
3303 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
3304 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
3305 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
3307 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
3308 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
3309 valuesIndex
+= kLen
;
3310 #if defined(URES_TREE_DEBUG)
3311 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
3312 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
3314 valuesBuf
[valuesIndex
++] = 0; /* terminate */
3320 valuesBuf
[valuesIndex
++] = 0; /* terminate */
3323 ures_close(&subItem
);
3325 #if defined(URES_TREE_DEBUG)
3326 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
3327 valuesIndex
, valuesCount
);
3329 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
3332 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
3333 U_INTERNAL UBool U_EXPORT2
3334 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
3335 if(res1
==NULL
|| res2
==NULL
){
3336 return res1
==res2
; /* pointer comparision */
3338 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
3339 return (res1
->fKey
==res2
->fKey
);
3341 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
3345 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
3348 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
3349 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
3351 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
3355 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
3358 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
3361 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
3364 if(res1
->fRes
!= res2
->fRes
){
3369 U_INTERNAL UResourceBundle
* U_EXPORT2
3370 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
3371 UResourceBundle
* bundle
= NULL
;
3372 UResourceBundle
* ret
= NULL
;
3373 if(U_FAILURE(*status
) || res
== NULL
){
3376 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
3377 if(res
->fResPath
!=NULL
){
3378 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
3385 U_INTERNAL
const UResourceBundle
* U_EXPORT2
3386 ures_getParentBundle(const UResourceBundle
* res
){
3390 return res
->fParentRes
;
3394 U_INTERNAL
void U_EXPORT2
3395 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
3398 str
= ures_getStringByKey(res
, key
, &len
, status
);
3399 if(U_SUCCESS(*status
)) {
3400 u_versionFromUString(ver
, str
);