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"
35 #include "unicode/uenum.h"
45 Static cache for already opened resource bundles - mostly for keeping fallback info
46 TODO: This cache should probably be removed when the deprecated code is
49 static UHashtable
*cache
= NULL
;
50 static icu::UInitOnce gCacheInitOnce
= U_INITONCE_INITIALIZER
;
52 static UMutex
*resbMutex() {
53 static UMutex
*m
= STATIC_NEW(UMutex
);
57 /* INTERNAL: hashes an entry */
58 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
59 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
60 UHashTok namekey
, pathkey
;
61 namekey
.pointer
= b
->fName
;
62 pathkey
.pointer
= b
->fPath
;
63 return uhash_hashChars(namekey
)+37u*uhash_hashChars(pathkey
);
66 /* INTERNAL: compares two entries */
67 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
68 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
69 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
70 UHashTok name1
, name2
, path1
, path2
;
71 name1
.pointer
= b1
->fName
;
72 name2
.pointer
= b2
->fName
;
73 path1
.pointer
= b1
->fPath
;
74 path2
.pointer
= b2
->fPath
;
75 return (UBool
)(uhash_compareChars(name1
, name2
) &&
76 uhash_compareChars(path1
, path2
));
81 * Internal function, gets parts of locale name according
82 * to the position of '_' character
84 static UBool
chopLocale(char *name
) {
85 char *i
= uprv_strrchr(name
, '_');
98 static void entryIncrease(UResourceDataEntry
*entry
) {
99 umtx_lock(resbMutex());
100 entry
->fCountExisting
++;
101 while(entry
->fParent
!= NULL
) {
102 entry
= entry
->fParent
;
103 entry
->fCountExisting
++;
105 umtx_unlock(resbMutex());
109 * Internal function. Tries to find a resource in given Resource
110 * Bundle, as well as in its parents
112 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
113 UResourceDataEntry
*resB
= resBundle
->fData
;
118 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
119 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
122 if(resBundle
->fHasFallback
== TRUE
) {
123 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
124 resB
= resB
->fParent
;
125 if(resB
->fBogus
== U_ZERO_ERROR
) {
127 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
132 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
134 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
135 *status
= U_USING_DEFAULT_WARNING
;
137 *status
= U_USING_FALLBACK_WARNING
;
141 return (&(resB
->fData
));
142 } else { /* If resource is not found, we need to give an error */
143 *status
= U_MISSING_RESOURCE_ERROR
;
147 *status
= U_MISSING_RESOURCE_ERROR
;
153 free_entry(UResourceDataEntry
*entry
) {
154 UResourceDataEntry
*alias
;
155 res_unload(&(entry
->fData
));
156 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
157 uprv_free(entry
->fName
);
159 if(entry
->fPath
!= NULL
) {
160 uprv_free(entry
->fPath
);
162 if(entry
->fPool
!= NULL
) {
163 --entry
->fPool
->fCountExisting
;
165 alias
= entry
->fAlias
;
167 while(alias
->fAlias
!= NULL
) {
168 alias
= alias
->fAlias
;
170 --alias
->fCountExisting
;
175 /* Works just like ucnv_flushCache() */
176 static int32_t ures_flushCache()
178 UResourceDataEntry
*resB
;
180 int32_t rbDeletedNum
= 0;
181 const UHashElement
*e
;
184 /*if shared data hasn't even been lazy evaluated yet
187 umtx_lock(resbMutex());
189 umtx_unlock(resbMutex());
195 /*creates an enumeration to iterate through every element in the table */
197 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
199 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
200 /* Deletes only if reference counter == 0
201 * Don't worry about the children of this node.
202 * Those will eventually get deleted too, if not already.
203 * Don't worry about the parents of this node.
204 * Those will eventually get deleted too, if not already.
206 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
207 /* some resource bundles are still open somewhere. */
209 if (resB
->fCountExisting
== 0) {
212 uhash_removeElement(cache
, e
);
217 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
218 * got decremented by free_entry().
220 } while(deletedMore
);
221 umtx_unlock(resbMutex());
229 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
230 UBool cacheNotEmpty
= FALSE
;
231 int32_t pos
= UHASH_FIRST
;
232 const UHashElement
*e
;
233 UResourceDataEntry
*resB
;
235 umtx_lock(resbMutex());
237 umtx_unlock(resbMutex());
238 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
242 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
244 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
245 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
247 (void*)resB
, resB
->fCountExisting
,
248 resB
->fName
?resB
->fName
:"NULL",
249 resB
->fPath
?resB
->fPath
:"NULL",
252 (void*)resB
->fParent
);
255 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
257 umtx_unlock(resbMutex());
259 return cacheNotEmpty
;
264 static UBool U_CALLCONV
ures_cleanup(void)
271 gCacheInitOnce
.reset();
275 /** INTERNAL: Initializes the cache for resources */
276 static void U_CALLCONV
createCache(UErrorCode
&status
) {
277 U_ASSERT(cache
== NULL
);
278 cache
= uhash_open(hashEntry
, compareEntries
, NULL
, &status
);
279 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
282 static void initCache(UErrorCode
*status
) {
283 umtx_initOnce(gCacheInitOnce
, &createCache
, *status
);
286 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
288 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
289 int32_t len
= (int32_t)uprv_strlen(name
);
290 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
291 uprv_free(res
->fName
);
293 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
294 res
->fName
= res
->fNameBuffer
;
297 res
->fName
= (char *)uprv_malloc(len
+1);
299 if(res
->fName
== NULL
) {
300 *status
= U_MEMORY_ALLOCATION_ERROR
;
302 uprv_strcpy(res
->fName
, name
);
306 static UResourceDataEntry
*
307 getPoolEntry(const char *path
, UErrorCode
*status
);
310 * INTERNAL: Inits and opens an entry from a data DLL.
311 * CAUTION: resbMutex must be locked when calling this function.
313 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
314 UResourceDataEntry
*r
= NULL
;
315 UResourceDataEntry find
;
316 /*int32_t hashValue;*/
318 char aliasName
[100] = { 0 };
319 int32_t aliasLen
= 0;
320 /*UBool isAlias = FALSE;*/
321 /*UHashTok hashkey; */
323 if(U_FAILURE(*status
)) {
327 /* here we try to deduce the right locale name */
328 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
329 name
= uloc_getDefault();
330 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
331 name
= kRootLocaleName
;
332 } else { /* otherwise, we'll open what we're given */
336 find
.fName
= (char *)name
;
337 find
.fPath
= (char *)path
;
339 /* calculate the hash value of the entry */
340 /*hashkey.pointer = (void *)&find;*/
341 /*hashValue = hashEntry(hashkey);*/
343 /* check to see if we already have this entry */
344 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
346 /* if the entry is not yet in the hash table, we'll try to construct a new one */
347 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
349 *status
= U_MEMORY_ALLOCATION_ERROR
;
353 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
354 /*r->fHashKey = hashValue;*/
356 setEntryName(r
, name
, status
);
357 if (U_FAILURE(*status
)) {
363 r
->fPath
= (char *)uprv_strdup(path
);
364 if(r
->fPath
== NULL
) {
365 *status
= U_MEMORY_ALLOCATION_ERROR
;
371 /* this is the actual loading */
372 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
374 if (U_FAILURE(*status
)) {
375 /* if we failed to load due to an out-of-memory error, exit early. */
376 if (*status
== U_MEMORY_ALLOCATION_ERROR
) {
380 /* we have no such entry in dll, so it will always use fallback */
381 *status
= U_USING_FALLBACK_WARNING
;
382 r
->fBogus
= U_USING_FALLBACK_WARNING
;
383 } else { /* if we have a regular entry */
385 if (r
->fData
.usesPoolBundle
) {
386 r
->fPool
= getPoolEntry(r
->fPath
, status
);
387 if (U_SUCCESS(*status
)) {
388 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
389 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
390 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
391 r
->fData
.poolBundleStrings
= r
->fPool
->fData
.p16BitUnits
;
393 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
399 if (U_SUCCESS(*status
)) {
400 /* handle the alias by trying to get out the %%Alias tag.*/
401 /* We'll try to get alias string from the bundle */
402 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
403 if (aliasres
!= RES_BOGUS
) {
404 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
405 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
406 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
407 r
->fAlias
= init_entry(aliasName
, path
, status
);
414 UResourceDataEntry
*oldR
= NULL
;
415 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
416 /* just insert it in the cache */
417 UErrorCode cacheStatus
= U_ZERO_ERROR
;
418 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
419 if (U_FAILURE(cacheStatus
)) {
420 *status
= cacheStatus
;
425 /* somebody have already inserted it while we were working, discard newly opened data */
426 /* Also, we could get here IF we opened an alias */
434 /* return the real bundle */
435 while(r
->fAlias
!= NULL
) {
438 r
->fCountExisting
++; /* we increase its reference count */
439 /* if the resource has a warning */
440 /* we don't want to overwrite a status with no error */
441 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
442 *status
= r
->fBogus
; /* set the returning status */
448 static UResourceDataEntry
*
449 getPoolEntry(const char *path
, UErrorCode
*status
) {
450 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
451 if( U_SUCCESS(*status
) &&
452 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
454 *status
= U_INVALID_FORMAT_ERROR
;
460 /* CAUTION: resbMutex must be locked when calling this function! */
461 static UResourceDataEntry
*
462 findFirstExisting(const char* path
, char* name
,
463 UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
464 UResourceDataEntry
*r
= NULL
;
465 UBool hasRealData
= FALSE
;
466 const char *defaultLoc
= uloc_getDefault();
467 *hasChopped
= TRUE
; /* we're starting with a fresh name */
469 while(*hasChopped
&& !hasRealData
) {
470 r
= init_entry(name
, path
, status
);
471 /* Null pointer test */
472 if (U_FAILURE(*status
)) {
475 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
476 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
478 /* this entry is not real. We will discard it. */
479 /* However, the parent line for this entry is */
480 /* not to be used - as there might be parent */
481 /* lines in cache from previous openings that */
482 /* are not updated yet. */
484 /*entryCloseInt(r);*/
486 *status
= U_USING_FALLBACK_WARNING
;
488 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
491 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
493 /*Fallback data stuff*/
494 *hasChopped
= chopLocale(name
);
495 if (*hasChopped
&& *name
== '\0') {
496 uprv_strcpy(name
, "und");
502 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
507 resB
->fMagic1
= MAGIC1
;
508 resB
->fMagic2
= MAGIC2
;
512 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
513 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
517 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
518 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
519 ures_setIsStackObject(resB
, TRUE
);
524 StackUResourceBundle::StackUResourceBundle() {
525 ures_initStackObject(&bundle
);
528 StackUResourceBundle::~StackUResourceBundle() {
534 static UBool
// returns U_SUCCESS(*status)
535 loadParentsExceptRoot(UResourceDataEntry
*&t1
,
536 char name
[], int32_t nameCapacity
,
537 UBool usingUSRData
, char usrDataPath
[], UErrorCode
*status
) {
538 if (U_FAILURE(*status
)) { return FALSE
; }
539 UBool hasChopped
= TRUE
;
540 while (hasChopped
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
&&
541 res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
542 Resource parentRes
= res_getResource(&t1
->fData
, "%%Parent");
543 if (parentRes
!= RES_BOGUS
) { // An explicit parent was found.
544 int32_t parentLocaleLen
= 0;
545 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), parentRes
, &parentLocaleLen
);
546 if(parentLocaleName
!= NULL
&& 0 < parentLocaleLen
&& parentLocaleLen
< nameCapacity
) {
547 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+ 1);
548 if (uprv_strcmp(name
, kRootLocaleName
) == 0) {
553 // Insert regular parents.
554 UErrorCode parentStatus
= U_ZERO_ERROR
;
555 UResourceDataEntry
*t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
556 if (U_FAILURE(parentStatus
)) {
557 *status
= parentStatus
;
560 UResourceDataEntry
*u2
= NULL
;
561 UErrorCode usrStatus
= U_ZERO_ERROR
;
562 if (usingUSRData
) { // This code inserts user override data into the inheritance chain.
563 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
564 // If we failed due to out-of-memory, report that to the caller and exit early.
565 if (usrStatus
== U_MEMORY_ALLOCATION_ERROR
) {
571 if (usingUSRData
&& U_SUCCESS(usrStatus
) && u2
->fBogus
== U_ZERO_ERROR
) {
577 // The USR override data wasn't found, set it to be deleted.
578 u2
->fCountExisting
= 0;
582 hasChopped
= chopLocale(name
);
587 static UBool
// returns U_SUCCESS(*status)
588 insertRootBundle(UResourceDataEntry
*&t1
, UErrorCode
*status
) {
589 if (U_FAILURE(*status
)) { return FALSE
; }
590 UErrorCode parentStatus
= U_ZERO_ERROR
;
591 UResourceDataEntry
*t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
592 if (U_FAILURE(parentStatus
)) {
593 *status
= parentStatus
;
603 * Open a resource bundle for the locale;
604 * if there is not even a base language bundle, then fall back to the default locale;
605 * if there is no bundle for that either, then load the root bundle.
607 * This is the default bundle loading behavior.
609 URES_OPEN_LOCALE_DEFAULT_ROOT
,
610 // TODO: ICU ticket #11271 "consistent default locale across locale trees"
611 // Add an option to look at the main locale tree for whether to
612 // fall back to root directly (if the locale has main data) or
613 // fall back to the default locale first (if the locale does not even have main data).
615 * Open a resource bundle for the locale;
616 * if there is not even a base language bundle, then load the root bundle;
617 * never fall back to the default locale.
619 * This is used for algorithms that have good pan-Unicode default behavior,
620 * such as case mappings, collation, and segmentation (BreakIterator).
622 URES_OPEN_LOCALE_ROOT
,
624 * Open a resource bundle for the exact bundle name as requested;
625 * no fallbacks, do not load parent bundles.
627 * This is used for supplemental (non-locale) data.
631 typedef enum UResOpenType UResOpenType
;
633 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
,
634 UResOpenType openType
, UErrorCode
* status
) {
635 U_ASSERT(openType
!= URES_OPEN_DIRECT
);
636 UErrorCode intStatus
= U_ZERO_ERROR
;
637 UResourceDataEntry
*r
= NULL
;
638 UResourceDataEntry
*t1
= NULL
;
639 UBool isDefault
= FALSE
;
640 UBool isRoot
= FALSE
;
641 UBool hasRealData
= FALSE
;
642 UBool hasChopped
= TRUE
;
643 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
645 char name
[ULOC_FULLNAME_CAPACITY
];
646 char usrDataPath
[96];
650 if(U_FAILURE(*status
)) {
654 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
655 name
[sizeof(name
) - 1] = 0;
657 if ( usingUSRData
) {
658 if ( path
== NULL
) {
659 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
661 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
662 usrDataPath
[0] = 'u';
663 usrDataPath
[1] = 's';
664 usrDataPath
[2] = 'r';
665 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
669 umtx_lock(resbMutex());
671 /* We're going to skip all the locales that do not have any data */
672 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
674 // If we failed due to out-of-memory, report the failure and exit early.
675 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
680 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
683 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
684 UErrorCode usrStatus
= U_ZERO_ERROR
;
685 UResourceDataEntry
*u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
686 // If we failed due to out-of-memory, report the failure and exit early.
687 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
692 if(u1
->fBogus
== U_ZERO_ERROR
) {
696 /* the USR override data wasn't found, set it to be deleted */
697 u1
->fCountExisting
= 0;
701 if (hasChopped
&& !isRoot
) {
702 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
708 /* we could have reached this point without having any real data */
709 /* if that is the case, we need to chain in the default locale */
710 if(r
==NULL
&& openType
== URES_OPEN_LOCALE_DEFAULT_ROOT
&& !isDefault
&& !isRoot
) {
711 /* insert default locale */
712 uprv_strcpy(name
, uloc_getDefault());
713 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
714 // If we failed due to out-of-memory, report the failure and exit early.
715 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
719 intStatus
= U_USING_DEFAULT_WARNING
;
720 if(r
!= NULL
) { /* the default locale exists */
724 // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
725 if (hasChopped
&& !isRoot
) {
726 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
733 /* we could still have r == NULL at this point - maybe even default locale is not */
736 uprv_strcpy(name
, kRootLocaleName
);
737 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
738 // If we failed due to out-of-memory, report the failure and exit early.
739 if (intStatus
== U_MEMORY_ALLOCATION_ERROR
) {
745 intStatus
= U_USING_DEFAULT_WARNING
;
747 } else { /* we don't even have the root locale */
748 *status
= U_MISSING_RESOURCE_ERROR
;
751 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 &&
752 t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
753 if (!insertRootBundle(t1
, status
)) {
757 r
->fBogus
= U_USING_DEFAULT_WARNING
;
761 // TODO: Does this ever loop? A: Yes, it can often loop up to 4 times or so.
762 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
763 t1
->fParent
->fCountExisting
++;
768 umtx_unlock(resbMutex());
770 if(U_SUCCESS(*status
)) {
771 if(intStatus
!= U_ZERO_ERROR
) {
781 * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
783 * Parent and root locale bundles are loaded if
784 * the requested bundle does not have the "nofallback" flag.
786 static UResourceDataEntry
*
787 entryOpenDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
789 if(U_FAILURE(*status
)) {
793 umtx_lock(resbMutex());
794 // findFirstExisting() without fallbacks.
795 UResourceDataEntry
*r
= init_entry(localeID
, path
, status
);
796 if(U_SUCCESS(*status
)) {
797 if(r
->fBogus
!= U_ZERO_ERROR
) {
805 // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
806 // unless it is marked with "nofallback".
807 UResourceDataEntry
*t1
= r
;
808 if(r
!= NULL
&& uprv_strcmp(localeID
, kRootLocaleName
) != 0 && // not root
809 r
->fParent
== NULL
&& !r
->fData
.noFallback
&&
810 uprv_strlen(localeID
) < ULOC_FULLNAME_CAPACITY
) {
811 char name
[ULOC_FULLNAME_CAPACITY
];
812 uprv_strcpy(name
, localeID
);
813 if(!chopLocale(name
) || uprv_strcmp(name
, kRootLocaleName
) == 0 ||
814 loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), FALSE
, NULL
, status
)) {
815 if(uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
) {
816 insertRootBundle(t1
, status
);
819 if(U_FAILURE(*status
)) {
825 // TODO: Does this ever loop?
826 while(t1
->fParent
!= NULL
) {
827 t1
->fParent
->fCountExisting
++;
831 umtx_unlock(resbMutex());
836 * Functions to create and destroy resource bundles.
837 * CAUTION: resbMutex must be locked when calling this function.
840 static void entryCloseInt(UResourceDataEntry
*resB
) {
841 UResourceDataEntry
*p
= resB
;
843 while(resB
!= NULL
) {
845 resB
->fCountExisting
--;
847 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
850 if(resB->fCountExisting <= 0) {
851 uhash_remove(cache, resB);
852 if(resB->fBogus == U_ZERO_ERROR) {
853 res_unload(&(resB->fData));
855 if(resB->fName != NULL) {
856 uprv_free(resB->fName);
858 if(resB->fPath != NULL) {
859 uprv_free(resB->fPath);
870 * API: closes a resource bundle and cleans up.
873 static void entryClose(UResourceDataEntry
*resB
) {
874 umtx_lock(resbMutex());
876 umtx_unlock(resbMutex());
880 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
881 if(resB->fResPath == NULL) {
882 resB->fResPath = resB->fResBuf;
883 *(resB->fResPath) = 0;
885 resB->fResPathLen = uprv_strlen(toAdd);
886 if(RES_BUFSIZE <= resB->fResPathLen+1) {
887 if(resB->fResPath == resB->fResBuf) {
888 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
890 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
893 uprv_strcpy(resB->fResPath, toAdd);
896 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
897 int32_t resPathLenOrig
= resB
->fResPathLen
;
898 if(resB
->fResPath
== NULL
) {
899 resB
->fResPath
= resB
->fResBuf
;
900 *(resB
->fResPath
) = 0;
901 resB
->fResPathLen
= 0;
903 resB
->fResPathLen
+= lenToAdd
;
904 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
905 if(resB
->fResPath
== resB
->fResBuf
) {
906 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
907 /* Check that memory was allocated correctly. */
908 if (resB
->fResPath
== NULL
) {
909 *status
= U_MEMORY_ALLOCATION_ERROR
;
912 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
914 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
915 /* Check that memory was reallocated correctly. */
917 *status
= U_MEMORY_ALLOCATION_ERROR
;
920 resB
->fResPath
= temp
;
923 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
926 static void ures_freeResPath(UResourceBundle
*resB
) {
927 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
928 uprv_free(resB
->fResPath
);
930 resB
->fResPath
= NULL
;
931 resB
->fResPathLen
= 0;
935 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
938 if(resB
->fData
!= NULL
) {
939 entryClose(resB
->fData
);
941 if(resB
->fVersion
!= NULL
) {
942 uprv_free(resB
->fVersion
);
944 ures_freeResPath(resB
);
946 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
951 /* poison the data */
952 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
958 U_CAPI
void U_EXPORT2
959 ures_close(UResourceBundle
* resB
)
961 ures_closeBundle(resB
, TRUE
);
964 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
965 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
966 const UResourceBundle
*parent
, int32_t noAlias
,
967 UResourceBundle
*resB
, UErrorCode
*status
)
969 if(status
== NULL
|| U_FAILURE(*status
)) {
972 if (parent
== NULL
) {
973 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
976 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
977 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
979 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
981 /* we have an alias, now let's cut it up */
982 char stackAlias
[200];
983 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
987 * Allocate enough space for both the char * version
988 * of the alias and parent->fResPath.
990 * We do this so that res_findResource() can modify the path,
991 * which allows us to remove redundant _res_findResource() variants
993 * res_findResource() now NUL-terminates each segment so that table keys
994 * can always be compared with strcmp() instead of strncmp().
995 * Saves code there and simplifies testing and code coverage.
999 ++len
; /* count the terminating NUL */
1000 if(parent
->fResPath
!= NULL
) {
1001 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
1005 if(capacity
< len
) {
1008 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
1009 capacity
= (int32_t)sizeof(stackAlias
);
1010 chAlias
= stackAlias
;
1012 chAlias
= (char *)uprv_malloc(capacity
);
1014 if(chAlias
== NULL
) {
1015 *status
= U_MEMORY_ALLOCATION_ERROR
;
1019 u_UCharsToChars(alias
, chAlias
, len
);
1021 if(*chAlias
== RES_PATH_SEPARATOR
) {
1022 /* there is a path included */
1023 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
1024 if(locale
== NULL
) {
1025 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
1031 if(uprv_strcmp(path
, "LOCALE") == 0) {
1032 /* this is an XPath alias, starting with "/LOCALE/" */
1033 /* it contains the path to a resource which should be looked up */
1034 /* starting in the requested locale */
1036 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
1037 path
= realData
->fPath
; /* we will be looking in the same package */
1039 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
1042 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1049 /* no path, start with a locale */
1051 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1056 path
= realData
->fPath
;
1061 /* got almost everything, let's try to open */
1062 /* first, open the bundle with real data */
1063 UResourceBundle
*result
= resB
;
1064 const char* temp
= NULL
;
1065 UErrorCode intStatus
= U_ZERO_ERROR
;
1066 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
1067 if(U_SUCCESS(intStatus
)) {
1068 if(keyPath
== NULL
) {
1069 /* no key path. This means that we are going to
1070 * to use the corresponding resource from
1073 /* first, we are going to get a corresponding parent
1074 * resource to the one we are searching.
1076 char *aKey
= parent
->fResPath
;
1078 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
1080 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
1085 /* we need to make keyPath from parent's fResPath and
1086 * current key, if there is a key associated
1088 len
= (int32_t)(uprv_strlen(key
) + 1);
1089 if(len
> capacity
) {
1091 if(chAlias
== stackAlias
) {
1092 chAlias
= (char *)uprv_malloc(capacity
);
1094 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
1096 if(chAlias
== NULL
) {
1097 ures_close(mainRes
);
1098 *status
= U_MEMORY_ALLOCATION_ERROR
;
1102 uprv_memcpy(chAlias
, key
, len
);
1104 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
1105 } else if(idx
!= -1) {
1106 /* if there is no key, but there is an index, try to get by the index */
1107 /* here we have either a table or an array, so get the element */
1108 int32_t type
= RES_GET_TYPE(r
);
1109 if(URES_IS_TABLE(type
)) {
1110 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
1111 } else { /* array */
1112 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
1115 if(r
!= RES_BOGUS
) {
1116 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
1118 *status
= U_MISSING_RESOURCE_ERROR
;
1122 /* this one is a bit trickier.
1123 * we start finding keys, but after we resolve one alias, the path might continue.
1125 * aliastest:alias { "testtypes/anotheralias/Sequence" }
1126 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1127 * aliastest resource should finally have the sequence, not collation elements.
1129 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1130 char stackPath
[URES_MAX_BUFFER_SIZE
];
1131 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1132 if(uprv_strlen(keyPath
) >= UPRV_LENGTHOF(stackPath
)) {
1133 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1134 if(pathBuf
== NULL
) {
1135 *status
= U_MEMORY_ALLOCATION_ERROR
;
1136 ures_close(mainRes
);
1140 uprv_strcpy(pathBuf
, keyPath
);
1142 /* now we have fallback following here */
1144 r
= dataEntry
->fData
.rootRes
;
1145 /* this loop handles 'found' resources over several levels */
1146 while(*myPath
&& U_SUCCESS(*status
)) {
1147 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1148 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1149 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1152 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1153 dataEntry
= result
->fData
;
1155 } else { /* no resource found, we don't really want to look anymore on this level */
1159 dataEntry
= dataEntry
->fParent
;
1160 uprv_strcpy(pathBuf
, keyPath
);
1162 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1163 if(r
== RES_BOGUS
) {
1164 *status
= U_MISSING_RESOURCE_ERROR
;
1167 if(pathBuf
!= stackPath
) {
1171 } else { /* we failed to open the resource we're aliasing to */
1172 *status
= intStatus
;
1174 if(chAlias
!= stackAlias
) {
1177 if(mainRes
!= result
) {
1178 ures_close(mainRes
);
1183 /* bad alias, should be an error */
1184 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1188 *status
= U_TOO_MANY_ALIASES_ERROR
;
1193 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1196 *status
= U_MEMORY_ALLOCATION_ERROR
;
1199 ures_setIsStackObject(resB
, FALSE
);
1200 resB
->fResPath
= NULL
;
1201 resB
->fResPathLen
= 0;
1203 if(resB
->fData
!= NULL
) {
1204 entryClose(resB
->fData
);
1206 if(resB
->fVersion
!= NULL
) {
1207 uprv_free(resB
->fVersion
);
1210 weiv: if stack object was passed in, it doesn't really need to be reinited,
1211 since the purpose of initing is to remove stack junk. However, at this point
1212 we would not do anything to an allocated object, so stack object should be
1216 if(ures_isStackObject(resB) != FALSE) {
1217 ures_initStackObject(resB);
1220 if(parent
!= resB
) {
1221 ures_freeResPath(resB
);
1224 resB
->fData
= realData
;
1225 entryIncrease(resB
->fData
);
1226 resB
->fHasFallback
= FALSE
;
1227 resB
->fIsTopLevel
= FALSE
;
1230 /*resB->fParentRes = parent;*/
1231 resB
->fTopLevelData
= parent
->fTopLevelData
;
1232 if(parent
->fResPath
&& parent
!= resB
) {
1233 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1236 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1237 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1238 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1240 } else if(idx
>= 0) {
1242 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1243 ures_appendResPath(resB
, buf
, len
, status
);
1244 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1245 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1248 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1250 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1251 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1254 resB
->fVersion
= NULL
;
1256 /*resB->fParent = parent->fRes;*/
1257 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1258 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1262 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1263 UBool isStackObject
;
1264 if(U_FAILURE(*status
) || r
== original
) {
1267 if(original
!= NULL
) {
1269 isStackObject
= FALSE
;
1270 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1273 *status
= U_MEMORY_ALLOCATION_ERROR
;
1277 isStackObject
= ures_isStackObject(r
);
1278 ures_closeBundle(r
, FALSE
);
1280 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1283 if(original
->fResPath
) {
1284 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1286 ures_setIsStackObject(r
, isStackObject
);
1287 if(r
->fData
!= NULL
) {
1288 entryIncrease(r
->fData
);
1295 * Functions to retrieve data from resource bundles.
1298 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1300 if (status
==NULL
|| U_FAILURE(*status
)) {
1304 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1307 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1309 *status
= U_RESOURCE_TYPE_MISMATCH
;
1315 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1316 char *dest
, int32_t *pLength
,
1318 UErrorCode
*status
) {
1321 if (U_FAILURE(*status
)) {
1324 if (pLength
!= NULL
) {
1325 capacity
= *pLength
;
1329 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1330 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1334 if (length16
== 0) {
1335 /* empty string, return as read-only pointer */
1336 if (pLength
!= NULL
) {
1340 u_terminateChars(dest
, capacity
, 0, status
);
1346 /* We need to transform the string to the destination buffer. */
1347 if (capacity
< length16
) {
1348 /* No chance for the string to fit. Pure preflighting. */
1349 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1351 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1353 * We know the string will fit into dest because each UChar turns
1354 * into at most three UTF-8 bytes. Fill the latter part of dest
1355 * so that callers do not expect to use dest as a string pointer,
1356 * hopefully leading to more robust code for when resource bundles
1357 * may store UTF-8 natively.
1358 * (In which case dest would not be used at all.)
1360 * We do not do this if forceCopy=TRUE because then the caller
1361 * expects the string to start exactly at dest.
1363 * The test above for <= 0x2aaaaaaa prevents overflows.
1364 * The +1 is for the NUL terminator.
1366 int32_t maxLength
= 3 * length16
+ 1;
1367 if (capacity
> maxLength
) {
1368 dest
+= capacity
- maxLength
;
1369 capacity
= maxLength
;
1372 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1376 U_CAPI
const char * U_EXPORT2
1377 ures_getUTF8String(const UResourceBundle
*resB
,
1378 char *dest
, int32_t *pLength
,
1380 UErrorCode
*status
) {
1382 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1383 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1386 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1387 UErrorCode
* status
) {
1389 if (status
==NULL
|| U_FAILURE(*status
)) {
1393 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1396 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1398 *status
= U_RESOURCE_TYPE_MISMATCH
;
1403 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1404 UErrorCode
* status
) {
1406 if (status
==NULL
|| U_FAILURE(*status
)) {
1410 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1413 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1415 *status
= U_RESOURCE_TYPE_MISMATCH
;
1420 /* this function returns a signed integer */
1421 /* it performs sign extension */
1422 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1423 if (status
==NULL
|| U_FAILURE(*status
)) {
1427 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1430 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1431 *status
= U_RESOURCE_TYPE_MISMATCH
;
1434 return RES_GET_INT(resB
->fRes
);
1437 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1438 if (status
==NULL
|| U_FAILURE(*status
)) {
1442 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1445 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1446 *status
= U_RESOURCE_TYPE_MISMATCH
;
1449 return RES_GET_UINT(resB
->fRes
);
1452 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1456 return res_getPublicType(resB
->fRes
);
1459 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1467 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1475 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1476 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1477 const UChar
* result
= 0;
1478 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1479 result
= ures_getString(tempRes
, len
, status
);
1480 ures_close(tempRes
);
1483 return res_getString(&(resB
->fResData
), r
, len
);
1487 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1494 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1498 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1501 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1502 Resource r
= RES_BOGUS
;
1504 if (status
==NULL
|| U_FAILURE(*status
)) {
1508 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1512 if(resB
->fIndex
== resB
->fSize
-1) {
1513 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1516 switch(RES_GET_TYPE(resB
->fRes
)) {
1518 case URES_STRING_V2
:
1519 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1523 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1524 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1525 /* TODO: do the fallback */
1527 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1530 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1531 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1532 /* TODO: do the fallback */
1534 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1536 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1539 case URES_INT_VECTOR
:
1540 *status
= U_RESOURCE_TYPE_MISMATCH
;
1550 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1551 const char *key
= NULL
;
1552 Resource r
= RES_BOGUS
;
1554 if (status
==NULL
|| U_FAILURE(*status
)) {
1559 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1564 if(resB
->fIndex
== resB
->fSize
-1) {
1565 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1569 switch(RES_GET_TYPE(resB
->fRes
)) {
1573 case URES_STRING_V2
:
1574 case URES_INT_VECTOR
:
1575 return ures_copyResb(fillIn
, resB
, status
);
1579 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1580 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1581 /* TODO: do the fallback */
1583 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1586 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1587 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1588 /* TODO: do the fallback */
1590 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1600 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1601 const char* key
= NULL
;
1602 Resource r
= RES_BOGUS
;
1604 if (status
==NULL
|| U_FAILURE(*status
)) {
1609 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1614 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1615 switch(RES_GET_TYPE(resB
->fRes
)) {
1619 case URES_STRING_V2
:
1620 case URES_INT_VECTOR
:
1621 return ures_copyResb(fillIn
, resB
, status
);
1625 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1626 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1627 /* TODO: do the fallback */
1629 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1632 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1633 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1634 /* TODO: do the fallback */
1636 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1642 *status
= U_MISSING_RESOURCE_ERROR
;
1648 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1649 const char* key
= NULL
;
1650 Resource r
= RES_BOGUS
;
1652 if (status
==NULL
|| U_FAILURE(*status
)) {
1656 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1660 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1661 switch(RES_GET_TYPE(resB
->fRes
)) {
1663 case URES_STRING_V2
:
1664 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1668 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1669 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1670 /* TODO: do the fallback */
1672 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1675 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1676 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1677 /* TODO: do the fallback */
1679 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1681 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1684 case URES_INT_VECTOR
:
1685 *status
= U_RESOURCE_TYPE_MISMATCH
;
1688 /* must not occur */
1689 *status
= U_INTERNAL_PROGRAM_ERROR
;
1693 *status
= U_MISSING_RESOURCE_ERROR
;
1698 U_CAPI
const char * U_EXPORT2
1699 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1701 char *dest
, int32_t *pLength
,
1703 UErrorCode
*status
) {
1705 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1706 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1709 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1710 return resB->fResPath;
1713 U_CAPI UResourceBundle
* U_EXPORT2
1714 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1716 UResourceBundle
*first
= NULL
;
1717 UResourceBundle
*result
= fillIn
;
1718 char *packageName
= NULL
;
1719 char *pathToResource
= NULL
, *save
= NULL
;
1720 char *locale
= NULL
, *localeEnd
= NULL
;
1723 if(status
== NULL
|| U_FAILURE(*status
)) {
1727 length
= (int32_t)(uprv_strlen(path
)+1);
1728 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1730 if(pathToResource
== NULL
) {
1731 *status
= U_MEMORY_ALLOCATION_ERROR
;
1734 uprv_memcpy(pathToResource
, path
, length
);
1736 locale
= pathToResource
;
1737 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1739 packageName
= pathToResource
;
1740 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1741 if(pathToResource
== NULL
) {
1742 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1744 *pathToResource
= 0;
1745 locale
= pathToResource
+1;
1749 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1750 if(localeEnd
!= NULL
) {
1754 first
= ures_open(packageName
, locale
, status
);
1756 if(U_SUCCESS(*status
)) {
1758 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1760 result
= ures_copyResb(fillIn
, first
, status
);
1768 U_CAPI UResourceBundle
* U_EXPORT2
1769 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1771 Resource res
= RES_BOGUS
;
1772 UResourceBundle
*result
= fillIn
;
1775 if(status
== NULL
|| U_FAILURE(*status
)) {
1779 /* here we do looping and circular alias checking */
1780 /* this loop is here because aliasing is resolved on this level, not on res level */
1781 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1783 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1784 if(res
!= RES_BOGUS
) {
1785 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1788 *status
= U_MISSING_RESOURCE_ERROR
;
1791 } while(*path
); /* there is more stuff in the path */
1795 U_INTERNAL
const UChar
* U_EXPORT2
1796 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1799 UErrorCode
*status
) {
1801 UResourceBundle stack
;
1802 const UChar
* retVal
= NULL
;
1803 ures_initStackObject(&stack
);
1804 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1806 retVal
= ures_getString(&stack
, &length
, status
);
1808 if (U_FAILURE(*status
)) {
1811 if (length
== 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1814 *status
= U_MISSING_RESOURCE_ERROR
;
1823 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1825 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1826 Resource resource
= table
; /* The current resource */
1827 icu::CharString path
;
1828 UErrorCode errorCode
= U_ZERO_ERROR
;
1829 path
.append(key
, errorCode
);
1830 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1831 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1832 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1833 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1834 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1835 if (nextPathPart
!= NULL
) {
1836 *nextPathPart
= 0; /* Terminating null for this part of path. */
1839 nextPathPart
= uprv_strchr(pathPart
, 0);
1842 const char *pathP
= pathPart
;
1843 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
1844 type
= (UResType
)RES_GET_TYPE(resource
);
1845 pathPart
= nextPathPart
;
1853 U_CAPI UResourceBundle
* U_EXPORT2
1854 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1856 UResourceBundle
*fillIn
,
1857 UErrorCode
*status
) {
1858 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1859 /*UResourceDataEntry *realData = NULL;*/
1860 UResourceBundle
*helper
= NULL
;
1862 if (status
==NULL
|| U_FAILURE(*status
)) {
1866 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1870 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1871 if(URES_IS_TABLE(type
)) {
1872 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
1873 const char* key
= inKey
;
1874 if(res
== RES_BOGUS
) {
1875 UResourceDataEntry
*dataEntry
= resB
->fData
;
1877 char *myPath
= NULL
;
1878 const char* resPath
= resB
->fResPath
;
1879 int32_t len
= resB
->fResPathLen
;
1880 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1881 dataEntry
= dataEntry
->fParent
;
1882 rootRes
= dataEntry
->fData
.rootRes
;
1884 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1887 path
.append(resPath
, len
, *status
);
1889 path
.append(inKey
, *status
);
1890 if (U_FAILURE(*status
)) {
1894 myPath
= path
.data();
1897 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1898 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1899 /* We hit an alias, but we didn't finish following the path. */
1900 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1901 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1903 dataEntry
= helper
->fData
;
1904 rootRes
= helper
->fRes
;
1905 resPath
= helper
->fResPath
;
1906 len
= helper
->fResPathLen
;
1912 } while(*myPath
); /* Continue until the whole path is consumed */
1915 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1916 if(res
!= RES_BOGUS
) {
1917 /* check if resB->fResPath gives the right name here */
1918 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1919 *status
= U_USING_DEFAULT_WARNING
;
1921 *status
= U_USING_FALLBACK_WARNING
;
1924 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1926 *status
= U_MISSING_RESOURCE_ERROR
;
1929 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1933 *status
= U_RESOURCE_TYPE_MISMATCH
;
1941 void getAllItemsWithFallback(
1942 const UResourceBundle
*bundle
, ResourceDataValue
&value
,
1944 UErrorCode
&errorCode
) {
1945 if (U_FAILURE(errorCode
)) { return; }
1946 // We recursively enumerate child-first,
1947 // only storing parent items in the absence of child items.
1948 // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
1949 // to prevent a parent item from being stored.
1951 // It would be possible to recursively enumerate parent-first,
1952 // overriding parent items with child items.
1953 // When the sink sees the no-fallback/no-inheritance marker,
1954 // then it would remove the parent's item.
1955 // We would deserialize parent values even though they are overridden in a child bundle.
1956 value
.pResData
= &bundle
->fResData
;
1957 UResourceDataEntry
*parentEntry
= bundle
->fData
->fParent
;
1958 UBool hasParent
= parentEntry
!= NULL
&& U_SUCCESS(parentEntry
->fBogus
);
1959 value
.setResource(bundle
->fRes
);
1960 sink
.put(bundle
->fKey
, value
, !hasParent
, errorCode
);
1962 // We might try to query the sink whether
1963 // any fallback from the parent bundle is still possible.
1965 // Turn the parent UResourceDataEntry into a UResourceBundle,
1966 // much like in ures_openWithType().
1967 // TODO: See if we can refactor ures_getByKeyWithFallback()
1968 // and pull out an inner function that takes and returns a UResourceDataEntry
1969 // so that we need not create UResourceBundle objects.
1970 UResourceBundle parentBundle
;
1971 ures_initStackObject(&parentBundle
);
1972 parentBundle
.fTopLevelData
= parentBundle
.fData
= parentEntry
;
1973 // TODO: What is the difference between bundle fData and fTopLevelData?
1974 uprv_memcpy(&parentBundle
.fResData
, &parentEntry
->fData
, sizeof(ResourceData
));
1975 // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1976 parentBundle
.fHasFallback
= !parentBundle
.fResData
.noFallback
;
1977 parentBundle
.fIsTopLevel
= TRUE
;
1978 parentBundle
.fRes
= parentBundle
.fResData
.rootRes
;
1979 parentBundle
.fSize
= res_countArrayItems(&(parentBundle
.fResData
), parentBundle
.fRes
);
1980 parentBundle
.fIndex
= -1;
1981 entryIncrease(parentEntry
);
1983 // Look up the container item in the parent bundle.
1984 UResourceBundle containerBundle
;
1985 ures_initStackObject(&containerBundle
);
1986 const UResourceBundle
*rb
;
1987 UErrorCode pathErrorCode
= U_ZERO_ERROR
; // Ignore if parents up to root do not have this path.
1988 if (bundle
->fResPath
== NULL
|| *bundle
->fResPath
== 0) {
1991 rb
= ures_getByKeyWithFallback(&parentBundle
, bundle
->fResPath
,
1992 &containerBundle
, &pathErrorCode
);
1994 if (U_SUCCESS(pathErrorCode
)) {
1995 getAllItemsWithFallback(rb
, value
, sink
, errorCode
);
1997 ures_close(&containerBundle
);
1998 ures_close(&parentBundle
);
2004 U_CAPI
void U_EXPORT2
2005 ures_getAllItemsWithFallback(const UResourceBundle
*bundle
, const char *path
,
2006 icu::ResourceSink
&sink
, UErrorCode
&errorCode
) {
2007 if (U_FAILURE(errorCode
)) { return; }
2009 errorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
2012 UResourceBundle stackBundle
;
2013 ures_initStackObject(&stackBundle
);
2014 const UResourceBundle
*rb
;
2019 rb
= ures_getByKeyWithFallback(bundle
, path
, &stackBundle
, &errorCode
);
2020 if (U_FAILURE(errorCode
)) {
2021 ures_close(&stackBundle
);
2025 // Get all table items with fallback.
2026 ResourceDataValue value
;
2027 getAllItemsWithFallback(rb
, value
, sink
, errorCode
);
2028 ures_close(&stackBundle
);
2031 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
2032 Resource res
= RES_BOGUS
;
2033 UResourceDataEntry
*realData
= NULL
;
2034 const char *key
= inKey
;
2036 if (status
==NULL
|| U_FAILURE(*status
)) {
2040 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2044 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2045 if(URES_IS_TABLE(type
)) {
2047 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2048 if(res
== RES_BOGUS
) {
2050 if(resB
->fHasFallback
== TRUE
) {
2051 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2052 if(U_SUCCESS(*status
)) {
2053 /* check if resB->fResPath gives the right name here */
2054 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
2056 *status
= U_MISSING_RESOURCE_ERROR
;
2059 *status
= U_MISSING_RESOURCE_ERROR
;
2062 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
2066 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2068 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2069 /* here should go a first attempt to locate the key using index table */
2070 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2071 if(U_SUCCESS(*status
)) {
2072 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
2074 *status
= U_MISSING_RESOURCE_ERROR
;
2079 *status
= U_RESOURCE_TYPE_MISMATCH
;
2084 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
2085 Resource res
= RES_BOGUS
;
2086 UResourceDataEntry
*realData
= NULL
;
2087 const char* key
= inKey
;
2089 if (status
==NULL
|| U_FAILURE(*status
)) {
2093 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2097 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2098 if(URES_IS_TABLE(type
)) {
2101 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2103 if(res
== RES_BOGUS
) {
2105 if(resB
->fHasFallback
== TRUE
) {
2106 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2107 if(U_SUCCESS(*status
)) {
2108 switch (RES_GET_TYPE(res
)) {
2110 case URES_STRING_V2
:
2111 return res_getString(rd
, res
, len
);
2114 const UChar
* result
= 0;
2115 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2116 result
= ures_getString(tempRes
, len
, status
);
2117 ures_close(tempRes
);
2121 *status
= U_RESOURCE_TYPE_MISMATCH
;
2124 *status
= U_MISSING_RESOURCE_ERROR
;
2127 *status
= U_MISSING_RESOURCE_ERROR
;
2130 switch (RES_GET_TYPE(res
)) {
2132 case URES_STRING_V2
:
2133 return res_getString(&(resB
->fResData
), res
, len
);
2136 const UChar
* result
= 0;
2137 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2138 result
= ures_getString(tempRes
, len
, status
);
2139 ures_close(tempRes
);
2143 *status
= U_RESOURCE_TYPE_MISMATCH
;
2148 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2150 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2151 /* here should go a first attempt to locate the key using index table */
2152 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2153 if(U_SUCCESS(*status
)) {
2154 return res_getString(rd
, res
, len
);
2156 *status
= U_MISSING_RESOURCE_ERROR
;
2161 *status
= U_RESOURCE_TYPE_MISMATCH
;
2166 U_CAPI
const char * U_EXPORT2
2167 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
2169 char *dest
, int32_t *pLength
,
2171 UErrorCode
*status
) {
2173 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
2174 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
2177 /* TODO: clean from here down */
2180 * INTERNAL: Get the name of the first real locale (not placeholder)
2181 * that has resource bundle data.
2183 U_INTERNAL
const char* U_EXPORT2
2184 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
2186 if (status
==NULL
|| U_FAILURE(*status
)) {
2189 if (!resourceBundle
) {
2190 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2193 return resourceBundle
->fData
->fName
;
2197 U_CAPI
const char* U_EXPORT2
2198 ures_getLocale(const UResourceBundle
* resourceBundle
,
2201 return ures_getLocaleInternal(resourceBundle
, status
);
2205 U_CAPI
const char* U_EXPORT2
2206 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
2207 ULocDataLocaleType type
,
2208 UErrorCode
* status
) {
2209 if (status
==NULL
|| U_FAILURE(*status
)) {
2212 if (!resourceBundle
) {
2213 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2217 case ULOC_ACTUAL_LOCALE
:
2218 return resourceBundle
->fData
->fName
;
2219 case ULOC_VALID_LOCALE
:
2220 return resourceBundle
->fTopLevelData
->fName
;
2221 case ULOC_REQUESTED_LOCALE
:
2223 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2229 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
2234 return resB
->fData
->fName
;
2238 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2243 return resB
->fData
->fPath
;
2247 static UResourceBundle
*
2248 ures_openWithType(UResourceBundle
*r
, const char* path
, const char* localeID
,
2249 UResOpenType openType
, UErrorCode
* status
) {
2250 if(U_FAILURE(*status
)) {
2254 UResourceDataEntry
*entry
;
2255 if(openType
!= URES_OPEN_DIRECT
) {
2256 /* first "canonicalize" the locale ID */
2257 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2258 uloc_getBaseName(localeID
, canonLocaleID
, UPRV_LENGTHOF(canonLocaleID
), status
);
2259 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2260 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2263 entry
= entryOpen(path
, canonLocaleID
, openType
, status
);
2265 entry
= entryOpenDirect(path
, localeID
, status
);
2267 if(U_FAILURE(*status
)) {
2271 *status
= U_MISSING_RESOURCE_ERROR
;
2275 UBool isStackObject
;
2277 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2280 *status
= U_MEMORY_ALLOCATION_ERROR
;
2283 isStackObject
= FALSE
;
2285 isStackObject
= ures_isStackObject(r
);
2286 ures_closeBundle(r
, FALSE
);
2288 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2289 ures_setIsStackObject(r
, isStackObject
);
2291 r
->fTopLevelData
= r
->fData
= entry
;
2292 uprv_memcpy(&r
->fResData
, &entry
->fData
, sizeof(ResourceData
));
2293 r
->fHasFallback
= openType
!= URES_OPEN_DIRECT
&& !r
->fResData
.noFallback
;
2294 r
->fIsTopLevel
= TRUE
;
2295 r
->fRes
= r
->fResData
.rootRes
;
2296 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2302 U_CAPI UResourceBundle
* U_EXPORT2
2303 ures_open(const char* path
, const char* localeID
, UErrorCode
* status
) {
2304 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2307 U_CAPI UResourceBundle
* U_EXPORT2
2308 ures_openNoDefault(const char* path
, const char* localeID
, UErrorCode
* status
) {
2309 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_ROOT
, status
);
2313 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2314 * or sought. However, alias substitution will happen!
2316 U_CAPI UResourceBundle
* U_EXPORT2
2317 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2318 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2322 * Internal API: This function is used to open a resource bundle
2323 * proper fallback chaining is executed while initialization.
2324 * The result is stored in cache for later fallback search.
2326 * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle.
2328 U_INTERNAL
void U_EXPORT2
2329 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2330 const char* localeID
, UErrorCode
* status
) {
2331 if(U_SUCCESS(*status
) && r
== NULL
) {
2332 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2335 ures_openWithType(r
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2339 * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle.
2341 U_INTERNAL
void U_EXPORT2
2342 ures_openDirectFillIn(UResourceBundle
*r
, const char* path
, const char* localeID
, UErrorCode
* status
) {
2343 if(U_SUCCESS(*status
) && r
== NULL
) {
2344 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2347 ures_openWithType(r
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2351 * API: Counts members. For arrays and tables, returns number of resources.
2352 * For strings, returns 1.
2354 U_CAPI
int32_t U_EXPORT2
2355 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2356 const char* resourceKey
,
2359 UResourceBundle resData
;
2360 ures_initStackObject(&resData
);
2361 if (status
==NULL
|| U_FAILURE(*status
)) {
2364 if(resourceBundle
== NULL
) {
2365 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2368 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2370 if(resData
.fResData
.data
!= NULL
) {
2371 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2372 ures_close(&resData
);
2375 *status
= U_MISSING_RESOURCE_ERROR
;
2376 ures_close(&resData
);
2382 * Internal function.
2383 * Return the version number associated with this ResourceBundle as a string.
2385 * @param resourceBundle The resource bundle for which the version is checked.
2386 * @return A version number string as specified in the resource bundle or its parent.
2387 * The caller does not own this string.
2388 * @see ures_getVersion
2391 U_INTERNAL
const char* U_EXPORT2
2392 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2394 if (!resourceBundle
) return NULL
;
2396 if(resourceBundle
->fVersion
== NULL
) {
2398 /* If the version ID has not been built yet, then do so. Retrieve */
2399 /* the minor version from the file. */
2400 UErrorCode status
= U_ZERO_ERROR
;
2401 int32_t minor_len
= 0;
2404 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2406 /* Determine the length of of the final version string. This is */
2407 /* the length of the major part + the length of the separator */
2408 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2411 len
= (minor_len
> 0) ? minor_len
: 1;
2413 /* Allocate the string, and build it up. */
2414 /* + 1 for zero byte */
2417 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2418 /* Check for null pointer. */
2419 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2424 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2425 resourceBundle
->fVersion
[len
] = '\0';
2428 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2432 return resourceBundle
->fVersion
;
2435 U_CAPI
const char* U_EXPORT2
2436 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2438 return ures_getVersionNumberInternal(resourceBundle
);
2441 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2444 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2447 /** Tree support functions *******************************/
2448 #define INDEX_LOCALE_NAME "res_index"
2449 #define INDEX_TAG "InstalledLocales"
2450 #define DEFAULT_TAG "default"
2452 #if defined(URES_TREE_DEBUG)
2456 typedef struct ULocalesContext
{
2457 UResourceBundle installed
;
2458 UResourceBundle curr
;
2461 static void U_CALLCONV
2462 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2463 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2464 ures_close(&ctx
->curr
);
2465 ures_close(&ctx
->installed
);
2467 uprv_free(enumerator
);
2470 static int32_t U_CALLCONV
2471 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2472 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2473 return ures_getSize(&ctx
->installed
);
2479 static const char * U_CALLCONV
2480 ures_loc_nextLocale(UEnumeration
* en
,
2481 int32_t* resultLength
,
2482 UErrorCode
* status
) {
2483 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2484 UResourceBundle
*res
= &(ctx
->installed
);
2485 UResourceBundle
*k
= NULL
;
2486 const char *result
= NULL
;
2488 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
)) != 0) {
2489 result
= ures_getKey(k
);
2490 len
= (int32_t)uprv_strlen(result
);
2493 *resultLength
= len
;
2498 static void U_CALLCONV
2499 ures_loc_resetLocales(UEnumeration
* en
,
2500 UErrorCode
* /*status*/) {
2501 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2502 ures_resetIterator(res
);
2507 static const UEnumeration gLocalesEnum
= {
2510 ures_loc_closeLocales
,
2511 ures_loc_countLocales
,
2513 ures_loc_nextLocale
,
2514 ures_loc_resetLocales
2518 U_CAPI UEnumeration
* U_EXPORT2
2519 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2521 UResourceBundle
*idx
= NULL
;
2522 UEnumeration
*en
= NULL
;
2523 ULocalesContext
*myContext
= NULL
;
2525 if(U_FAILURE(*status
)) {
2528 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2529 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2530 if(!en
|| !myContext
) {
2531 *status
= U_MEMORY_ALLOCATION_ERROR
;
2533 uprv_free(myContext
);
2536 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2538 ures_initStackObject(&myContext
->installed
);
2539 ures_initStackObject(&myContext
->curr
);
2540 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2541 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2542 if(U_SUCCESS(*status
)) {
2543 #if defined(URES_TREE_DEBUG)
2544 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2545 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2547 en
->context
= myContext
;
2549 #if defined(URES_TREE_DEBUG)
2550 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2552 ures_close(&myContext
->installed
);
2553 uprv_free(myContext
);
2563 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2565 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2566 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2573 U_CAPI
int32_t U_EXPORT2
2574 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2575 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2576 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2578 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2579 char defVal
[1024] = ""; /* default value for given locale */
2580 char defLoc
[1024] = ""; /* default value for given locale */
2581 char base
[1024] = ""; /* base locale */
2584 char full
[1024] = "";
2585 UResourceBundle bund1
, bund2
;
2586 UResourceBundle
*res
= NULL
;
2587 UErrorCode subStatus
= U_ZERO_ERROR
;
2589 if(U_FAILURE(*status
)) return 0;
2590 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2591 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2594 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2595 #if defined(URES_TREE_DEBUG)
2596 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2597 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2599 ures_initStackObject(&bund1
);
2600 ures_initStackObject(&bund2
);
2603 uprv_strcpy(parent
, base
);
2604 uprv_strcpy(found
, base
);
2607 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2608 *isAvailable
= TRUE
;
2609 if (U_SUCCESS(subStatus
)) {
2610 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2612 uenum_close(locEnum
);
2615 if(U_FAILURE(subStatus
)) {
2616 *status
= subStatus
;
2621 subStatus
= U_ZERO_ERROR
;
2622 res
= ures_open(path
, parent
, &subStatus
);
2623 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2624 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2626 *isAvailable
= FALSE
;
2628 isAvailable
= NULL
; /* only want to set this the first time around */
2630 #if defined(URES_TREE_DEBUG)
2631 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2633 if(U_FAILURE(subStatus
)) {
2634 *status
= subStatus
;
2635 } else if(subStatus
== U_ZERO_ERROR
) {
2636 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2637 if(subStatus
== U_ZERO_ERROR
) {
2638 const UChar
*defUstr
;
2640 /* look for default item */
2641 #if defined(URES_TREE_DEBUG)
2642 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2643 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2645 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2646 if(U_SUCCESS(subStatus
) && defLen
) {
2647 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2648 #if defined(URES_TREE_DEBUG)
2649 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2650 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2652 uprv_strcpy(defLoc
, parent
);
2654 uprv_strcpy(kwVal
, defVal
);
2655 #if defined(URES_TREE_DEBUG)
2656 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2657 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2664 subStatus
= U_ZERO_ERROR
;
2667 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2670 if (uprv_strcmp(found
, parent
) != 0) {
2671 uprv_strcpy(parent
, found
);
2673 uloc_getParent(found
, parent
, sizeof(parent
), &subStatus
);
2677 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2679 /* Now, see if we can find the kwVal collator.. start the search over.. */
2680 uprv_strcpy(parent
, base
);
2681 uprv_strcpy(found
, base
);
2684 subStatus
= U_ZERO_ERROR
;
2685 res
= ures_open(path
, parent
, &subStatus
);
2686 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2687 *isAvailable
= FALSE
;
2689 isAvailable
= NULL
; /* only want to set this the first time around */
2691 #if defined(URES_TREE_DEBUG)
2692 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2693 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2695 if(U_FAILURE(subStatus
)) {
2696 *status
= subStatus
;
2697 } else if(subStatus
== U_ZERO_ERROR
) {
2698 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2699 #if defined(URES_TREE_DEBUG)
2700 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2702 if(subStatus
== U_ZERO_ERROR
) {
2703 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2704 #if defined(URES_TREE_DEBUG)
2705 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2707 if(subStatus
== U_ZERO_ERROR
) {
2708 #if defined(URES_TREE_DEBUG)
2709 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2710 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2712 uprv_strcpy(full
, parent
);
2714 uprv_strcpy(full
, "root");
2716 /* now, recalculate default kw if need be */
2717 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2718 const UChar
*defUstr
;
2720 /* look for default item */
2721 #if defined(URES_TREE_DEBUG)
2722 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2723 path
?path
:"ICUDATA", full
);
2725 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2726 if(U_SUCCESS(subStatus
) && defLen
) {
2727 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2728 #if defined(URES_TREE_DEBUG)
2729 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2730 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2732 uprv_strcpy(defLoc
, full
);
2734 } /* end of recalculate default KW */
2735 #if defined(URES_TREE_DEBUG)
2737 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2741 #if defined(URES_TREE_DEBUG)
2742 fprintf(stderr
, "err=%s in %s looking for %s\n",
2743 u_errorName(subStatus
), parent
, kwVal
);
2749 subStatus
= U_ZERO_ERROR
;
2751 UBool haveFound
= FALSE
;
2752 // At least for collations which may be aliased, we need to use the VALID locale
2753 // as the parent instead of just truncating, as long as the VALID locale is not
2754 // root and has a different language than the parent. Use of the VALID locale
2755 // here is similar to the procedure used at the end of the previous do-while loop
2756 // for all resource types. This is for <rdar://problem/31138554>.
2757 // It may be appropriate for all resources here too, filing an ICU ticket.
2758 if (res
!= NULL
&& uprv_strcmp(resName
, "collations") == 0) {
2759 const char *validLoc
= ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
);
2760 if (U_SUCCESS(subStatus
) && validLoc
!= NULL
&& validLoc
[0] != 0 && uprv_strcmp(validLoc
, "root") != 0) {
2761 char validLang
[ULOC_LANG_CAPACITY
];
2762 char parentLang
[ULOC_LANG_CAPACITY
];
2763 uloc_getLanguage(validLoc
, validLang
, ULOC_LANG_CAPACITY
, &subStatus
);
2764 uloc_getLanguage(parent
, parentLang
, ULOC_LANG_CAPACITY
, &subStatus
);
2765 if (U_SUCCESS(subStatus
) && uprv_strcmp(validLang
, parentLang
) != 0) {
2766 // validLoc is not root and has a different language than parent, use it instead
2767 uprv_strcpy(found
, validLoc
);
2771 subStatus
= U_ZERO_ERROR
;
2774 uprv_strcpy(found
, parent
);
2777 uloc_getParent(found
,parent
,1023,&subStatus
);
2779 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2781 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2782 #if defined(URES_TREE_DEBUG)
2783 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2785 uprv_strcpy(kwVal
, defVal
);
2786 uprv_strcpy(parent
, base
);
2787 uprv_strcpy(found
, base
);
2789 do { /* search for 'default' named item */
2790 subStatus
= U_ZERO_ERROR
;
2791 res
= ures_open(path
, parent
, &subStatus
);
2792 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2793 *isAvailable
= FALSE
;
2795 isAvailable
= NULL
; /* only want to set this the first time around */
2797 #if defined(URES_TREE_DEBUG)
2798 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2799 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2801 if(U_FAILURE(subStatus
)) {
2802 *status
= subStatus
;
2803 } else if(subStatus
== U_ZERO_ERROR
) {
2804 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2805 if(subStatus
== U_ZERO_ERROR
) {
2806 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2807 if(subStatus
== U_ZERO_ERROR
) {
2808 #if defined(URES_TREE_DEBUG)
2809 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2810 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2812 uprv_strcpy(full
, parent
);
2814 uprv_strcpy(full
, "root");
2817 /* now, recalculate default kw if need be */
2818 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2819 const UChar
*defUstr
;
2821 /* look for default item */
2822 #if defined(URES_TREE_DEBUG)
2823 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2824 path
?path
:"ICUDATA", full
);
2826 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2827 if(U_SUCCESS(subStatus
) && defLen
) {
2828 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2829 #if defined(URES_TREE_DEBUG)
2830 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2831 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2833 uprv_strcpy(defLoc
, full
);
2835 } /* end of recalculate default KW */
2836 #if defined(URES_TREE_DEBUG)
2838 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2844 subStatus
= U_ZERO_ERROR
;
2846 uprv_strcpy(found
, parent
);
2847 uloc_getParent(found
,parent
,1023,&subStatus
);
2849 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2852 if(U_SUCCESS(*status
)) {
2854 #if defined(URES_TREE_DEBUG)
2855 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2857 *status
= U_MISSING_RESOURCE_ERROR
;
2858 } else if(omitDefault
) {
2859 #if defined(URES_TREE_DEBUG)
2860 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2862 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2863 /* found the keyword in a *child* of where the default tag was present. */
2864 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2865 /* and the default is in or in an ancestor of the current locale */
2866 #if defined(URES_TREE_DEBUG)
2867 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2873 uprv_strcpy(found
, full
);
2875 uprv_strcat(found
, "@");
2876 uprv_strcat(found
, keyword
);
2877 uprv_strcat(found
, "=");
2878 uprv_strcat(found
, kwVal
);
2879 } else if(!omitDefault
) {
2880 uprv_strcat(found
, "@");
2881 uprv_strcat(found
, keyword
);
2882 uprv_strcat(found
, "=");
2883 uprv_strcat(found
, defVal
);
2886 /* we found the default locale - no need to repeat it.*/
2891 length
= (int32_t)uprv_strlen(found
);
2893 if(U_SUCCESS(*status
)) {
2894 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2896 uprv_strncpy(result
, found
, copyLength
);
2899 *status
= U_MISSING_RESOURCE_ERROR
;
2905 return u_terminateChars(result
, resultCapacity
, length
, status
);
2908 U_CAPI UEnumeration
* U_EXPORT2
2909 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2911 #define VALUES_BUF_SIZE 2048
2912 #define VALUES_LIST_SIZE 512
2914 char valuesBuf
[VALUES_BUF_SIZE
];
2915 int32_t valuesIndex
= 0;
2916 const char *valuesList
[VALUES_LIST_SIZE
];
2917 int32_t valuesCount
= 0;
2922 UEnumeration
*locs
= NULL
;
2924 UResourceBundle item
;
2925 UResourceBundle subItem
;
2927 ures_initStackObject(&item
);
2928 ures_initStackObject(&subItem
);
2929 locs
= ures_openAvailableLocales(path
, status
);
2931 if(U_FAILURE(*status
)) {
2933 ures_close(&subItem
);
2940 while((locale
= uenum_next(locs
, &locLen
, status
)) != 0) {
2941 UResourceBundle
*bund
= NULL
;
2942 UResourceBundle
*subPtr
= NULL
;
2943 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2944 bund
= ures_openDirect(path
, locale
, &subStatus
);
2946 #if defined(URES_TREE_DEBUG)
2947 if(!bund
|| U_FAILURE(subStatus
)) {
2948 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2949 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2953 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2955 if(!bund
|| U_FAILURE(subStatus
)) {
2956 #if defined(URES_TREE_DEBUG)
2957 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2958 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2965 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
)) != 0
2966 && U_SUCCESS(subStatus
)) {
2969 k
= ures_getKey(subPtr
);
2971 #if defined(URES_TREE_DEBUG)
2972 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2974 if(k
== NULL
|| *k
== 0 ||
2975 uprv_strcmp(k
, DEFAULT_TAG
) == 0 || uprv_strncmp(k
, "private-", 8) == 0) {
2976 // empty or "default" or unlisted type
2979 for(i
=0; i
<valuesCount
; i
++) {
2980 if(!uprv_strcmp(valuesList
[i
],k
)) {
2981 k
= NULL
; /* found duplicate */
2986 int32_t kLen
= (int32_t)uprv_strlen(k
);
2987 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2988 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2989 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2991 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2992 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2993 valuesIndex
+= kLen
;
2994 #if defined(URES_TREE_DEBUG)
2995 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2996 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2998 valuesBuf
[valuesIndex
++] = 0; /* terminate */
3004 valuesBuf
[valuesIndex
++] = 0; /* terminate */
3007 ures_close(&subItem
);
3009 #if defined(URES_TREE_DEBUG)
3010 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
3011 valuesIndex
, valuesCount
);
3013 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
3016 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
3017 U_INTERNAL UBool U_EXPORT2
3018 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
3019 if(res1
==NULL
|| res2
==NULL
){
3020 return res1
==res2
; /* pointer comparision */
3022 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
3023 return (res1
->fKey
==res2
->fKey
);
3025 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
3029 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
3032 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
3033 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
3035 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
3039 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
3042 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
3045 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
3048 if(res1
->fRes
!= res2
->fRes
){
3053 U_INTERNAL UResourceBundle
* U_EXPORT2
3054 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
3055 UResourceBundle
* bundle
= NULL
;
3056 UResourceBundle
* ret
= NULL
;
3057 if(U_FAILURE(*status
) || res
== NULL
){
3060 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
3061 if(res
->fResPath
!=NULL
){
3062 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
3069 U_INTERNAL
const UResourceBundle
* U_EXPORT2
3070 ures_getParentBundle(const UResourceBundle
* res
){
3074 return res
->fParentRes
;
3078 U_INTERNAL
void U_EXPORT2
3079 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
3082 str
= ures_getStringByKey(res
, key
, &len
, status
);
3083 if(U_SUCCESS(*status
)) {
3084 u_versionFromUString(ver
, str
);