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/ustring.h"
25 #include "unicode/ucnv.h"
34 #include "unicode/uenum.h"
44 Static cache for already opened resource bundles - mostly for keeping fallback info
45 TODO: This cache should probably be removed when the deprecated code is
48 static UHashtable
*cache
= NULL
;
49 static icu::UInitOnce gCacheInitOnce
;
51 static UMutex resbMutex
= U_MUTEX_INITIALIZER
;
53 /* INTERNAL: hashes an entry */
54 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
55 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
56 UHashTok namekey
, pathkey
;
57 namekey
.pointer
= b
->fName
;
58 pathkey
.pointer
= b
->fPath
;
59 return uhash_hashChars(namekey
)+37u*uhash_hashChars(pathkey
);
62 /* INTERNAL: compares two entries */
63 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
64 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
65 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
66 UHashTok name1
, name2
, path1
, path2
;
67 name1
.pointer
= b1
->fName
;
68 name2
.pointer
= b2
->fName
;
69 path1
.pointer
= b1
->fPath
;
70 path2
.pointer
= b2
->fPath
;
71 return (UBool
)(uhash_compareChars(name1
, name2
) &&
72 uhash_compareChars(path1
, path2
));
77 * Internal function, gets parts of locale name according
78 * to the position of '_' character
80 static UBool
chopLocale(char *name
) {
81 char *i
= uprv_strrchr(name
, '_');
94 static void entryIncrease(UResourceDataEntry
*entry
) {
95 umtx_lock(&resbMutex
);
96 entry
->fCountExisting
++;
97 while(entry
->fParent
!= NULL
) {
98 entry
= entry
->fParent
;
99 entry
->fCountExisting
++;
101 umtx_unlock(&resbMutex
);
105 * Internal function. Tries to find a resource in given Resource
106 * Bundle, as well as in its parents
108 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
109 UResourceDataEntry
*resB
= resBundle
->fData
;
114 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
115 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
118 if(resBundle
->fHasFallback
== TRUE
) {
119 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
120 resB
= resB
->fParent
;
121 if(resB
->fBogus
== U_ZERO_ERROR
) {
123 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
128 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
130 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
131 *status
= U_USING_DEFAULT_WARNING
;
133 *status
= U_USING_FALLBACK_WARNING
;
137 return (&(resB
->fData
));
138 } else { /* If resource is not found, we need to give an error */
139 *status
= U_MISSING_RESOURCE_ERROR
;
143 *status
= U_MISSING_RESOURCE_ERROR
;
149 free_entry(UResourceDataEntry
*entry
) {
150 UResourceDataEntry
*alias
;
151 res_unload(&(entry
->fData
));
152 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
153 uprv_free(entry
->fName
);
155 if(entry
->fPath
!= NULL
) {
156 uprv_free(entry
->fPath
);
158 if(entry
->fPool
!= NULL
) {
159 --entry
->fPool
->fCountExisting
;
161 alias
= entry
->fAlias
;
163 while(alias
->fAlias
!= NULL
) {
164 alias
= alias
->fAlias
;
166 --alias
->fCountExisting
;
171 /* Works just like ucnv_flushCache() */
172 static int32_t ures_flushCache()
174 UResourceDataEntry
*resB
;
176 int32_t rbDeletedNum
= 0;
177 const UHashElement
*e
;
180 /*if shared data hasn't even been lazy evaluated yet
183 umtx_lock(&resbMutex
);
185 umtx_unlock(&resbMutex
);
191 /*creates an enumeration to iterate through every element in the table */
193 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
195 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
196 /* Deletes only if reference counter == 0
197 * Don't worry about the children of this node.
198 * Those will eventually get deleted too, if not already.
199 * Don't worry about the parents of this node.
200 * Those will eventually get deleted too, if not already.
202 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
203 /* some resource bundles are still open somewhere. */
205 if (resB
->fCountExisting
== 0) {
208 uhash_removeElement(cache
, e
);
213 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
214 * got decremented by free_entry().
216 } while(deletedMore
);
217 umtx_unlock(&resbMutex
);
225 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
226 UBool cacheNotEmpty
= FALSE
;
227 int32_t pos
= UHASH_FIRST
;
228 const UHashElement
*e
;
229 UResourceDataEntry
*resB
;
231 umtx_lock(&resbMutex
);
233 umtx_unlock(&resbMutex
);
234 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
238 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
240 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
241 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
243 (void*)resB
, resB
->fCountExisting
,
244 resB
->fName
?resB
->fName
:"NULL",
245 resB
->fPath
?resB
->fPath
:"NULL",
248 (void*)resB
->fParent
);
251 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
253 umtx_unlock(&resbMutex
);
255 return cacheNotEmpty
;
260 static UBool U_CALLCONV
ures_cleanup(void)
267 gCacheInitOnce
.reset();
271 /** INTERNAL: Initializes the cache for resources */
272 static void U_CALLCONV
createCache(UErrorCode
&status
) {
273 U_ASSERT(cache
== NULL
);
274 cache
= uhash_open(hashEntry
, compareEntries
, NULL
, &status
);
275 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
278 static void initCache(UErrorCode
*status
) {
279 umtx_initOnce(gCacheInitOnce
, &createCache
, *status
);
282 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
284 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
285 int32_t len
= (int32_t)uprv_strlen(name
);
286 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
287 uprv_free(res
->fName
);
289 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
290 res
->fName
= res
->fNameBuffer
;
293 res
->fName
= (char *)uprv_malloc(len
+1);
295 if(res
->fName
== NULL
) {
296 *status
= U_MEMORY_ALLOCATION_ERROR
;
298 uprv_strcpy(res
->fName
, name
);
302 static UResourceDataEntry
*
303 getPoolEntry(const char *path
, UErrorCode
*status
);
306 * INTERNAL: Inits and opens an entry from a data DLL.
307 * CAUTION: resbMutex must be locked when calling this function.
309 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
310 UResourceDataEntry
*r
= NULL
;
311 UResourceDataEntry find
;
312 /*int32_t hashValue;*/
314 char aliasName
[100] = { 0 };
315 int32_t aliasLen
= 0;
316 /*UBool isAlias = FALSE;*/
317 /*UHashTok hashkey; */
319 if(U_FAILURE(*status
)) {
323 /* here we try to deduce the right locale name */
324 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
325 name
= uloc_getDefault();
326 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
327 name
= kRootLocaleName
;
328 } else { /* otherwise, we'll open what we're given */
332 find
.fName
= (char *)name
;
333 find
.fPath
= (char *)path
;
335 /* calculate the hash value of the entry */
336 /*hashkey.pointer = (void *)&find;*/
337 /*hashValue = hashEntry(hashkey);*/
339 /* check to see if we already have this entry */
340 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
342 /* if the entry is not yet in the hash table, we'll try to construct a new one */
343 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
345 *status
= U_MEMORY_ALLOCATION_ERROR
;
349 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
350 /*r->fHashKey = hashValue;*/
352 setEntryName(r
, name
, status
);
353 if (U_FAILURE(*status
)) {
359 r
->fPath
= (char *)uprv_strdup(path
);
360 if(r
->fPath
== NULL
) {
361 *status
= U_MEMORY_ALLOCATION_ERROR
;
367 /* this is the actual loading */
368 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
370 if (U_FAILURE(*status
)) {
371 /* we have no such entry in dll, so it will always use fallback */
372 *status
= U_USING_FALLBACK_WARNING
;
373 r
->fBogus
= U_USING_FALLBACK_WARNING
;
374 } else { /* if we have a regular entry */
376 if (r
->fData
.usesPoolBundle
) {
377 r
->fPool
= getPoolEntry(r
->fPath
, status
);
378 if (U_SUCCESS(*status
)) {
379 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
380 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
381 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
382 r
->fData
.poolBundleStrings
= r
->fPool
->fData
.p16BitUnits
;
384 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
390 if (U_SUCCESS(*status
)) {
391 /* handle the alias by trying to get out the %%Alias tag.*/
392 /* We'll try to get alias string from the bundle */
393 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
394 if (aliasres
!= RES_BOGUS
) {
395 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
396 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
397 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
398 r
->fAlias
= init_entry(aliasName
, path
, status
);
405 UResourceDataEntry
*oldR
= NULL
;
406 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
407 /* just insert it in the cache */
408 UErrorCode cacheStatus
= U_ZERO_ERROR
;
409 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
410 if (U_FAILURE(cacheStatus
)) {
411 *status
= cacheStatus
;
416 /* somebody have already inserted it while we were working, discard newly opened data */
417 /* Also, we could get here IF we opened an alias */
425 /* return the real bundle */
426 while(r
->fAlias
!= NULL
) {
429 r
->fCountExisting
++; /* we increase its reference count */
430 /* if the resource has a warning */
431 /* we don't want to overwrite a status with no error */
432 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
433 *status
= r
->fBogus
; /* set the returning status */
439 static UResourceDataEntry
*
440 getPoolEntry(const char *path
, UErrorCode
*status
) {
441 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
442 if( U_SUCCESS(*status
) &&
443 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
445 *status
= U_INVALID_FORMAT_ERROR
;
451 /* CAUTION: resbMutex must be locked when calling this function! */
452 static UResourceDataEntry
*
453 findFirstExisting(const char* path
, char* name
,
454 UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
455 UResourceDataEntry
*r
= NULL
;
456 UBool hasRealData
= FALSE
;
457 const char *defaultLoc
= uloc_getDefault();
458 *hasChopped
= TRUE
; /* we're starting with a fresh name */
460 while(*hasChopped
&& !hasRealData
) {
461 r
= init_entry(name
, path
, status
);
462 /* Null pointer test */
463 if (U_FAILURE(*status
)) {
466 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
467 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
469 /* this entry is not real. We will discard it. */
470 /* However, the parent line for this entry is */
471 /* not to be used - as there might be parent */
472 /* lines in cache from previous openings that */
473 /* are not updated yet. */
475 /*entryCloseInt(r);*/
477 *status
= U_USING_FALLBACK_WARNING
;
479 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
482 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
484 /*Fallback data stuff*/
485 *hasChopped
= chopLocale(name
);
490 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
495 resB
->fMagic1
= MAGIC1
;
496 resB
->fMagic2
= MAGIC2
;
500 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
501 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
505 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
506 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
507 ures_setIsStackObject(resB
, TRUE
);
510 static UBool
// returns U_SUCCESS(*status)
511 loadParentsExceptRoot(UResourceDataEntry
*&t1
,
512 char name
[], int32_t nameCapacity
,
513 UBool usingUSRData
, char usrDataPath
[], UErrorCode
*status
) {
514 if (U_FAILURE(*status
)) { return FALSE
; }
515 UBool hasChopped
= TRUE
;
516 while (hasChopped
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
&&
517 res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
518 Resource parentRes
= res_getResource(&t1
->fData
, "%%Parent");
519 if (parentRes
!= RES_BOGUS
) { // An explicit parent was found.
520 int32_t parentLocaleLen
= 0;
521 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), parentRes
, &parentLocaleLen
);
522 if(parentLocaleName
!= NULL
&& 0 < parentLocaleLen
&& parentLocaleLen
< nameCapacity
) {
523 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+ 1);
524 if (uprv_strcmp(name
, kRootLocaleName
) == 0) {
529 // Insert regular parents.
530 UErrorCode parentStatus
= U_ZERO_ERROR
;
531 UResourceDataEntry
*t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
532 if (U_FAILURE(parentStatus
)) {
533 *status
= parentStatus
;
536 UResourceDataEntry
*u2
= NULL
;
537 UErrorCode usrStatus
= U_ZERO_ERROR
;
538 if (usingUSRData
) { // This code inserts user override data into the inheritance chain.
539 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
542 if (usingUSRData
&& U_SUCCESS(usrStatus
) && u2
->fBogus
== U_ZERO_ERROR
) {
548 // The USR override data wasn't found, set it to be deleted.
549 u2
->fCountExisting
= 0;
553 hasChopped
= chopLocale(name
);
558 static UBool
// returns U_SUCCESS(*status)
559 insertRootBundle(UResourceDataEntry
*&t1
, UErrorCode
*status
) {
560 if (U_FAILURE(*status
)) { return FALSE
; }
561 UErrorCode parentStatus
= U_ZERO_ERROR
;
562 UResourceDataEntry
*t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
563 if (U_FAILURE(parentStatus
)) {
564 *status
= parentStatus
;
574 * Open a resource bundle for the locale;
575 * if there is not even a base language bundle, then fall back to the default locale;
576 * if there is no bundle for that either, then load the root bundle.
578 * This is the default bundle loading behavior.
580 URES_OPEN_LOCALE_DEFAULT_ROOT
,
581 // TODO: ICU ticket #11271 "consistent default locale across locale trees"
582 // Add an option to look at the main locale tree for whether to
583 // fall back to root directly (if the locale has main data) or
584 // fall back to the default locale first (if the locale does not even have main data).
586 * Open a resource bundle for the locale;
587 * if there is not even a base language bundle, then load the root bundle;
588 * never fall back to the default locale.
590 * This is used for algorithms that have good pan-Unicode default behavior,
591 * such as case mappings, collation, and segmentation (BreakIterator).
593 URES_OPEN_LOCALE_ROOT
,
595 * Open a resource bundle for the exact bundle name as requested;
596 * no fallbacks, do not load parent bundles.
598 * This is used for supplemental (non-locale) data.
602 typedef enum UResOpenType UResOpenType
;
604 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
,
605 UResOpenType openType
, UErrorCode
* status
) {
606 U_ASSERT(openType
!= URES_OPEN_DIRECT
);
607 UErrorCode intStatus
= U_ZERO_ERROR
;
608 UResourceDataEntry
*r
= NULL
;
609 UResourceDataEntry
*t1
= NULL
;
610 UBool isDefault
= FALSE
;
611 UBool isRoot
= FALSE
;
612 UBool hasRealData
= FALSE
;
613 UBool hasChopped
= TRUE
;
614 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
616 char name
[ULOC_FULLNAME_CAPACITY
];
617 char usrDataPath
[96];
621 if(U_FAILURE(*status
)) {
625 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
626 name
[sizeof(name
) - 1] = 0;
628 if ( usingUSRData
) {
629 if ( path
== NULL
) {
630 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
632 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
633 usrDataPath
[0] = 'u';
634 usrDataPath
[1] = 's';
635 usrDataPath
[2] = 'r';
636 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
640 umtx_lock(&resbMutex
);
642 /* We're going to skip all the locales that do not have any data */
643 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
645 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
648 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
649 UErrorCode usrStatus
= U_ZERO_ERROR
;
650 UResourceDataEntry
*u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
652 if(u1
->fBogus
== U_ZERO_ERROR
) {
656 /* the USR override data wasn't found, set it to be deleted */
657 u1
->fCountExisting
= 0;
661 if (hasChopped
&& !isRoot
) {
662 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
668 /* we could have reached this point without having any real data */
669 /* if that is the case, we need to chain in the default locale */
670 if(r
==NULL
&& openType
== URES_OPEN_LOCALE_DEFAULT_ROOT
&& !isDefault
&& !isRoot
) {
671 /* insert default locale */
672 uprv_strcpy(name
, uloc_getDefault());
673 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
674 intStatus
= U_USING_DEFAULT_WARNING
;
675 if(r
!= NULL
) { /* the default locale exists */
679 // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
680 if (hasChopped
&& !isRoot
) {
681 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
688 /* we could still have r == NULL at this point - maybe even default locale is not */
691 uprv_strcpy(name
, kRootLocaleName
);
692 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
695 intStatus
= U_USING_DEFAULT_WARNING
;
697 } else { /* we don't even have the root locale */
698 *status
= U_MISSING_RESOURCE_ERROR
;
701 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 &&
702 t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
703 if (!insertRootBundle(t1
, status
)) {
707 r
->fBogus
= U_USING_DEFAULT_WARNING
;
711 // TODO: Does this ever loop? A: Yes, it can often loop up to 4 times or so.
712 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
713 t1
->fParent
->fCountExisting
++;
718 umtx_unlock(&resbMutex
);
720 if(U_SUCCESS(*status
)) {
721 if(intStatus
!= U_ZERO_ERROR
) {
731 * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
733 * Parent and root locale bundles are loaded if
734 * the requested bundle does not have the "nofallback" flag.
736 static UResourceDataEntry
*
737 entryOpenDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
739 if(U_FAILURE(*status
)) {
743 umtx_lock(&resbMutex
);
744 // findFirstExisting() without fallbacks.
745 UResourceDataEntry
*r
= init_entry(localeID
, path
, status
);
746 if(U_SUCCESS(*status
)) {
747 if(r
->fBogus
!= U_ZERO_ERROR
) {
755 // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
756 // unless it is marked with "nofallback".
757 UResourceDataEntry
*t1
= r
;
758 if(r
!= NULL
&& uprv_strcmp(localeID
, kRootLocaleName
) != 0 && // not root
759 r
->fParent
== NULL
&& !r
->fData
.noFallback
&&
760 uprv_strlen(localeID
) < ULOC_FULLNAME_CAPACITY
) {
761 char name
[ULOC_FULLNAME_CAPACITY
];
762 uprv_strcpy(name
, localeID
);
763 if(!chopLocale(name
) || uprv_strcmp(name
, kRootLocaleName
) == 0 ||
764 loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), FALSE
, NULL
, status
)) {
765 if(uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
) {
766 insertRootBundle(t1
, status
);
769 if(U_FAILURE(*status
)) {
775 // TODO: Does this ever loop?
776 while(t1
->fParent
!= NULL
) {
777 t1
->fParent
->fCountExisting
++;
781 umtx_unlock(&resbMutex
);
786 * Functions to create and destroy resource bundles.
787 * CAUTION: resbMutex must be locked when calling this function.
790 static void entryCloseInt(UResourceDataEntry
*resB
) {
791 UResourceDataEntry
*p
= resB
;
793 while(resB
!= NULL
) {
795 resB
->fCountExisting
--;
797 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
800 if(resB->fCountExisting <= 0) {
801 uhash_remove(cache, resB);
802 if(resB->fBogus == U_ZERO_ERROR) {
803 res_unload(&(resB->fData));
805 if(resB->fName != NULL) {
806 uprv_free(resB->fName);
808 if(resB->fPath != NULL) {
809 uprv_free(resB->fPath);
820 * API: closes a resource bundle and cleans up.
823 static void entryClose(UResourceDataEntry
*resB
) {
824 umtx_lock(&resbMutex
);
826 umtx_unlock(&resbMutex
);
830 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
831 if(resB->fResPath == NULL) {
832 resB->fResPath = resB->fResBuf;
833 *(resB->fResPath) = 0;
835 resB->fResPathLen = uprv_strlen(toAdd);
836 if(RES_BUFSIZE <= resB->fResPathLen+1) {
837 if(resB->fResPath == resB->fResBuf) {
838 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
840 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
843 uprv_strcpy(resB->fResPath, toAdd);
846 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
847 int32_t resPathLenOrig
= resB
->fResPathLen
;
848 if(resB
->fResPath
== NULL
) {
849 resB
->fResPath
= resB
->fResBuf
;
850 *(resB
->fResPath
) = 0;
851 resB
->fResPathLen
= 0;
853 resB
->fResPathLen
+= lenToAdd
;
854 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
855 if(resB
->fResPath
== resB
->fResBuf
) {
856 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
857 /* Check that memory was allocated correctly. */
858 if (resB
->fResPath
== NULL
) {
859 *status
= U_MEMORY_ALLOCATION_ERROR
;
862 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
864 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
865 /* Check that memory was reallocated correctly. */
867 *status
= U_MEMORY_ALLOCATION_ERROR
;
870 resB
->fResPath
= temp
;
873 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
876 static void ures_freeResPath(UResourceBundle
*resB
) {
877 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
878 uprv_free(resB
->fResPath
);
880 resB
->fResPath
= NULL
;
881 resB
->fResPathLen
= 0;
885 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
888 if(resB
->fData
!= NULL
) {
889 entryClose(resB
->fData
);
891 if(resB
->fVersion
!= NULL
) {
892 uprv_free(resB
->fVersion
);
894 ures_freeResPath(resB
);
896 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
901 /* poison the data */
902 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
908 U_CAPI
void U_EXPORT2
909 ures_close(UResourceBundle
* resB
)
911 ures_closeBundle(resB
, TRUE
);
914 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
915 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
916 const UResourceBundle
*parent
, int32_t noAlias
,
917 UResourceBundle
*resB
, UErrorCode
*status
)
919 if(status
== NULL
|| U_FAILURE(*status
)) {
922 if (parent
== NULL
) {
923 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
926 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
927 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
929 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
931 /* we have an alias, now let's cut it up */
932 char stackAlias
[200];
933 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
937 * Allocate enough space for both the char * version
938 * of the alias and parent->fResPath.
940 * We do this so that res_findResource() can modify the path,
941 * which allows us to remove redundant _res_findResource() variants
943 * res_findResource() now NUL-terminates each segment so that table keys
944 * can always be compared with strcmp() instead of strncmp().
945 * Saves code there and simplifies testing and code coverage.
949 ++len
; /* count the terminating NUL */
950 if(parent
->fResPath
!= NULL
) {
951 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
958 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
959 capacity
= (int32_t)sizeof(stackAlias
);
960 chAlias
= stackAlias
;
962 chAlias
= (char *)uprv_malloc(capacity
);
964 if(chAlias
== NULL
) {
965 *status
= U_MEMORY_ALLOCATION_ERROR
;
969 u_UCharsToChars(alias
, chAlias
, len
);
971 if(*chAlias
== RES_PATH_SEPARATOR
) {
972 /* there is a path included */
973 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
975 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
981 if(uprv_strcmp(path
, "LOCALE") == 0) {
982 /* this is an XPath alias, starting with "/LOCALE/" */
983 /* it contains the path to a resource which should be looked up */
984 /* starting in the requested locale */
986 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
987 path
= realData
->fPath
; /* we will be looking in the same package */
989 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
992 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
999 /* no path, start with a locale */
1001 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1006 path
= realData
->fPath
;
1011 /* got almost everything, let's try to open */
1012 /* first, open the bundle with real data */
1013 UResourceBundle
*result
= resB
;
1014 const char* temp
= NULL
;
1015 UErrorCode intStatus
= U_ZERO_ERROR
;
1016 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
1017 if(U_SUCCESS(intStatus
)) {
1018 if(keyPath
== NULL
) {
1019 /* no key path. This means that we are going to
1020 * to use the corresponding resource from
1023 /* first, we are going to get a corresponding parent
1024 * resource to the one we are searching.
1026 char *aKey
= parent
->fResPath
;
1028 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
1030 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
1035 /* we need to make keyPath from parent's fResPath and
1036 * current key, if there is a key associated
1038 len
= (int32_t)(uprv_strlen(key
) + 1);
1039 if(len
> capacity
) {
1041 if(chAlias
== stackAlias
) {
1042 chAlias
= (char *)uprv_malloc(capacity
);
1044 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
1046 if(chAlias
== NULL
) {
1047 ures_close(mainRes
);
1048 *status
= U_MEMORY_ALLOCATION_ERROR
;
1052 uprv_memcpy(chAlias
, key
, len
);
1054 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
1055 } else if(idx
!= -1) {
1056 /* if there is no key, but there is an index, try to get by the index */
1057 /* here we have either a table or an array, so get the element */
1058 int32_t type
= RES_GET_TYPE(r
);
1059 if(URES_IS_TABLE(type
)) {
1060 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
1061 } else { /* array */
1062 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
1065 if(r
!= RES_BOGUS
) {
1066 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
1068 *status
= U_MISSING_RESOURCE_ERROR
;
1072 /* this one is a bit trickier.
1073 * we start finding keys, but after we resolve one alias, the path might continue.
1075 * aliastest:alias { "testtypes/anotheralias/Sequence" }
1076 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1077 * aliastest resource should finally have the sequence, not collation elements.
1079 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1080 char stackPath
[URES_MAX_BUFFER_SIZE
];
1081 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1082 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
1083 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1084 if(pathBuf
== NULL
) {
1085 *status
= U_MEMORY_ALLOCATION_ERROR
;
1089 uprv_strcpy(pathBuf
, keyPath
);
1091 /* now we have fallback following here */
1093 r
= dataEntry
->fData
.rootRes
;
1094 /* this loop handles 'found' resources over several levels */
1095 while(*myPath
&& U_SUCCESS(*status
)) {
1096 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1097 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1098 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1101 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1102 dataEntry
= result
->fData
;
1104 } else { /* no resource found, we don't really want to look anymore on this level */
1108 dataEntry
= dataEntry
->fParent
;
1109 uprv_strcpy(pathBuf
, keyPath
);
1111 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1112 if(r
== RES_BOGUS
) {
1113 *status
= U_MISSING_RESOURCE_ERROR
;
1116 if(pathBuf
!= stackPath
) {
1120 } else { /* we failed to open the resource we're aliasing to */
1121 *status
= intStatus
;
1123 if(chAlias
!= stackAlias
) {
1126 if(mainRes
!= result
) {
1127 ures_close(mainRes
);
1132 /* bad alias, should be an error */
1133 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1137 *status
= U_TOO_MANY_ALIASES_ERROR
;
1142 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1145 *status
= U_MEMORY_ALLOCATION_ERROR
;
1148 ures_setIsStackObject(resB
, FALSE
);
1149 resB
->fResPath
= NULL
;
1150 resB
->fResPathLen
= 0;
1152 if(resB
->fData
!= NULL
) {
1153 entryClose(resB
->fData
);
1155 if(resB
->fVersion
!= NULL
) {
1156 uprv_free(resB
->fVersion
);
1159 weiv: if stack object was passed in, it doesn't really need to be reinited,
1160 since the purpose of initing is to remove stack junk. However, at this point
1161 we would not do anything to an allocated object, so stack object should be
1165 if(ures_isStackObject(resB) != FALSE) {
1166 ures_initStackObject(resB);
1169 if(parent
!= resB
) {
1170 ures_freeResPath(resB
);
1173 resB
->fData
= realData
;
1174 entryIncrease(resB
->fData
);
1175 resB
->fHasFallback
= FALSE
;
1176 resB
->fIsTopLevel
= FALSE
;
1179 /*resB->fParentRes = parent;*/
1180 resB
->fTopLevelData
= parent
->fTopLevelData
;
1181 if(parent
->fResPath
&& parent
!= resB
) {
1182 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1185 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1186 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1187 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1189 } else if(idx
>= 0) {
1191 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1192 ures_appendResPath(resB
, buf
, len
, status
);
1193 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1194 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1197 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1199 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1200 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1203 resB
->fVersion
= NULL
;
1205 /*resB->fParent = parent->fRes;*/
1206 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1207 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1211 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1212 UBool isStackObject
;
1213 if(U_FAILURE(*status
) || r
== original
) {
1216 if(original
!= NULL
) {
1218 isStackObject
= FALSE
;
1219 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1222 *status
= U_MEMORY_ALLOCATION_ERROR
;
1226 isStackObject
= ures_isStackObject(r
);
1227 ures_closeBundle(r
, FALSE
);
1229 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1232 if(original
->fResPath
) {
1233 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1235 ures_setIsStackObject(r
, isStackObject
);
1236 if(r
->fData
!= NULL
) {
1237 entryIncrease(r
->fData
);
1244 * Functions to retrieve data from resource bundles.
1247 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1249 if (status
==NULL
|| U_FAILURE(*status
)) {
1253 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1256 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1258 *status
= U_RESOURCE_TYPE_MISMATCH
;
1264 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1265 char *dest
, int32_t *pLength
,
1267 UErrorCode
*status
) {
1270 if (U_FAILURE(*status
)) {
1273 if (pLength
!= NULL
) {
1274 capacity
= *pLength
;
1278 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1279 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1283 if (length16
== 0) {
1284 /* empty string, return as read-only pointer */
1285 if (pLength
!= NULL
) {
1289 u_terminateChars(dest
, capacity
, 0, status
);
1295 /* We need to transform the string to the destination buffer. */
1296 if (capacity
< length16
) {
1297 /* No chance for the string to fit. Pure preflighting. */
1298 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1300 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1302 * We know the string will fit into dest because each UChar turns
1303 * into at most three UTF-8 bytes. Fill the latter part of dest
1304 * so that callers do not expect to use dest as a string pointer,
1305 * hopefully leading to more robust code for when resource bundles
1306 * may store UTF-8 natively.
1307 * (In which case dest would not be used at all.)
1309 * We do not do this if forceCopy=TRUE because then the caller
1310 * expects the string to start exactly at dest.
1312 * The test above for <= 0x2aaaaaaa prevents overflows.
1313 * The +1 is for the NUL terminator.
1315 int32_t maxLength
= 3 * length16
+ 1;
1316 if (capacity
> maxLength
) {
1317 dest
+= capacity
- maxLength
;
1318 capacity
= maxLength
;
1321 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1325 U_CAPI
const char * U_EXPORT2
1326 ures_getUTF8String(const UResourceBundle
*resB
,
1327 char *dest
, int32_t *pLength
,
1329 UErrorCode
*status
) {
1331 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1332 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1335 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1336 UErrorCode
* status
) {
1338 if (status
==NULL
|| U_FAILURE(*status
)) {
1342 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1345 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1347 *status
= U_RESOURCE_TYPE_MISMATCH
;
1352 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1353 UErrorCode
* status
) {
1355 if (status
==NULL
|| U_FAILURE(*status
)) {
1359 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1362 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1364 *status
= U_RESOURCE_TYPE_MISMATCH
;
1369 /* this function returns a signed integer */
1370 /* it performs sign extension */
1371 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1372 if (status
==NULL
|| U_FAILURE(*status
)) {
1376 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1379 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1380 *status
= U_RESOURCE_TYPE_MISMATCH
;
1383 return RES_GET_INT(resB
->fRes
);
1386 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1387 if (status
==NULL
|| U_FAILURE(*status
)) {
1391 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1394 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1395 *status
= U_RESOURCE_TYPE_MISMATCH
;
1398 return RES_GET_UINT(resB
->fRes
);
1401 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1405 return res_getPublicType(resB
->fRes
);
1408 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1416 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1424 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1425 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1426 const UChar
* result
= 0;
1427 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1428 result
= ures_getString(tempRes
, len
, status
);
1429 ures_close(tempRes
);
1432 return res_getString(&(resB
->fResData
), r
, len
);
1436 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1443 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1447 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1450 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1451 Resource r
= RES_BOGUS
;
1453 if (status
==NULL
|| U_FAILURE(*status
)) {
1457 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1461 if(resB
->fIndex
== resB
->fSize
-1) {
1462 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1465 switch(RES_GET_TYPE(resB
->fRes
)) {
1467 case URES_STRING_V2
:
1468 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1472 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1473 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1474 /* TODO: do the fallback */
1476 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1479 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1480 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1481 /* TODO: do the fallback */
1483 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1485 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1488 case URES_INT_VECTOR
:
1489 *status
= U_RESOURCE_TYPE_MISMATCH
;
1499 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1500 const char *key
= NULL
;
1501 Resource r
= RES_BOGUS
;
1503 if (status
==NULL
|| U_FAILURE(*status
)) {
1508 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1513 if(resB
->fIndex
== resB
->fSize
-1) {
1514 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1518 switch(RES_GET_TYPE(resB
->fRes
)) {
1522 case URES_STRING_V2
:
1523 case URES_INT_VECTOR
:
1524 return ures_copyResb(fillIn
, resB
, status
);
1528 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1529 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1530 /* TODO: do the fallback */
1532 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1535 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1536 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1537 /* TODO: do the fallback */
1539 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1549 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1550 const char* key
= NULL
;
1551 Resource r
= RES_BOGUS
;
1553 if (status
==NULL
|| U_FAILURE(*status
)) {
1558 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1563 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1564 switch(RES_GET_TYPE(resB
->fRes
)) {
1568 case URES_STRING_V2
:
1569 case URES_INT_VECTOR
:
1570 return ures_copyResb(fillIn
, resB
, status
);
1574 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1575 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1576 /* TODO: do the fallback */
1578 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1581 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1582 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1583 /* TODO: do the fallback */
1585 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1591 *status
= U_MISSING_RESOURCE_ERROR
;
1597 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1598 const char* key
= NULL
;
1599 Resource r
= RES_BOGUS
;
1601 if (status
==NULL
|| U_FAILURE(*status
)) {
1605 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1609 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1610 switch(RES_GET_TYPE(resB
->fRes
)) {
1612 case URES_STRING_V2
:
1613 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1617 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1618 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1619 /* TODO: do the fallback */
1621 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1624 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1625 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1626 /* TODO: do the fallback */
1628 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1630 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1633 case URES_INT_VECTOR
:
1634 *status
= U_RESOURCE_TYPE_MISMATCH
;
1637 /* must not occur */
1638 *status
= U_INTERNAL_PROGRAM_ERROR
;
1642 *status
= U_MISSING_RESOURCE_ERROR
;
1647 U_CAPI
const char * U_EXPORT2
1648 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1650 char *dest
, int32_t *pLength
,
1652 UErrorCode
*status
) {
1654 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1655 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1658 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1659 return resB->fResPath;
1662 U_CAPI UResourceBundle
* U_EXPORT2
1663 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1665 UResourceBundle
*first
= NULL
;
1666 UResourceBundle
*result
= fillIn
;
1667 char *packageName
= NULL
;
1668 char *pathToResource
= NULL
, *save
= NULL
;
1669 char *locale
= NULL
, *localeEnd
= NULL
;
1672 if(status
== NULL
|| U_FAILURE(*status
)) {
1676 length
= (int32_t)(uprv_strlen(path
)+1);
1677 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1679 if(pathToResource
== NULL
) {
1680 *status
= U_MEMORY_ALLOCATION_ERROR
;
1683 uprv_memcpy(pathToResource
, path
, length
);
1685 locale
= pathToResource
;
1686 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1688 packageName
= pathToResource
;
1689 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1690 if(pathToResource
== NULL
) {
1691 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1693 *pathToResource
= 0;
1694 locale
= pathToResource
+1;
1698 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1699 if(localeEnd
!= NULL
) {
1703 first
= ures_open(packageName
, locale
, status
);
1705 if(U_SUCCESS(*status
)) {
1707 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1709 result
= ures_copyResb(fillIn
, first
, status
);
1717 U_CAPI UResourceBundle
* U_EXPORT2
1718 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1720 Resource res
= RES_BOGUS
;
1721 UResourceBundle
*result
= fillIn
;
1724 if(status
== NULL
|| U_FAILURE(*status
)) {
1728 /* here we do looping and circular alias checking */
1729 /* this loop is here because aliasing is resolved on this level, not on res level */
1730 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1732 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1733 if(res
!= RES_BOGUS
) {
1734 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1737 *status
= U_MISSING_RESOURCE_ERROR
;
1740 } while(*path
); /* there is more stuff in the path */
1744 U_INTERNAL
const UChar
* U_EXPORT2
1745 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1748 UErrorCode
*status
) {
1750 UResourceBundle stack
;
1751 const UChar
* retVal
= NULL
;
1752 ures_initStackObject(&stack
);
1753 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1755 retVal
= ures_getString(&stack
, &length
, status
);
1757 if (U_FAILURE(*status
)) {
1760 if (length
== 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1763 *status
= U_MISSING_RESOURCE_ERROR
;
1772 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1774 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1775 Resource resource
= table
; /* The current resource */
1776 icu::CharString path
;
1777 UErrorCode errorCode
= U_ZERO_ERROR
;
1778 path
.append(key
, errorCode
);
1779 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1780 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1781 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1782 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1783 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1784 if (nextPathPart
!= NULL
) {
1785 *nextPathPart
= 0; /* Terminating null for this part of path. */
1788 nextPathPart
= uprv_strchr(pathPart
, 0);
1791 const char *pathP
= pathPart
;
1792 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
1793 type
= (UResType
)RES_GET_TYPE(resource
);
1794 pathPart
= nextPathPart
;
1802 U_CAPI UResourceBundle
* U_EXPORT2
1803 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1805 UResourceBundle
*fillIn
,
1806 UErrorCode
*status
) {
1807 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1808 /*UResourceDataEntry *realData = NULL;*/
1809 UResourceBundle
*helper
= NULL
;
1811 if (status
==NULL
|| U_FAILURE(*status
)) {
1815 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1819 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1820 if(URES_IS_TABLE(type
)) {
1821 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
1822 const char* key
= inKey
;
1823 if(res
== RES_BOGUS
) {
1824 UResourceDataEntry
*dataEntry
= resB
->fData
;
1826 char *myPath
= NULL
;
1827 const char* resPath
= resB
->fResPath
;
1828 int32_t len
= resB
->fResPathLen
;
1829 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1830 dataEntry
= dataEntry
->fParent
;
1831 rootRes
= dataEntry
->fData
.rootRes
;
1833 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1836 path
.append(resPath
, len
, *status
);
1838 path
.append(inKey
, *status
);
1839 if (U_FAILURE(*status
)) {
1843 myPath
= path
.data();
1846 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1847 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1848 /* We hit an alias, but we didn't finish following the path. */
1849 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1850 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1852 dataEntry
= helper
->fData
;
1853 rootRes
= helper
->fRes
;
1854 resPath
= helper
->fResPath
;
1855 len
= helper
->fResPathLen
;
1861 } while(*myPath
); /* Continue until the whole path is consumed */
1864 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1865 if(res
!= RES_BOGUS
) {
1866 /* check if resB->fResPath gives the right name here */
1867 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1868 *status
= U_USING_DEFAULT_WARNING
;
1870 *status
= U_USING_FALLBACK_WARNING
;
1873 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1875 *status
= U_MISSING_RESOURCE_ERROR
;
1878 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1882 *status
= U_RESOURCE_TYPE_MISMATCH
;
1890 void getAllItemsWithFallback(
1891 const UResourceBundle
*bundle
, ResourceDataValue
&value
,
1893 UErrorCode
&errorCode
) {
1894 if (U_FAILURE(errorCode
)) { return; }
1895 // We recursively enumerate child-first,
1896 // only storing parent items in the absence of child items.
1897 // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
1898 // to prevent a parent item from being stored.
1900 // It would be possible to recursively enumerate parent-first,
1901 // overriding parent items with child items.
1902 // When the sink sees the no-fallback/no-inheritance marker,
1903 // then it would remove the parent's item.
1904 // We would deserialize parent values even though they are overridden in a child bundle.
1905 value
.pResData
= &bundle
->fResData
;
1906 UResourceDataEntry
*parentEntry
= bundle
->fData
->fParent
;
1907 UBool hasParent
= parentEntry
!= NULL
&& U_SUCCESS(parentEntry
->fBogus
);
1908 value
.setResource(bundle
->fRes
);
1909 sink
.put(bundle
->fKey
, value
, !hasParent
, errorCode
);
1911 // We might try to query the sink whether
1912 // any fallback from the parent bundle is still possible.
1914 // Turn the parent UResourceDataEntry into a UResourceBundle,
1915 // much like in ures_openWithType().
1916 // TODO: See if we can refactor ures_getByKeyWithFallback()
1917 // and pull out an inner function that takes and returns a UResourceDataEntry
1918 // so that we need not create UResourceBundle objects.
1919 UResourceBundle parentBundle
;
1920 ures_initStackObject(&parentBundle
);
1921 parentBundle
.fTopLevelData
= parentBundle
.fData
= parentEntry
;
1922 // TODO: What is the difference between bundle fData and fTopLevelData?
1923 uprv_memcpy(&parentBundle
.fResData
, &parentEntry
->fData
, sizeof(ResourceData
));
1924 // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1925 parentBundle
.fHasFallback
= !parentBundle
.fResData
.noFallback
;
1926 parentBundle
.fIsTopLevel
= TRUE
;
1927 parentBundle
.fRes
= parentBundle
.fResData
.rootRes
;
1928 parentBundle
.fSize
= res_countArrayItems(&(parentBundle
.fResData
), parentBundle
.fRes
);
1929 parentBundle
.fIndex
= -1;
1930 entryIncrease(parentEntry
);
1932 // Look up the container item in the parent bundle.
1933 UResourceBundle containerBundle
;
1934 ures_initStackObject(&containerBundle
);
1935 const UResourceBundle
*rb
;
1936 UErrorCode pathErrorCode
= U_ZERO_ERROR
; // Ignore if parents up to root do not have this path.
1937 if (bundle
->fResPath
== NULL
|| *bundle
->fResPath
== 0) {
1940 rb
= ures_getByKeyWithFallback(&parentBundle
, bundle
->fResPath
,
1941 &containerBundle
, &pathErrorCode
);
1943 if (U_SUCCESS(pathErrorCode
)) {
1944 getAllItemsWithFallback(rb
, value
, sink
, errorCode
);
1946 ures_close(&containerBundle
);
1947 ures_close(&parentBundle
);
1953 U_CAPI
void U_EXPORT2
1954 ures_getAllItemsWithFallback(const UResourceBundle
*bundle
, const char *path
,
1955 icu::ResourceSink
&sink
, UErrorCode
&errorCode
) {
1956 if (U_FAILURE(errorCode
)) { return; }
1958 errorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
1961 UResourceBundle stackBundle
;
1962 ures_initStackObject(&stackBundle
);
1963 const UResourceBundle
*rb
;
1968 rb
= ures_getByKeyWithFallback(bundle
, path
, &stackBundle
, &errorCode
);
1969 if (U_FAILURE(errorCode
)) {
1970 ures_close(&stackBundle
);
1974 // Get all table items with fallback.
1975 ResourceDataValue value
;
1976 getAllItemsWithFallback(rb
, value
, sink
, errorCode
);
1977 ures_close(&stackBundle
);
1980 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1981 Resource res
= RES_BOGUS
;
1982 UResourceDataEntry
*realData
= NULL
;
1983 const char *key
= inKey
;
1985 if (status
==NULL
|| U_FAILURE(*status
)) {
1989 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1993 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1994 if(URES_IS_TABLE(type
)) {
1996 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1997 if(res
== RES_BOGUS
) {
1999 if(resB
->fHasFallback
== TRUE
) {
2000 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2001 if(U_SUCCESS(*status
)) {
2002 /* check if resB->fResPath gives the right name here */
2003 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
2005 *status
= U_MISSING_RESOURCE_ERROR
;
2008 *status
= U_MISSING_RESOURCE_ERROR
;
2011 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
2015 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2017 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2018 /* here should go a first attempt to locate the key using index table */
2019 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2020 if(U_SUCCESS(*status
)) {
2021 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
2023 *status
= U_MISSING_RESOURCE_ERROR
;
2028 *status
= U_RESOURCE_TYPE_MISMATCH
;
2033 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
2034 Resource res
= RES_BOGUS
;
2035 UResourceDataEntry
*realData
= NULL
;
2036 const char* key
= inKey
;
2038 if (status
==NULL
|| U_FAILURE(*status
)) {
2042 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2046 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2047 if(URES_IS_TABLE(type
)) {
2050 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2052 if(res
== RES_BOGUS
) {
2054 if(resB
->fHasFallback
== TRUE
) {
2055 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2056 if(U_SUCCESS(*status
)) {
2057 switch (RES_GET_TYPE(res
)) {
2059 case URES_STRING_V2
:
2060 return res_getString(rd
, res
, len
);
2063 const UChar
* result
= 0;
2064 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2065 result
= ures_getString(tempRes
, len
, status
);
2066 ures_close(tempRes
);
2070 *status
= U_RESOURCE_TYPE_MISMATCH
;
2073 *status
= U_MISSING_RESOURCE_ERROR
;
2076 *status
= U_MISSING_RESOURCE_ERROR
;
2079 switch (RES_GET_TYPE(res
)) {
2081 case URES_STRING_V2
:
2082 return res_getString(&(resB
->fResData
), res
, len
);
2085 const UChar
* result
= 0;
2086 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2087 result
= ures_getString(tempRes
, len
, status
);
2088 ures_close(tempRes
);
2092 *status
= U_RESOURCE_TYPE_MISMATCH
;
2097 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2099 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2100 /* here should go a first attempt to locate the key using index table */
2101 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2102 if(U_SUCCESS(*status
)) {
2103 return res_getString(rd
, res
, len
);
2105 *status
= U_MISSING_RESOURCE_ERROR
;
2110 *status
= U_RESOURCE_TYPE_MISMATCH
;
2115 U_CAPI
const char * U_EXPORT2
2116 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
2118 char *dest
, int32_t *pLength
,
2120 UErrorCode
*status
) {
2122 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
2123 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
2126 /* TODO: clean from here down */
2129 * INTERNAL: Get the name of the first real locale (not placeholder)
2130 * that has resource bundle data.
2132 U_INTERNAL
const char* U_EXPORT2
2133 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
2135 if (status
==NULL
|| U_FAILURE(*status
)) {
2138 if (!resourceBundle
) {
2139 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2142 return resourceBundle
->fData
->fName
;
2146 U_CAPI
const char* U_EXPORT2
2147 ures_getLocale(const UResourceBundle
* resourceBundle
,
2150 return ures_getLocaleInternal(resourceBundle
, status
);
2154 U_CAPI
const char* U_EXPORT2
2155 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
2156 ULocDataLocaleType type
,
2157 UErrorCode
* status
) {
2158 if (status
==NULL
|| U_FAILURE(*status
)) {
2161 if (!resourceBundle
) {
2162 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2166 case ULOC_ACTUAL_LOCALE
:
2167 return resourceBundle
->fData
->fName
;
2168 case ULOC_VALID_LOCALE
:
2169 return resourceBundle
->fTopLevelData
->fName
;
2170 case ULOC_REQUESTED_LOCALE
:
2172 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2178 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
2183 return resB
->fData
->fName
;
2187 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2192 return resB
->fData
->fPath
;
2196 static UResourceBundle
*
2197 ures_openWithType(UResourceBundle
*r
, const char* path
, const char* localeID
,
2198 UResOpenType openType
, UErrorCode
* status
) {
2199 if(U_FAILURE(*status
)) {
2203 UResourceDataEntry
*entry
;
2204 if(openType
!= URES_OPEN_DIRECT
) {
2205 /* first "canonicalize" the locale ID */
2206 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2207 uloc_getBaseName(localeID
, canonLocaleID
, UPRV_LENGTHOF(canonLocaleID
), status
);
2208 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2209 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2212 entry
= entryOpen(path
, canonLocaleID
, openType
, status
);
2214 entry
= entryOpenDirect(path
, localeID
, status
);
2216 if(U_FAILURE(*status
)) {
2220 *status
= U_MISSING_RESOURCE_ERROR
;
2224 UBool isStackObject
;
2226 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2229 *status
= U_MEMORY_ALLOCATION_ERROR
;
2232 isStackObject
= FALSE
;
2234 isStackObject
= ures_isStackObject(r
);
2235 ures_closeBundle(r
, FALSE
);
2237 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2238 ures_setIsStackObject(r
, isStackObject
);
2240 r
->fTopLevelData
= r
->fData
= entry
;
2241 uprv_memcpy(&r
->fResData
, &entry
->fData
, sizeof(ResourceData
));
2242 r
->fHasFallback
= openType
!= URES_OPEN_DIRECT
&& !r
->fResData
.noFallback
;
2243 r
->fIsTopLevel
= TRUE
;
2244 r
->fRes
= r
->fResData
.rootRes
;
2245 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2251 U_CAPI UResourceBundle
* U_EXPORT2
2252 ures_open(const char* path
, const char* localeID
, UErrorCode
* status
) {
2253 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2256 U_CAPI UResourceBundle
* U_EXPORT2
2257 ures_openNoDefault(const char* path
, const char* localeID
, UErrorCode
* status
) {
2258 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_ROOT
, status
);
2262 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2263 * or sought. However, alias substitution will happen!
2265 U_CAPI UResourceBundle
* U_EXPORT2
2266 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2267 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2271 * API: This function is used to open a resource bundle
2272 * proper fallback chaining is executed while initialization.
2273 * The result is stored in cache for later fallback search.
2275 U_CAPI
void U_EXPORT2
2276 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2277 const char* localeID
, UErrorCode
* status
) {
2278 if(U_SUCCESS(*status
) && r
== NULL
) {
2279 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2282 ures_openWithType(r
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2286 * API: Counts members. For arrays and tables, returns number of resources.
2287 * For strings, returns 1.
2289 U_CAPI
int32_t U_EXPORT2
2290 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2291 const char* resourceKey
,
2294 UResourceBundle resData
;
2295 ures_initStackObject(&resData
);
2296 if (status
==NULL
|| U_FAILURE(*status
)) {
2299 if(resourceBundle
== NULL
) {
2300 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2303 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2305 if(resData
.fResData
.data
!= NULL
) {
2306 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2307 ures_close(&resData
);
2310 *status
= U_MISSING_RESOURCE_ERROR
;
2311 ures_close(&resData
);
2317 * Internal function.
2318 * Return the version number associated with this ResourceBundle as a string.
2320 * @param resourceBundle The resource bundle for which the version is checked.
2321 * @return A version number string as specified in the resource bundle or its parent.
2322 * The caller does not own this string.
2323 * @see ures_getVersion
2326 U_INTERNAL
const char* U_EXPORT2
2327 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2329 if (!resourceBundle
) return NULL
;
2331 if(resourceBundle
->fVersion
== NULL
) {
2333 /* If the version ID has not been built yet, then do so. Retrieve */
2334 /* the minor version from the file. */
2335 UErrorCode status
= U_ZERO_ERROR
;
2336 int32_t minor_len
= 0;
2339 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2341 /* Determine the length of of the final version string. This is */
2342 /* the length of the major part + the length of the separator */
2343 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2346 len
= (minor_len
> 0) ? minor_len
: 1;
2348 /* Allocate the string, and build it up. */
2349 /* + 1 for zero byte */
2352 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2353 /* Check for null pointer. */
2354 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2359 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2360 resourceBundle
->fVersion
[len
] = '\0';
2363 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2367 return resourceBundle
->fVersion
;
2370 U_CAPI
const char* U_EXPORT2
2371 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2373 return ures_getVersionNumberInternal(resourceBundle
);
2376 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2379 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2382 /** Tree support functions *******************************/
2383 #define INDEX_LOCALE_NAME "res_index"
2384 #define INDEX_TAG "InstalledLocales"
2385 #define DEFAULT_TAG "default"
2387 #if defined(URES_TREE_DEBUG)
2391 typedef struct ULocalesContext
{
2392 UResourceBundle installed
;
2393 UResourceBundle curr
;
2396 static void U_CALLCONV
2397 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2398 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2399 ures_close(&ctx
->curr
);
2400 ures_close(&ctx
->installed
);
2402 uprv_free(enumerator
);
2405 static int32_t U_CALLCONV
2406 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2407 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2408 return ures_getSize(&ctx
->installed
);
2414 static const char * U_CALLCONV
2415 ures_loc_nextLocale(UEnumeration
* en
,
2416 int32_t* resultLength
,
2417 UErrorCode
* status
) {
2418 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2419 UResourceBundle
*res
= &(ctx
->installed
);
2420 UResourceBundle
*k
= NULL
;
2421 const char *result
= NULL
;
2423 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2424 result
= ures_getKey(k
);
2425 len
= (int32_t)uprv_strlen(result
);
2428 *resultLength
= len
;
2433 static void U_CALLCONV
2434 ures_loc_resetLocales(UEnumeration
* en
,
2435 UErrorCode
* /*status*/) {
2436 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2437 ures_resetIterator(res
);
2442 static const UEnumeration gLocalesEnum
= {
2445 ures_loc_closeLocales
,
2446 ures_loc_countLocales
,
2448 ures_loc_nextLocale
,
2449 ures_loc_resetLocales
2453 U_CAPI UEnumeration
* U_EXPORT2
2454 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2456 UResourceBundle
*idx
= NULL
;
2457 UEnumeration
*en
= NULL
;
2458 ULocalesContext
*myContext
= NULL
;
2460 if(U_FAILURE(*status
)) {
2463 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2464 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2465 if(!en
|| !myContext
) {
2466 *status
= U_MEMORY_ALLOCATION_ERROR
;
2468 uprv_free(myContext
);
2471 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2473 ures_initStackObject(&myContext
->installed
);
2474 ures_initStackObject(&myContext
->curr
);
2475 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2476 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2477 if(U_SUCCESS(*status
)) {
2478 #if defined(URES_TREE_DEBUG)
2479 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2480 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2482 en
->context
= myContext
;
2484 #if defined(URES_TREE_DEBUG)
2485 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2487 ures_close(&myContext
->installed
);
2488 uprv_free(myContext
);
2498 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2500 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2501 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2508 U_CAPI
int32_t U_EXPORT2
2509 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2510 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2511 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2513 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2514 char defVal
[1024] = ""; /* default value for given locale */
2515 char defLoc
[1024] = ""; /* default value for given locale */
2516 char base
[1024] = ""; /* base locale */
2519 char full
[1024] = "";
2520 UResourceBundle bund1
, bund2
;
2521 UResourceBundle
*res
= NULL
;
2522 UErrorCode subStatus
= U_ZERO_ERROR
;
2524 if(U_FAILURE(*status
)) return 0;
2525 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2526 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2529 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2530 #if defined(URES_TREE_DEBUG)
2531 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2532 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2534 ures_initStackObject(&bund1
);
2535 ures_initStackObject(&bund2
);
2538 uprv_strcpy(parent
, base
);
2539 uprv_strcpy(found
, base
);
2542 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2543 *isAvailable
= TRUE
;
2544 if (U_SUCCESS(subStatus
)) {
2545 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2547 uenum_close(locEnum
);
2550 if(U_FAILURE(subStatus
)) {
2551 *status
= subStatus
;
2556 subStatus
= U_ZERO_ERROR
;
2557 res
= ures_open(path
, parent
, &subStatus
);
2558 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2559 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2561 *isAvailable
= FALSE
;
2563 isAvailable
= NULL
; /* only want to set this the first time around */
2565 #if defined(URES_TREE_DEBUG)
2566 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2568 if(U_FAILURE(subStatus
)) {
2569 *status
= subStatus
;
2570 } else if(subStatus
== U_ZERO_ERROR
) {
2571 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2572 if(subStatus
== U_ZERO_ERROR
) {
2573 const UChar
*defUstr
;
2575 /* look for default item */
2576 #if defined(URES_TREE_DEBUG)
2577 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2578 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2580 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2581 if(U_SUCCESS(subStatus
) && defLen
) {
2582 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2583 #if defined(URES_TREE_DEBUG)
2584 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2585 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2587 uprv_strcpy(defLoc
, parent
);
2589 uprv_strcpy(kwVal
, defVal
);
2590 #if defined(URES_TREE_DEBUG)
2591 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2592 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2599 subStatus
= U_ZERO_ERROR
;
2602 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2605 if (found
!= NULL
&& uprv_strcmp(found
, parent
) != 0) {
2606 uprv_strcpy(parent
, found
);
2609 uloc_getParent(found
, parent
, sizeof(parent
), &subStatus
);
2613 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2615 /* Now, see if we can find the kwVal collator.. start the search over.. */
2616 uprv_strcpy(parent
, base
);
2617 uprv_strcpy(found
, base
);
2620 subStatus
= U_ZERO_ERROR
;
2621 res
= ures_open(path
, parent
, &subStatus
);
2622 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2623 *isAvailable
= FALSE
;
2625 isAvailable
= NULL
; /* only want to set this the first time around */
2627 #if defined(URES_TREE_DEBUG)
2628 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2629 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2631 if(U_FAILURE(subStatus
)) {
2632 *status
= subStatus
;
2633 } else if(subStatus
== U_ZERO_ERROR
) {
2634 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2635 #if defined(URES_TREE_DEBUG)
2636 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2638 if(subStatus
== U_ZERO_ERROR
) {
2639 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2640 #if defined(URES_TREE_DEBUG)
2641 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2643 if(subStatus
== U_ZERO_ERROR
) {
2644 #if defined(URES_TREE_DEBUG)
2645 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2646 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2648 uprv_strcpy(full
, parent
);
2650 uprv_strcpy(full
, "root");
2652 /* now, recalculate default kw if need be */
2653 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2654 const UChar
*defUstr
;
2656 /* look for default item */
2657 #if defined(URES_TREE_DEBUG)
2658 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2659 path
?path
:"ICUDATA", full
);
2661 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2662 if(U_SUCCESS(subStatus
) && defLen
) {
2663 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2664 #if defined(URES_TREE_DEBUG)
2665 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2666 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2668 uprv_strcpy(defLoc
, full
);
2670 } /* end of recalculate default KW */
2671 #if defined(URES_TREE_DEBUG)
2673 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2677 #if defined(URES_TREE_DEBUG)
2678 fprintf(stderr
, "err=%s in %s looking for %s\n",
2679 u_errorName(subStatus
), parent
, kwVal
);
2685 subStatus
= U_ZERO_ERROR
;
2687 UBool haveFound
= FALSE
;
2688 // At least for collations which may be aliased, we need to use the VALID locale
2689 // as the parent instead of just truncating, as long as the VALID locale is not
2690 // root and has a different language than the parent. Use of the VALID locale
2691 // here is similar to the procedure used at the end of the previous do-while loop
2692 // for all resource types. This is for <rdar://problem/31138554>.
2693 // It may be appropriate for all resources here too, filing an ICU ticket.
2694 if (res
!= NULL
&& uprv_strcmp(resName
, "collations") == 0) {
2695 const char *validLoc
= ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
);
2696 if (U_SUCCESS(subStatus
) && validLoc
!= NULL
&& validLoc
[0] != 0 && uprv_strcmp(validLoc
, "root") != 0) {
2697 char validLang
[ULOC_LANG_CAPACITY
];
2698 char parentLang
[ULOC_LANG_CAPACITY
];
2699 uloc_getLanguage(validLoc
, validLang
, ULOC_LANG_CAPACITY
, &subStatus
);
2700 uloc_getLanguage(parent
, parentLang
, ULOC_LANG_CAPACITY
, &subStatus
);
2701 if (U_SUCCESS(subStatus
) && uprv_strcmp(validLang
, parentLang
) != 0) {
2702 // validLoc is not root and has a different language than parent, use it instead
2703 uprv_strcpy(found
, validLoc
);
2707 subStatus
= U_ZERO_ERROR
;
2710 uprv_strcpy(found
, parent
);
2713 uloc_getParent(found
,parent
,1023,&subStatus
);
2715 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2717 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2718 #if defined(URES_TREE_DEBUG)
2719 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2721 uprv_strcpy(kwVal
, defVal
);
2722 uprv_strcpy(parent
, base
);
2723 uprv_strcpy(found
, base
);
2725 do { /* search for 'default' named item */
2726 subStatus
= U_ZERO_ERROR
;
2727 res
= ures_open(path
, parent
, &subStatus
);
2728 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2729 *isAvailable
= FALSE
;
2731 isAvailable
= NULL
; /* only want to set this the first time around */
2733 #if defined(URES_TREE_DEBUG)
2734 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2735 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2737 if(U_FAILURE(subStatus
)) {
2738 *status
= subStatus
;
2739 } else if(subStatus
== U_ZERO_ERROR
) {
2740 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2741 if(subStatus
== U_ZERO_ERROR
) {
2742 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2743 if(subStatus
== U_ZERO_ERROR
) {
2744 #if defined(URES_TREE_DEBUG)
2745 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2746 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2748 uprv_strcpy(full
, parent
);
2750 uprv_strcpy(full
, "root");
2753 /* now, recalculate default kw if need be */
2754 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2755 const UChar
*defUstr
;
2757 /* look for default item */
2758 #if defined(URES_TREE_DEBUG)
2759 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2760 path
?path
:"ICUDATA", full
);
2762 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2763 if(U_SUCCESS(subStatus
) && defLen
) {
2764 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2765 #if defined(URES_TREE_DEBUG)
2766 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2767 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2769 uprv_strcpy(defLoc
, full
);
2771 } /* end of recalculate default KW */
2772 #if defined(URES_TREE_DEBUG)
2774 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2780 subStatus
= U_ZERO_ERROR
;
2782 uprv_strcpy(found
, parent
);
2783 uloc_getParent(found
,parent
,1023,&subStatus
);
2785 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2788 if(U_SUCCESS(*status
)) {
2790 #if defined(URES_TREE_DEBUG)
2791 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2793 *status
= U_MISSING_RESOURCE_ERROR
;
2794 } else if(omitDefault
) {
2795 #if defined(URES_TREE_DEBUG)
2796 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2798 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2799 /* found the keyword in a *child* of where the default tag was present. */
2800 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2801 /* and the default is in or in an ancestor of the current locale */
2802 #if defined(URES_TREE_DEBUG)
2803 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2809 uprv_strcpy(found
, full
);
2811 uprv_strcat(found
, "@");
2812 uprv_strcat(found
, keyword
);
2813 uprv_strcat(found
, "=");
2814 uprv_strcat(found
, kwVal
);
2815 } else if(!omitDefault
) {
2816 uprv_strcat(found
, "@");
2817 uprv_strcat(found
, keyword
);
2818 uprv_strcat(found
, "=");
2819 uprv_strcat(found
, defVal
);
2822 /* we found the default locale - no need to repeat it.*/
2827 length
= (int32_t)uprv_strlen(found
);
2829 if(U_SUCCESS(*status
)) {
2830 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2832 uprv_strncpy(result
, found
, copyLength
);
2835 *status
= U_MISSING_RESOURCE_ERROR
;
2841 return u_terminateChars(result
, resultCapacity
, length
, status
);
2844 U_CAPI UEnumeration
* U_EXPORT2
2845 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2847 #define VALUES_BUF_SIZE 2048
2848 #define VALUES_LIST_SIZE 512
2850 char valuesBuf
[VALUES_BUF_SIZE
];
2851 int32_t valuesIndex
= 0;
2852 const char *valuesList
[VALUES_LIST_SIZE
];
2853 int32_t valuesCount
= 0;
2858 UEnumeration
*locs
= NULL
;
2860 UResourceBundle item
;
2861 UResourceBundle subItem
;
2863 ures_initStackObject(&item
);
2864 ures_initStackObject(&subItem
);
2865 locs
= ures_openAvailableLocales(path
, status
);
2867 if(U_FAILURE(*status
)) {
2869 ures_close(&subItem
);
2876 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2877 UResourceBundle
*bund
= NULL
;
2878 UResourceBundle
*subPtr
= NULL
;
2879 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2880 bund
= ures_openDirect(path
, locale
, &subStatus
);
2882 #if defined(URES_TREE_DEBUG)
2883 if(!bund
|| U_FAILURE(subStatus
)) {
2884 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2885 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2889 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2891 if(!bund
|| U_FAILURE(subStatus
)) {
2892 #if defined(URES_TREE_DEBUG)
2893 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2894 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2901 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2902 && U_SUCCESS(subStatus
)) {
2905 k
= ures_getKey(subPtr
);
2907 #if defined(URES_TREE_DEBUG)
2908 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2910 if(k
== NULL
|| *k
== 0 ||
2911 uprv_strcmp(k
, DEFAULT_TAG
) == 0 || uprv_strncmp(k
, "private-", 8) == 0) {
2912 // empty or "default" or unlisted type
2915 for(i
=0; i
<valuesCount
; i
++) {
2916 if(!uprv_strcmp(valuesList
[i
],k
)) {
2917 k
= NULL
; /* found duplicate */
2922 int32_t kLen
= (int32_t)uprv_strlen(k
);
2923 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2924 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2925 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2927 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2928 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2929 valuesIndex
+= kLen
;
2930 #if defined(URES_TREE_DEBUG)
2931 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2932 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2934 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2940 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2943 ures_close(&subItem
);
2945 #if defined(URES_TREE_DEBUG)
2946 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2947 valuesIndex
, valuesCount
);
2949 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2952 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2953 U_INTERNAL UBool U_EXPORT2
2954 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2955 if(res1
==NULL
|| res2
==NULL
){
2956 return res1
==res2
; /* pointer comparision */
2958 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2959 return (res1
->fKey
==res2
->fKey
);
2961 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2965 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2968 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2969 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2971 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2975 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2978 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2981 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2984 if(res1
->fRes
!= res2
->fRes
){
2989 U_INTERNAL UResourceBundle
* U_EXPORT2
2990 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2991 UResourceBundle
* bundle
= NULL
;
2992 UResourceBundle
* ret
= NULL
;
2993 if(U_FAILURE(*status
) || res
== NULL
){
2996 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2997 if(res
->fResPath
!=NULL
){
2998 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
3005 U_INTERNAL
const UResourceBundle
* U_EXPORT2
3006 ures_getParentBundle(const UResourceBundle
* res
){
3010 return res
->fParentRes
;
3014 U_INTERNAL
void U_EXPORT2
3015 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
3018 str
= ures_getStringByKey(res
, key
, &len
, status
);
3019 if(U_SUCCESS(*status
)) {
3020 u_versionFromUString(ver
, str
);