2 ******************************************************************************
3 * Copyright (C) 1997-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 04/01/97 aliu Creation.
13 * 06/14/99 stephen Removed functions taking a filename suffix.
14 * 07/20/99 stephen Changed for UResourceBundle typedef'd to void*
15 * 11/09/99 weiv Added ures_getLocale()
16 * March 2000 weiv Total overhaul - using data in DLLs
17 * 06/20/2000 helena OS/400 port changes; mostly typecast.
18 * 06/24/02 weiv Added support for resource sharing
19 ******************************************************************************
22 #include "unicode/ustring.h"
23 #include "unicode/ucnv.h"
32 #include "unicode/uenum.h"
42 Static cache for already opened resource bundles - mostly for keeping fallback info
43 TODO: This cache should probably be removed when the deprecated code is
46 static UHashtable
*cache
= NULL
;
47 static icu::UInitOnce gCacheInitOnce
;
49 static UMutex resbMutex
= U_MUTEX_INITIALIZER
;
51 /* INTERNAL: hashes an entry */
52 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
53 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
54 UHashTok namekey
, pathkey
;
55 namekey
.pointer
= b
->fName
;
56 pathkey
.pointer
= b
->fPath
;
57 return uhash_hashChars(namekey
)+37u*uhash_hashChars(pathkey
);
60 /* INTERNAL: compares two entries */
61 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
62 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
63 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
64 UHashTok name1
, name2
, path1
, path2
;
65 name1
.pointer
= b1
->fName
;
66 name2
.pointer
= b2
->fName
;
67 path1
.pointer
= b1
->fPath
;
68 path2
.pointer
= b2
->fPath
;
69 return (UBool
)(uhash_compareChars(name1
, name2
) &&
70 uhash_compareChars(path1
, path2
));
75 * Internal function, gets parts of locale name according
76 * to the position of '_' character
78 static UBool
chopLocale(char *name
) {
79 char *i
= uprv_strrchr(name
, '_');
92 static void entryIncrease(UResourceDataEntry
*entry
) {
93 umtx_lock(&resbMutex
);
94 entry
->fCountExisting
++;
95 while(entry
->fParent
!= NULL
) {
96 entry
= entry
->fParent
;
97 entry
->fCountExisting
++;
99 umtx_unlock(&resbMutex
);
103 * Internal function. Tries to find a resource in given Resource
104 * Bundle, as well as in its parents
106 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
107 UResourceDataEntry
*resB
= resBundle
->fData
;
112 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
113 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
116 if(resBundle
->fHasFallback
== TRUE
) {
117 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
118 resB
= resB
->fParent
;
119 if(resB
->fBogus
== U_ZERO_ERROR
) {
121 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
126 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
128 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
129 *status
= U_USING_DEFAULT_WARNING
;
131 *status
= U_USING_FALLBACK_WARNING
;
135 return (&(resB
->fData
));
136 } else { /* If resource is not found, we need to give an error */
137 *status
= U_MISSING_RESOURCE_ERROR
;
141 *status
= U_MISSING_RESOURCE_ERROR
;
147 free_entry(UResourceDataEntry
*entry
) {
148 UResourceDataEntry
*alias
;
149 res_unload(&(entry
->fData
));
150 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
151 uprv_free(entry
->fName
);
153 if(entry
->fPath
!= NULL
) {
154 uprv_free(entry
->fPath
);
156 if(entry
->fPool
!= NULL
) {
157 --entry
->fPool
->fCountExisting
;
159 alias
= entry
->fAlias
;
161 while(alias
->fAlias
!= NULL
) {
162 alias
= alias
->fAlias
;
164 --alias
->fCountExisting
;
169 /* Works just like ucnv_flushCache() */
170 static int32_t ures_flushCache()
172 UResourceDataEntry
*resB
;
174 int32_t rbDeletedNum
= 0;
175 const UHashElement
*e
;
178 /*if shared data hasn't even been lazy evaluated yet
181 umtx_lock(&resbMutex
);
183 umtx_unlock(&resbMutex
);
189 /*creates an enumeration to iterate through every element in the table */
191 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
193 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
194 /* Deletes only if reference counter == 0
195 * Don't worry about the children of this node.
196 * Those will eventually get deleted too, if not already.
197 * Don't worry about the parents of this node.
198 * Those will eventually get deleted too, if not already.
200 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
201 /* some resource bundles are still open somewhere. */
203 if (resB
->fCountExisting
== 0) {
206 uhash_removeElement(cache
, e
);
211 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
212 * got decremented by free_entry().
214 } while(deletedMore
);
215 umtx_unlock(&resbMutex
);
223 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
224 UBool cacheNotEmpty
= FALSE
;
225 int32_t pos
= UHASH_FIRST
;
226 const UHashElement
*e
;
227 UResourceDataEntry
*resB
;
229 umtx_lock(&resbMutex
);
231 umtx_unlock(&resbMutex
);
232 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
236 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
238 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
239 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
241 (void*)resB
, resB
->fCountExisting
,
242 resB
->fName
?resB
->fName
:"NULL",
243 resB
->fPath
?resB
->fPath
:"NULL",
246 (void*)resB
->fParent
);
249 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
251 umtx_unlock(&resbMutex
);
253 return cacheNotEmpty
;
258 static UBool U_CALLCONV
ures_cleanup(void)
265 gCacheInitOnce
.reset();
269 /** INTERNAL: Initializes the cache for resources */
270 static void createCache(UErrorCode
&status
) {
271 U_ASSERT(cache
== NULL
);
272 cache
= uhash_open(hashEntry
, compareEntries
, NULL
, &status
);
273 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
276 static void initCache(UErrorCode
*status
) {
277 umtx_initOnce(gCacheInitOnce
, &createCache
, *status
);
280 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
282 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
283 int32_t len
= (int32_t)uprv_strlen(name
);
284 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
285 uprv_free(res
->fName
);
287 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
288 res
->fName
= res
->fNameBuffer
;
291 res
->fName
= (char *)uprv_malloc(len
+1);
293 if(res
->fName
== NULL
) {
294 *status
= U_MEMORY_ALLOCATION_ERROR
;
296 uprv_strcpy(res
->fName
, name
);
300 static UResourceDataEntry
*
301 getPoolEntry(const char *path
, UErrorCode
*status
);
304 * INTERNAL: Inits and opens an entry from a data DLL.
305 * CAUTION: resbMutex must be locked when calling this function.
307 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
308 UResourceDataEntry
*r
= NULL
;
309 UResourceDataEntry find
;
310 /*int32_t hashValue;*/
312 char aliasName
[100] = { 0 };
313 int32_t aliasLen
= 0;
314 /*UBool isAlias = FALSE;*/
315 /*UHashTok hashkey; */
317 if(U_FAILURE(*status
)) {
321 /* here we try to deduce the right locale name */
322 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
323 name
= uloc_getDefault();
324 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
325 name
= kRootLocaleName
;
326 } else { /* otherwise, we'll open what we're given */
330 find
.fName
= (char *)name
;
331 find
.fPath
= (char *)path
;
333 /* calculate the hash value of the entry */
334 /*hashkey.pointer = (void *)&find;*/
335 /*hashValue = hashEntry(hashkey);*/
337 /* check to see if we already have this entry */
338 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
340 /* if the entry is not yet in the hash table, we'll try to construct a new one */
341 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
343 *status
= U_MEMORY_ALLOCATION_ERROR
;
347 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
348 /*r->fHashKey = hashValue;*/
350 setEntryName(r
, name
, status
);
351 if (U_FAILURE(*status
)) {
357 r
->fPath
= (char *)uprv_strdup(path
);
358 if(r
->fPath
== NULL
) {
359 *status
= U_MEMORY_ALLOCATION_ERROR
;
365 /* this is the actual loading */
366 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
368 if (U_FAILURE(*status
)) {
369 /* we have no such entry in dll, so it will always use fallback */
370 *status
= U_USING_FALLBACK_WARNING
;
371 r
->fBogus
= U_USING_FALLBACK_WARNING
;
372 } else { /* if we have a regular entry */
374 if (r
->fData
.usesPoolBundle
) {
375 r
->fPool
= getPoolEntry(r
->fPath
, status
);
376 if (U_SUCCESS(*status
)) {
377 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
378 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
379 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
381 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
387 if (U_SUCCESS(*status
)) {
388 /* handle the alias by trying to get out the %%Alias tag.*/
389 /* We'll try to get alias string from the bundle */
390 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
391 if (aliasres
!= RES_BOGUS
) {
392 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
393 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
394 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
395 r
->fAlias
= init_entry(aliasName
, path
, status
);
402 UResourceDataEntry
*oldR
= NULL
;
403 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
404 /* just insert it in the cache */
405 UErrorCode cacheStatus
= U_ZERO_ERROR
;
406 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
407 if (U_FAILURE(cacheStatus
)) {
408 *status
= cacheStatus
;
413 /* somebody have already inserted it while we were working, discard newly opened data */
414 /* Also, we could get here IF we opened an alias */
422 /* return the real bundle */
423 while(r
->fAlias
!= NULL
) {
426 r
->fCountExisting
++; /* we increase its reference count */
427 /* if the resource has a warning */
428 /* we don't want to overwrite a status with no error */
429 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
430 *status
= r
->fBogus
; /* set the returning status */
436 static UResourceDataEntry
*
437 getPoolEntry(const char *path
, UErrorCode
*status
) {
438 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
439 if( U_SUCCESS(*status
) &&
440 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
442 *status
= U_INVALID_FORMAT_ERROR
;
448 /* CAUTION: resbMutex must be locked when calling this function! */
449 static UResourceDataEntry
*
450 findFirstExisting(const char* path
, char* name
,
451 UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
452 UResourceDataEntry
*r
= NULL
;
453 UBool hasRealData
= FALSE
;
454 const char *defaultLoc
= uloc_getDefault();
455 *hasChopped
= TRUE
; /* we're starting with a fresh name */
457 while(*hasChopped
&& !hasRealData
) {
458 r
= init_entry(name
, path
, status
);
459 /* Null pointer test */
460 if (U_FAILURE(*status
)) {
463 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
464 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
466 /* this entry is not real. We will discard it. */
467 /* However, the parent line for this entry is */
468 /* not to be used - as there might be parent */
469 /* lines in cache from previous openings that */
470 /* are not updated yet. */
472 /*entryCloseInt(r);*/
474 *status
= U_USING_FALLBACK_WARNING
;
476 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
479 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
481 /*Fallback data stuff*/
482 *hasChopped
= chopLocale(name
);
487 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
492 resB
->fMagic1
= MAGIC1
;
493 resB
->fMagic2
= MAGIC2
;
497 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
498 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
502 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
503 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
504 ures_setIsStackObject(resB
, TRUE
);
507 static UBool
// returns U_SUCCESS(*status)
508 loadParentsExceptRoot(UResourceDataEntry
*&t1
,
509 char name
[], int32_t nameCapacity
,
510 UBool usingUSRData
, char usrDataPath
[], UErrorCode
*status
) {
511 if (U_FAILURE(*status
)) { return FALSE
; }
512 UBool hasChopped
= TRUE
;
513 while (hasChopped
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
&&
514 res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
515 Resource parentRes
= res_getResource(&t1
->fData
, "%%Parent");
516 if (parentRes
!= RES_BOGUS
) { // An explicit parent was found.
517 int32_t parentLocaleLen
= 0;
518 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), parentRes
, &parentLocaleLen
);
519 if(parentLocaleName
!= NULL
&& 0 < parentLocaleLen
&& parentLocaleLen
< nameCapacity
) {
520 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+ 1);
521 if (uprv_strcmp(name
, kRootLocaleName
) == 0) {
526 // Insert regular parents.
527 UErrorCode parentStatus
= U_ZERO_ERROR
;
528 UResourceDataEntry
*t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
529 if (U_FAILURE(parentStatus
)) {
530 *status
= parentStatus
;
533 UResourceDataEntry
*u2
= NULL
;
534 UErrorCode usrStatus
= U_ZERO_ERROR
;
535 if (usingUSRData
) { // This code inserts user override data into the inheritance chain.
536 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
539 if (usingUSRData
&& U_SUCCESS(usrStatus
) && u2
->fBogus
== U_ZERO_ERROR
) {
545 // The USR override data wasn't found, set it to be deleted.
546 u2
->fCountExisting
= 0;
550 hasChopped
= chopLocale(name
);
555 static UBool
// returns U_SUCCESS(*status)
556 insertRootBundle(UResourceDataEntry
*&t1
, UErrorCode
*status
) {
557 if (U_FAILURE(*status
)) { return FALSE
; }
558 UErrorCode parentStatus
= U_ZERO_ERROR
;
559 UResourceDataEntry
*t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
560 if (U_FAILURE(parentStatus
)) {
561 *status
= parentStatus
;
571 * Open a resource bundle for the locale;
572 * if there is not even a base language bundle, then fall back to the default locale;
573 * if there is no bundle for that either, then load the root bundle.
575 * This is the default bundle loading behavior.
577 URES_OPEN_LOCALE_DEFAULT_ROOT
,
578 // TODO: ICU ticket #11271 "consistent default locale across locale trees"
579 // Add an option to look at the main locale tree for whether to
580 // fall back to root directly (if the locale has main data) or
581 // fall back to the default locale first (if the locale does not even have main data).
583 * Open a resource bundle for the locale;
584 * if there is not even a base language bundle, then load the root bundle;
585 * never fall back to the default locale.
587 * This is used for algorithms that have good pan-Unicode default behavior,
588 * such as case mappings, collation, and segmentation (BreakIterator).
590 URES_OPEN_LOCALE_ROOT
,
592 * Open a resource bundle for the exact bundle name as requested;
593 * no fallbacks, do not load parent bundles.
595 * This is used for supplemental (non-locale) data.
599 typedef enum UResOpenType UResOpenType
;
601 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
,
602 UResOpenType openType
, UErrorCode
* status
) {
603 U_ASSERT(openType
!= URES_OPEN_DIRECT
);
604 UErrorCode intStatus
= U_ZERO_ERROR
;
605 UResourceDataEntry
*r
= NULL
;
606 UResourceDataEntry
*t1
= NULL
;
607 UBool isDefault
= FALSE
;
608 UBool isRoot
= FALSE
;
609 UBool hasRealData
= FALSE
;
610 UBool hasChopped
= TRUE
;
611 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
613 char name
[ULOC_FULLNAME_CAPACITY
];
614 char usrDataPath
[96];
618 if(U_FAILURE(*status
)) {
622 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
623 name
[sizeof(name
) - 1] = 0;
625 if ( usingUSRData
) {
626 if ( path
== NULL
) {
627 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
629 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
630 usrDataPath
[0] = 'u';
631 usrDataPath
[1] = 's';
632 usrDataPath
[2] = 'r';
633 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
637 umtx_lock(&resbMutex
);
639 /* We're going to skip all the locales that do not have any data */
640 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
642 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
645 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
646 UErrorCode usrStatus
= U_ZERO_ERROR
;
647 UResourceDataEntry
*u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
649 if(u1
->fBogus
== U_ZERO_ERROR
) {
653 /* the USR override data wasn't found, set it to be deleted */
654 u1
->fCountExisting
= 0;
658 if (hasChopped
&& !isRoot
) {
659 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
665 /* we could have reached this point without having any real data */
666 /* if that is the case, we need to chain in the default locale */
667 if(r
==NULL
&& openType
== URES_OPEN_LOCALE_DEFAULT_ROOT
&& !isDefault
&& !isRoot
) {
668 /* insert default locale */
669 uprv_strcpy(name
, uloc_getDefault());
670 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
671 intStatus
= U_USING_DEFAULT_WARNING
;
672 if(r
!= NULL
) { /* the default locale exists */
676 // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
677 if (hasChopped
&& !isRoot
) {
678 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
685 /* we could still have r == NULL at this point - maybe even default locale is not */
688 uprv_strcpy(name
, kRootLocaleName
);
689 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
692 intStatus
= U_USING_DEFAULT_WARNING
;
694 } else { /* we don't even have the root locale */
695 *status
= U_MISSING_RESOURCE_ERROR
;
698 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 &&
699 t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
700 if (!insertRootBundle(t1
, status
)) {
704 r
->fBogus
= U_USING_DEFAULT_WARNING
;
708 // TODO: Does this ever loop?
709 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
710 t1
->fParent
->fCountExisting
++;
715 umtx_unlock(&resbMutex
);
717 if(U_SUCCESS(*status
)) {
718 if(intStatus
!= U_ZERO_ERROR
) {
728 * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
730 * Parent and root locale bundles are loaded if
731 * the requested bundle does not have the "nofallback" flag.
733 static UResourceDataEntry
*
734 entryOpenDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
736 if(U_FAILURE(*status
)) {
740 umtx_lock(&resbMutex
);
741 // findFirstExisting() without fallbacks.
742 UResourceDataEntry
*r
= init_entry(localeID
, path
, status
);
743 if(U_SUCCESS(*status
)) {
744 if(r
->fBogus
!= U_ZERO_ERROR
) {
752 // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
753 // unless it is marked with "nofallback".
754 UResourceDataEntry
*t1
= r
;
755 if(r
!= NULL
&& uprv_strcmp(localeID
, kRootLocaleName
) != 0 && // not root
756 r
->fParent
== NULL
&& !r
->fData
.noFallback
&&
757 uprv_strlen(localeID
) < ULOC_FULLNAME_CAPACITY
) {
758 char name
[ULOC_FULLNAME_CAPACITY
];
759 uprv_strcpy(name
, localeID
);
760 if(!chopLocale(name
) || uprv_strcmp(name
, kRootLocaleName
) == 0 ||
761 loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), FALSE
, NULL
, status
)) {
762 if(uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
) {
763 insertRootBundle(t1
, status
);
766 if(U_FAILURE(*status
)) {
772 // TODO: Does this ever loop?
773 while(t1
->fParent
!= NULL
) {
774 t1
->fParent
->fCountExisting
++;
778 umtx_unlock(&resbMutex
);
783 * Functions to create and destroy resource bundles.
784 * CAUTION: resbMutex must be locked when calling this function.
787 static void entryCloseInt(UResourceDataEntry
*resB
) {
788 UResourceDataEntry
*p
= resB
;
790 while(resB
!= NULL
) {
792 resB
->fCountExisting
--;
794 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
797 if(resB->fCountExisting <= 0) {
798 uhash_remove(cache, resB);
799 if(resB->fBogus == U_ZERO_ERROR) {
800 res_unload(&(resB->fData));
802 if(resB->fName != NULL) {
803 uprv_free(resB->fName);
805 if(resB->fPath != NULL) {
806 uprv_free(resB->fPath);
817 * API: closes a resource bundle and cleans up.
820 static void entryClose(UResourceDataEntry
*resB
) {
821 umtx_lock(&resbMutex
);
823 umtx_unlock(&resbMutex
);
827 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
828 if(resB->fResPath == NULL) {
829 resB->fResPath = resB->fResBuf;
830 *(resB->fResPath) = 0;
832 resB->fResPathLen = uprv_strlen(toAdd);
833 if(RES_BUFSIZE <= resB->fResPathLen+1) {
834 if(resB->fResPath == resB->fResBuf) {
835 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
837 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
840 uprv_strcpy(resB->fResPath, toAdd);
843 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
844 int32_t resPathLenOrig
= resB
->fResPathLen
;
845 if(resB
->fResPath
== NULL
) {
846 resB
->fResPath
= resB
->fResBuf
;
847 *(resB
->fResPath
) = 0;
848 resB
->fResPathLen
= 0;
850 resB
->fResPathLen
+= lenToAdd
;
851 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
852 if(resB
->fResPath
== resB
->fResBuf
) {
853 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
854 /* Check that memory was allocated correctly. */
855 if (resB
->fResPath
== NULL
) {
856 *status
= U_MEMORY_ALLOCATION_ERROR
;
859 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
861 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
862 /* Check that memory was reallocated correctly. */
864 *status
= U_MEMORY_ALLOCATION_ERROR
;
867 resB
->fResPath
= temp
;
870 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
873 static void ures_freeResPath(UResourceBundle
*resB
) {
874 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
875 uprv_free(resB
->fResPath
);
877 resB
->fResPath
= NULL
;
878 resB
->fResPathLen
= 0;
882 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
885 if(resB
->fData
!= NULL
) {
886 entryClose(resB
->fData
);
888 if(resB
->fVersion
!= NULL
) {
889 uprv_free(resB
->fVersion
);
891 ures_freeResPath(resB
);
893 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
898 /* poison the data */
899 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
905 U_CAPI
void U_EXPORT2
906 ures_close(UResourceBundle
* resB
)
908 ures_closeBundle(resB
, TRUE
);
911 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
912 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
913 const UResourceBundle
*parent
, int32_t noAlias
,
914 UResourceBundle
*resB
, UErrorCode
*status
)
916 if(status
== NULL
|| U_FAILURE(*status
)) {
919 if (parent
== NULL
) {
920 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
923 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
924 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
926 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
928 /* we have an alias, now let's cut it up */
929 char stackAlias
[200];
930 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
934 * Allocate enough space for both the char * version
935 * of the alias and parent->fResPath.
937 * We do this so that res_findResource() can modify the path,
938 * which allows us to remove redundant _res_findResource() variants
940 * res_findResource() now NUL-terminates each segment so that table keys
941 * can always be compared with strcmp() instead of strncmp().
942 * Saves code there and simplifies testing and code coverage.
946 ++len
; /* count the terminating NUL */
947 if(parent
->fResPath
!= NULL
) {
948 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
955 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
956 capacity
= (int32_t)sizeof(stackAlias
);
957 chAlias
= stackAlias
;
959 chAlias
= (char *)uprv_malloc(capacity
);
961 if(chAlias
== NULL
) {
962 *status
= U_MEMORY_ALLOCATION_ERROR
;
966 u_UCharsToChars(alias
, chAlias
, len
);
968 if(*chAlias
== RES_PATH_SEPARATOR
) {
969 /* there is a path included */
970 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
972 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
978 if(uprv_strcmp(path
, "LOCALE") == 0) {
979 /* this is an XPath alias, starting with "/LOCALE/" */
980 /* it contains the path to a resource which should be looked up */
981 /* starting in the requested locale */
983 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
984 path
= realData
->fPath
; /* we will be looking in the same package */
986 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
989 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
996 /* no path, start with a locale */
998 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1003 path
= realData
->fPath
;
1008 /* got almost everything, let's try to open */
1009 /* first, open the bundle with real data */
1010 UResourceBundle
*result
= resB
;
1011 const char* temp
= NULL
;
1012 UErrorCode intStatus
= U_ZERO_ERROR
;
1013 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
1014 if(U_SUCCESS(intStatus
)) {
1015 if(keyPath
== NULL
) {
1016 /* no key path. This means that we are going to
1017 * to use the corresponding resource from
1020 /* first, we are going to get a corresponding parent
1021 * resource to the one we are searching.
1023 char *aKey
= parent
->fResPath
;
1025 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
1027 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
1032 /* we need to make keyPath from parent's fResPath and
1033 * current key, if there is a key associated
1035 len
= (int32_t)(uprv_strlen(key
) + 1);
1036 if(len
> capacity
) {
1038 if(chAlias
== stackAlias
) {
1039 chAlias
= (char *)uprv_malloc(capacity
);
1041 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
1043 if(chAlias
== NULL
) {
1044 ures_close(mainRes
);
1045 *status
= U_MEMORY_ALLOCATION_ERROR
;
1049 uprv_memcpy(chAlias
, key
, len
);
1051 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
1052 } else if(idx
!= -1) {
1053 /* if there is no key, but there is an index, try to get by the index */
1054 /* here we have either a table or an array, so get the element */
1055 int32_t type
= RES_GET_TYPE(r
);
1056 if(URES_IS_TABLE(type
)) {
1057 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
1058 } else { /* array */
1059 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
1062 if(r
!= RES_BOGUS
) {
1063 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
1065 *status
= U_MISSING_RESOURCE_ERROR
;
1069 /* this one is a bit trickier.
1070 * we start finding keys, but after we resolve one alias, the path might continue.
1072 * aliastest:alias { "testtypes/anotheralias/Sequence" }
1073 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1074 * aliastest resource should finally have the sequence, not collation elements.
1076 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1077 char stackPath
[URES_MAX_BUFFER_SIZE
];
1078 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1079 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
1080 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1081 if(pathBuf
== NULL
) {
1082 *status
= U_MEMORY_ALLOCATION_ERROR
;
1086 uprv_strcpy(pathBuf
, keyPath
);
1088 /* now we have fallback following here */
1090 r
= dataEntry
->fData
.rootRes
;
1091 /* this loop handles 'found' resources over several levels */
1092 while(*myPath
&& U_SUCCESS(*status
)) {
1093 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1094 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1095 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1098 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1099 dataEntry
= result
->fData
;
1101 } else { /* no resource found, we don't really want to look anymore on this level */
1105 dataEntry
= dataEntry
->fParent
;
1106 uprv_strcpy(pathBuf
, keyPath
);
1108 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1109 if(r
== RES_BOGUS
) {
1110 *status
= U_MISSING_RESOURCE_ERROR
;
1113 if(pathBuf
!= stackPath
) {
1117 } else { /* we failed to open the resource we're aliasing to */
1118 *status
= intStatus
;
1120 if(chAlias
!= stackAlias
) {
1123 if(mainRes
!= result
) {
1124 ures_close(mainRes
);
1129 /* bad alias, should be an error */
1130 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1134 *status
= U_TOO_MANY_ALIASES_ERROR
;
1139 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1142 *status
= U_MEMORY_ALLOCATION_ERROR
;
1145 ures_setIsStackObject(resB
, FALSE
);
1146 resB
->fResPath
= NULL
;
1147 resB
->fResPathLen
= 0;
1149 if(resB
->fData
!= NULL
) {
1150 entryClose(resB
->fData
);
1152 if(resB
->fVersion
!= NULL
) {
1153 uprv_free(resB
->fVersion
);
1156 weiv: if stack object was passed in, it doesn't really need to be reinited,
1157 since the purpose of initing is to remove stack junk. However, at this point
1158 we would not do anything to an allocated object, so stack object should be
1162 if(ures_isStackObject(resB) != FALSE) {
1163 ures_initStackObject(resB);
1166 if(parent
!= resB
) {
1167 ures_freeResPath(resB
);
1170 resB
->fData
= realData
;
1171 entryIncrease(resB
->fData
);
1172 resB
->fHasFallback
= FALSE
;
1173 resB
->fIsTopLevel
= FALSE
;
1176 /*resB->fParentRes = parent;*/
1177 resB
->fTopLevelData
= parent
->fTopLevelData
;
1178 if(parent
->fResPath
&& parent
!= resB
) {
1179 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1182 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1183 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1184 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1186 } else if(idx
>= 0) {
1188 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1189 ures_appendResPath(resB
, buf
, len
, status
);
1190 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1191 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1194 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1196 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1197 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1200 resB
->fVersion
= NULL
;
1202 /*resB->fParent = parent->fRes;*/
1203 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1204 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1208 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1209 UBool isStackObject
;
1210 if(U_FAILURE(*status
) || r
== original
) {
1213 if(original
!= NULL
) {
1215 isStackObject
= FALSE
;
1216 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1219 *status
= U_MEMORY_ALLOCATION_ERROR
;
1223 isStackObject
= ures_isStackObject(r
);
1224 ures_closeBundle(r
, FALSE
);
1226 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1229 if(original
->fResPath
) {
1230 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1232 ures_setIsStackObject(r
, isStackObject
);
1233 if(r
->fData
!= NULL
) {
1234 entryIncrease(r
->fData
);
1241 * Functions to retrieve data from resource bundles.
1244 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1246 if (status
==NULL
|| U_FAILURE(*status
)) {
1250 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1253 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1255 *status
= U_RESOURCE_TYPE_MISMATCH
;
1261 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1262 char *dest
, int32_t *pLength
,
1264 UErrorCode
*status
) {
1267 if (U_FAILURE(*status
)) {
1270 if (pLength
!= NULL
) {
1271 capacity
= *pLength
;
1275 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1276 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1280 if (length16
== 0) {
1281 /* empty string, return as read-only pointer */
1282 if (pLength
!= NULL
) {
1286 u_terminateChars(dest
, capacity
, 0, status
);
1292 /* We need to transform the string to the destination buffer. */
1293 if (capacity
< length16
) {
1294 /* No chance for the string to fit. Pure preflighting. */
1295 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1297 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1299 * We know the string will fit into dest because each UChar turns
1300 * into at most three UTF-8 bytes. Fill the latter part of dest
1301 * so that callers do not expect to use dest as a string pointer,
1302 * hopefully leading to more robust code for when resource bundles
1303 * may store UTF-8 natively.
1304 * (In which case dest would not be used at all.)
1306 * We do not do this if forceCopy=TRUE because then the caller
1307 * expects the string to start exactly at dest.
1309 * The test above for <= 0x2aaaaaaa prevents overflows.
1310 * The +1 is for the NUL terminator.
1312 int32_t maxLength
= 3 * length16
+ 1;
1313 if (capacity
> maxLength
) {
1314 dest
+= capacity
- maxLength
;
1315 capacity
= maxLength
;
1318 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1322 U_CAPI
const char * U_EXPORT2
1323 ures_getUTF8String(const UResourceBundle
*resB
,
1324 char *dest
, int32_t *pLength
,
1326 UErrorCode
*status
) {
1328 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1329 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1332 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1333 UErrorCode
* status
) {
1335 if (status
==NULL
|| U_FAILURE(*status
)) {
1339 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1342 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1344 *status
= U_RESOURCE_TYPE_MISMATCH
;
1349 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1350 UErrorCode
* status
) {
1352 if (status
==NULL
|| U_FAILURE(*status
)) {
1356 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1359 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1361 *status
= U_RESOURCE_TYPE_MISMATCH
;
1366 /* this function returns a signed integer */
1367 /* it performs sign extension */
1368 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1369 if (status
==NULL
|| U_FAILURE(*status
)) {
1373 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1376 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1377 *status
= U_RESOURCE_TYPE_MISMATCH
;
1380 return RES_GET_INT(resB
->fRes
);
1383 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1384 if (status
==NULL
|| U_FAILURE(*status
)) {
1388 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1391 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1392 *status
= U_RESOURCE_TYPE_MISMATCH
;
1395 return RES_GET_UINT(resB
->fRes
);
1398 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1402 return res_getPublicType(resB
->fRes
);
1405 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1413 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1421 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1422 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1423 const UChar
* result
= 0;
1424 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1425 result
= ures_getString(tempRes
, len
, status
);
1426 ures_close(tempRes
);
1429 return res_getString(&(resB
->fResData
), r
, len
);
1433 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1440 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1444 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1447 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1448 Resource r
= RES_BOGUS
;
1450 if (status
==NULL
|| U_FAILURE(*status
)) {
1454 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1458 if(resB
->fIndex
== resB
->fSize
-1) {
1459 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1462 switch(RES_GET_TYPE(resB
->fRes
)) {
1464 case URES_STRING_V2
:
1465 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1469 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1470 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1471 /* TODO: do the fallback */
1473 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1476 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1477 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1478 /* TODO: do the fallback */
1480 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1482 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1485 case URES_INT_VECTOR
:
1486 *status
= U_RESOURCE_TYPE_MISMATCH
;
1487 default: /*fall through*/
1495 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1496 const char *key
= NULL
;
1497 Resource r
= RES_BOGUS
;
1499 if (status
==NULL
|| U_FAILURE(*status
)) {
1504 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1509 if(resB
->fIndex
== resB
->fSize
-1) {
1510 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1514 switch(RES_GET_TYPE(resB
->fRes
)) {
1518 case URES_STRING_V2
:
1519 case URES_INT_VECTOR
:
1520 return ures_copyResb(fillIn
, resB
, status
);
1524 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1525 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1526 /* TODO: do the fallback */
1528 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1531 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1532 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1533 /* TODO: do the fallback */
1535 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1545 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1546 const char* key
= NULL
;
1547 Resource r
= RES_BOGUS
;
1549 if (status
==NULL
|| U_FAILURE(*status
)) {
1554 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1559 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1560 switch(RES_GET_TYPE(resB
->fRes
)) {
1564 case URES_STRING_V2
:
1565 case URES_INT_VECTOR
:
1566 return ures_copyResb(fillIn
, resB
, status
);
1570 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1571 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1572 /* TODO: do the fallback */
1574 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1577 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1578 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1579 /* TODO: do the fallback */
1581 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1587 *status
= U_MISSING_RESOURCE_ERROR
;
1593 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1594 const char* key
= NULL
;
1595 Resource r
= RES_BOGUS
;
1597 if (status
==NULL
|| U_FAILURE(*status
)) {
1601 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1605 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1606 switch(RES_GET_TYPE(resB
->fRes
)) {
1608 case URES_STRING_V2
:
1609 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1613 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1614 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1615 /* TODO: do the fallback */
1617 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1620 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1621 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1622 /* TODO: do the fallback */
1624 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1626 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1629 case URES_INT_VECTOR
:
1630 *status
= U_RESOURCE_TYPE_MISMATCH
;
1633 /* must not occur */
1634 *status
= U_INTERNAL_PROGRAM_ERROR
;
1638 *status
= U_MISSING_RESOURCE_ERROR
;
1643 U_CAPI
const char * U_EXPORT2
1644 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1646 char *dest
, int32_t *pLength
,
1648 UErrorCode
*status
) {
1650 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1651 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1654 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1655 return resB->fResPath;
1658 U_CAPI UResourceBundle
* U_EXPORT2
1659 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1661 UResourceBundle
*first
= NULL
;
1662 UResourceBundle
*result
= fillIn
;
1663 char *packageName
= NULL
;
1664 char *pathToResource
= NULL
, *save
= NULL
;
1665 char *locale
= NULL
, *localeEnd
= NULL
;
1668 if(status
== NULL
|| U_FAILURE(*status
)) {
1672 length
= (int32_t)(uprv_strlen(path
)+1);
1673 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1675 if(pathToResource
== NULL
) {
1676 *status
= U_MEMORY_ALLOCATION_ERROR
;
1679 uprv_memcpy(pathToResource
, path
, length
);
1681 locale
= pathToResource
;
1682 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1684 packageName
= pathToResource
;
1685 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1686 if(pathToResource
== NULL
) {
1687 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1689 *pathToResource
= 0;
1690 locale
= pathToResource
+1;
1694 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1695 if(localeEnd
!= NULL
) {
1699 first
= ures_open(packageName
, locale
, status
);
1701 if(U_SUCCESS(*status
)) {
1703 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1705 result
= ures_copyResb(fillIn
, first
, status
);
1713 U_CAPI UResourceBundle
* U_EXPORT2
1714 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1716 Resource res
= RES_BOGUS
;
1717 UResourceBundle
*result
= fillIn
;
1720 if(status
== NULL
|| U_FAILURE(*status
)) {
1724 /* here we do looping and circular alias checking */
1725 /* this loop is here because aliasing is resolved on this level, not on res level */
1726 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1728 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1729 if(res
!= RES_BOGUS
) {
1730 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1733 *status
= U_MISSING_RESOURCE_ERROR
;
1736 } while(*path
); /* there is more stuff in the path */
1740 U_INTERNAL
const UChar
* U_EXPORT2
1741 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1744 UErrorCode
*status
) {
1746 UResourceBundle stack
;
1747 const UChar
* retVal
= NULL
;
1748 ures_initStackObject(&stack
);
1749 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1751 retVal
= ures_getString(&stack
, &length
, status
);
1753 if (U_FAILURE(*status
)) {
1756 if (length
== 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1759 *status
= U_MISSING_RESOURCE_ERROR
;
1768 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1770 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1771 Resource resource
= table
; /* The current resource */
1772 icu::CharString path
;
1773 UErrorCode errorCode
= U_ZERO_ERROR
;
1774 path
.append(key
, errorCode
);
1775 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1776 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1777 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1778 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1779 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1780 if (nextPathPart
!= NULL
) {
1781 *nextPathPart
= 0; /* Terminating null for this part of path. */
1784 nextPathPart
= uprv_strchr(pathPart
, 0);
1787 const char *pathP
= pathPart
;
1788 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
1789 type
= (UResType
)RES_GET_TYPE(resource
);
1790 pathPart
= nextPathPart
;
1798 U_CAPI UResourceBundle
* U_EXPORT2
1799 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1801 UResourceBundle
*fillIn
,
1802 UErrorCode
*status
) {
1803 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1804 /*UResourceDataEntry *realData = NULL;*/
1805 UResourceBundle
*helper
= NULL
;
1807 if (status
==NULL
|| U_FAILURE(*status
)) {
1811 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1815 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1816 if(URES_IS_TABLE(type
)) {
1817 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
1818 const char* key
= inKey
;
1819 if(res
== RES_BOGUS
) {
1820 UResourceDataEntry
*dataEntry
= resB
->fData
;
1822 char *myPath
= NULL
;
1823 const char* resPath
= resB
->fResPath
;
1824 int32_t len
= resB
->fResPathLen
;
1825 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1826 dataEntry
= dataEntry
->fParent
;
1827 rootRes
= dataEntry
->fData
.rootRes
;
1829 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1832 path
.append(resPath
, len
, *status
);
1834 path
.append(inKey
, *status
);
1835 if (U_FAILURE(*status
)) {
1839 myPath
= path
.data();
1842 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1843 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1844 /* We hit an alias, but we didn't finish following the path. */
1845 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1846 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1848 dataEntry
= helper
->fData
;
1849 rootRes
= helper
->fRes
;
1850 resPath
= helper
->fResPath
;
1851 len
= helper
->fResPathLen
;
1857 } while(*myPath
); /* Continue until the whole path is consumed */
1860 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1861 if(res
!= RES_BOGUS
) {
1862 /* check if resB->fResPath gives the right name here */
1863 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1864 *status
= U_USING_DEFAULT_WARNING
;
1866 *status
= U_USING_FALLBACK_WARNING
;
1869 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1871 *status
= U_MISSING_RESOURCE_ERROR
;
1874 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1878 *status
= U_RESOURCE_TYPE_MISMATCH
;
1885 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1886 Resource res
= RES_BOGUS
;
1887 UResourceDataEntry
*realData
= NULL
;
1888 const char *key
= inKey
;
1890 if (status
==NULL
|| U_FAILURE(*status
)) {
1894 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1898 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1899 if(URES_IS_TABLE(type
)) {
1901 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1902 if(res
== RES_BOGUS
) {
1904 if(resB
->fHasFallback
== TRUE
) {
1905 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1906 if(U_SUCCESS(*status
)) {
1907 /* check if resB->fResPath gives the right name here */
1908 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1910 *status
= U_MISSING_RESOURCE_ERROR
;
1913 *status
= U_MISSING_RESOURCE_ERROR
;
1916 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1920 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1922 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1923 /* here should go a first attempt to locate the key using index table */
1924 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1925 if(U_SUCCESS(*status
)) {
1926 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1928 *status
= U_MISSING_RESOURCE_ERROR
;
1933 *status
= U_RESOURCE_TYPE_MISMATCH
;
1938 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1939 Resource res
= RES_BOGUS
;
1940 UResourceDataEntry
*realData
= NULL
;
1941 const char* key
= inKey
;
1943 if (status
==NULL
|| U_FAILURE(*status
)) {
1947 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1951 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1952 if(URES_IS_TABLE(type
)) {
1955 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1957 if(res
== RES_BOGUS
) {
1959 if(resB
->fHasFallback
== TRUE
) {
1960 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1961 if(U_SUCCESS(*status
)) {
1962 switch (RES_GET_TYPE(res
)) {
1964 case URES_STRING_V2
:
1965 return res_getString(rd
, res
, len
);
1968 const UChar
* result
= 0;
1969 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1970 result
= ures_getString(tempRes
, len
, status
);
1971 ures_close(tempRes
);
1975 *status
= U_RESOURCE_TYPE_MISMATCH
;
1978 *status
= U_MISSING_RESOURCE_ERROR
;
1981 *status
= U_MISSING_RESOURCE_ERROR
;
1984 switch (RES_GET_TYPE(res
)) {
1986 case URES_STRING_V2
:
1987 return res_getString(&(resB
->fResData
), res
, len
);
1990 const UChar
* result
= 0;
1991 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1992 result
= ures_getString(tempRes
, len
, status
);
1993 ures_close(tempRes
);
1997 *status
= U_RESOURCE_TYPE_MISMATCH
;
2002 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2004 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2005 /* here should go a first attempt to locate the key using index table */
2006 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2007 if(U_SUCCESS(*status
)) {
2008 return res_getString(rd
, res
, len
);
2010 *status
= U_MISSING_RESOURCE_ERROR
;
2015 *status
= U_RESOURCE_TYPE_MISMATCH
;
2020 U_CAPI
const char * U_EXPORT2
2021 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
2023 char *dest
, int32_t *pLength
,
2025 UErrorCode
*status
) {
2027 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
2028 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
2031 /* TODO: clean from here down */
2034 * INTERNAL: Get the name of the first real locale (not placeholder)
2035 * that has resource bundle data.
2037 U_INTERNAL
const char* U_EXPORT2
2038 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
2040 if (status
==NULL
|| U_FAILURE(*status
)) {
2043 if (!resourceBundle
) {
2044 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2047 return resourceBundle
->fData
->fName
;
2051 U_CAPI
const char* U_EXPORT2
2052 ures_getLocale(const UResourceBundle
* resourceBundle
,
2055 return ures_getLocaleInternal(resourceBundle
, status
);
2059 U_CAPI
const char* U_EXPORT2
2060 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
2061 ULocDataLocaleType type
,
2062 UErrorCode
* status
) {
2063 if (status
==NULL
|| U_FAILURE(*status
)) {
2066 if (!resourceBundle
) {
2067 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2071 case ULOC_ACTUAL_LOCALE
:
2072 return resourceBundle
->fData
->fName
;
2073 case ULOC_VALID_LOCALE
:
2074 return resourceBundle
->fTopLevelData
->fName
;
2075 case ULOC_REQUESTED_LOCALE
:
2077 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2083 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
2088 return resB
->fData
->fName
;
2092 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2097 return resB
->fData
->fPath
;
2101 static UResourceBundle
*
2102 ures_openWithType(UResourceBundle
*r
, const char* path
, const char* localeID
,
2103 UResOpenType openType
, UErrorCode
* status
) {
2104 if(U_FAILURE(*status
)) {
2108 UResourceDataEntry
*entry
;
2109 if(openType
!= URES_OPEN_DIRECT
) {
2110 /* first "canonicalize" the locale ID */
2111 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2112 uloc_getBaseName(localeID
, canonLocaleID
, UPRV_LENGTHOF(canonLocaleID
), status
);
2113 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2114 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2117 entry
= entryOpen(path
, canonLocaleID
, openType
, status
);
2119 entry
= entryOpenDirect(path
, localeID
, status
);
2121 if(U_FAILURE(*status
)) {
2125 *status
= U_MISSING_RESOURCE_ERROR
;
2129 UBool isStackObject
;
2131 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2134 *status
= U_MEMORY_ALLOCATION_ERROR
;
2137 isStackObject
= FALSE
;
2139 isStackObject
= ures_isStackObject(r
);
2140 ures_closeBundle(r
, FALSE
);
2142 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2143 ures_setIsStackObject(r
, isStackObject
);
2145 r
->fTopLevelData
= r
->fData
= entry
;
2146 uprv_memcpy(&r
->fResData
, &entry
->fData
, sizeof(ResourceData
));
2147 r
->fHasFallback
= openType
!= URES_OPEN_DIRECT
&& !r
->fResData
.noFallback
;
2148 r
->fIsTopLevel
= TRUE
;
2149 r
->fRes
= r
->fResData
.rootRes
;
2150 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2156 U_CAPI UResourceBundle
* U_EXPORT2
2157 ures_open(const char* path
, const char* localeID
, UErrorCode
* status
) {
2158 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2161 U_CAPI UResourceBundle
* U_EXPORT2
2162 ures_openNoDefault(const char* path
, const char* localeID
, UErrorCode
* status
) {
2163 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_ROOT
, status
);
2167 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2168 * or sought. However, alias substitution will happen!
2170 U_CAPI UResourceBundle
* U_EXPORT2
2171 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2172 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2176 * API: This function is used to open a resource bundle
2177 * proper fallback chaining is executed while initialization.
2178 * The result is stored in cache for later fallback search.
2180 U_CAPI
void U_EXPORT2
2181 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2182 const char* localeID
, UErrorCode
* status
) {
2183 if(U_SUCCESS(*status
) && r
== NULL
) {
2184 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2187 ures_openWithType(r
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2191 * API: Counts members. For arrays and tables, returns number of resources.
2192 * For strings, returns 1.
2194 U_CAPI
int32_t U_EXPORT2
2195 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2196 const char* resourceKey
,
2199 UResourceBundle resData
;
2200 ures_initStackObject(&resData
);
2201 if (status
==NULL
|| U_FAILURE(*status
)) {
2204 if(resourceBundle
== NULL
) {
2205 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2208 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2210 if(resData
.fResData
.data
!= NULL
) {
2211 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2212 ures_close(&resData
);
2215 *status
= U_MISSING_RESOURCE_ERROR
;
2216 ures_close(&resData
);
2222 * Internal function.
2223 * Return the version number associated with this ResourceBundle as a string.
2225 * @param resourceBundle The resource bundle for which the version is checked.
2226 * @return A version number string as specified in the resource bundle or its parent.
2227 * The caller does not own this string.
2228 * @see ures_getVersion
2231 U_INTERNAL
const char* U_EXPORT2
2232 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2234 if (!resourceBundle
) return NULL
;
2236 if(resourceBundle
->fVersion
== NULL
) {
2238 /* If the version ID has not been built yet, then do so. Retrieve */
2239 /* the minor version from the file. */
2240 UErrorCode status
= U_ZERO_ERROR
;
2241 int32_t minor_len
= 0;
2244 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2246 /* Determine the length of of the final version string. This is */
2247 /* the length of the major part + the length of the separator */
2248 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2251 len
= (minor_len
> 0) ? minor_len
: 1;
2253 /* Allocate the string, and build it up. */
2254 /* + 1 for zero byte */
2257 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2258 /* Check for null pointer. */
2259 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2264 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2265 resourceBundle
->fVersion
[len
] = '\0';
2268 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2272 return resourceBundle
->fVersion
;
2275 U_CAPI
const char* U_EXPORT2
2276 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2278 return ures_getVersionNumberInternal(resourceBundle
);
2281 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2284 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2287 /** Tree support functions *******************************/
2288 #define INDEX_LOCALE_NAME "res_index"
2289 #define INDEX_TAG "InstalledLocales"
2290 #define DEFAULT_TAG "default"
2292 #if defined(URES_TREE_DEBUG)
2296 typedef struct ULocalesContext
{
2297 UResourceBundle installed
;
2298 UResourceBundle curr
;
2301 static void U_CALLCONV
2302 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2303 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2304 ures_close(&ctx
->curr
);
2305 ures_close(&ctx
->installed
);
2307 uprv_free(enumerator
);
2310 static int32_t U_CALLCONV
2311 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2312 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2313 return ures_getSize(&ctx
->installed
);
2316 static const char* U_CALLCONV
2317 ures_loc_nextLocale(UEnumeration
* en
,
2318 int32_t* resultLength
,
2319 UErrorCode
* status
) {
2320 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2321 UResourceBundle
*res
= &(ctx
->installed
);
2322 UResourceBundle
*k
= NULL
;
2323 const char *result
= NULL
;
2325 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2326 result
= ures_getKey(k
);
2327 len
= (int32_t)uprv_strlen(result
);
2330 *resultLength
= len
;
2335 static void U_CALLCONV
2336 ures_loc_resetLocales(UEnumeration
* en
,
2337 UErrorCode
* /*status*/) {
2338 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2339 ures_resetIterator(res
);
2343 static const UEnumeration gLocalesEnum
= {
2346 ures_loc_closeLocales
,
2347 ures_loc_countLocales
,
2349 ures_loc_nextLocale
,
2350 ures_loc_resetLocales
2354 U_CAPI UEnumeration
* U_EXPORT2
2355 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2357 UResourceBundle
*idx
= NULL
;
2358 UEnumeration
*en
= NULL
;
2359 ULocalesContext
*myContext
= NULL
;
2361 if(U_FAILURE(*status
)) {
2364 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2365 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2366 if(!en
|| !myContext
) {
2367 *status
= U_MEMORY_ALLOCATION_ERROR
;
2369 uprv_free(myContext
);
2372 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2374 ures_initStackObject(&myContext
->installed
);
2375 ures_initStackObject(&myContext
->curr
);
2376 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2377 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2378 if(U_SUCCESS(*status
)) {
2379 #if defined(URES_TREE_DEBUG)
2380 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2381 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2383 en
->context
= myContext
;
2385 #if defined(URES_TREE_DEBUG)
2386 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2388 ures_close(&myContext
->installed
);
2389 uprv_free(myContext
);
2399 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2401 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2402 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2409 U_CAPI
int32_t U_EXPORT2
2410 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2411 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2412 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2414 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2415 char defVal
[1024] = ""; /* default value for given locale */
2416 char defLoc
[1024] = ""; /* default value for given locale */
2417 char base
[1024] = ""; /* base locale */
2420 char full
[1024] = "";
2421 UResourceBundle bund1
, bund2
;
2422 UResourceBundle
*res
= NULL
;
2423 UErrorCode subStatus
= U_ZERO_ERROR
;
2425 if(U_FAILURE(*status
)) return 0;
2426 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2427 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2430 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2431 #if defined(URES_TREE_DEBUG)
2432 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2433 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2435 ures_initStackObject(&bund1
);
2436 ures_initStackObject(&bund2
);
2439 uprv_strcpy(parent
, base
);
2440 uprv_strcpy(found
, base
);
2443 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2444 *isAvailable
= TRUE
;
2445 if (U_SUCCESS(subStatus
)) {
2446 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2448 uenum_close(locEnum
);
2451 if(U_FAILURE(subStatus
)) {
2452 *status
= subStatus
;
2457 subStatus
= U_ZERO_ERROR
;
2458 res
= ures_open(path
, parent
, &subStatus
);
2459 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2460 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2462 *isAvailable
= FALSE
;
2464 isAvailable
= NULL
; /* only want to set this the first time around */
2466 #if defined(URES_TREE_DEBUG)
2467 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2469 if(U_FAILURE(subStatus
)) {
2470 *status
= subStatus
;
2471 } else if(subStatus
== U_ZERO_ERROR
) {
2472 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2473 if(subStatus
== U_ZERO_ERROR
) {
2474 const UChar
*defUstr
;
2476 /* look for default item */
2477 #if defined(URES_TREE_DEBUG)
2478 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2479 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2481 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2482 if(U_SUCCESS(subStatus
) && defLen
) {
2483 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2484 #if defined(URES_TREE_DEBUG)
2485 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2486 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2488 uprv_strcpy(defLoc
, parent
);
2490 uprv_strcpy(kwVal
, defVal
);
2491 #if defined(URES_TREE_DEBUG)
2492 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2493 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2500 subStatus
= U_ZERO_ERROR
;
2503 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2506 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2508 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2510 /* Now, see if we can find the kwVal collator.. start the search over.. */
2511 uprv_strcpy(parent
, base
);
2512 uprv_strcpy(found
, base
);
2515 subStatus
= U_ZERO_ERROR
;
2516 res
= ures_open(path
, parent
, &subStatus
);
2517 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2518 *isAvailable
= FALSE
;
2520 isAvailable
= NULL
; /* only want to set this the first time around */
2522 #if defined(URES_TREE_DEBUG)
2523 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2524 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2526 if(U_FAILURE(subStatus
)) {
2527 *status
= subStatus
;
2528 } else if(subStatus
== U_ZERO_ERROR
) {
2529 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2530 #if defined(URES_TREE_DEBUG)
2531 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2533 if(subStatus
== U_ZERO_ERROR
) {
2534 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2535 #if defined(URES_TREE_DEBUG)
2536 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2538 if(subStatus
== U_ZERO_ERROR
) {
2539 #if defined(URES_TREE_DEBUG)
2540 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2541 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2543 uprv_strcpy(full
, parent
);
2545 uprv_strcpy(full
, "root");
2547 /* now, recalculate default kw if need be */
2548 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2549 const UChar
*defUstr
;
2551 /* look for default item */
2552 #if defined(URES_TREE_DEBUG)
2553 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2554 path
?path
:"ICUDATA", full
);
2556 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2557 if(U_SUCCESS(subStatus
) && defLen
) {
2558 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2559 #if defined(URES_TREE_DEBUG)
2560 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2561 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2563 uprv_strcpy(defLoc
, full
);
2565 } /* end of recalculate default KW */
2566 #if defined(URES_TREE_DEBUG)
2568 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2572 #if defined(URES_TREE_DEBUG)
2573 fprintf(stderr
, "err=%s in %s looking for %s\n",
2574 u_errorName(subStatus
), parent
, kwVal
);
2580 subStatus
= U_ZERO_ERROR
;
2582 uprv_strcpy(found
, parent
);
2583 uloc_getParent(found
,parent
,1023,&subStatus
);
2585 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2587 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2588 #if defined(URES_TREE_DEBUG)
2589 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2591 uprv_strcpy(kwVal
, defVal
);
2592 uprv_strcpy(parent
, base
);
2593 uprv_strcpy(found
, base
);
2595 do { /* search for 'default' named item */
2596 subStatus
= U_ZERO_ERROR
;
2597 res
= ures_open(path
, parent
, &subStatus
);
2598 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2599 *isAvailable
= FALSE
;
2601 isAvailable
= NULL
; /* only want to set this the first time around */
2603 #if defined(URES_TREE_DEBUG)
2604 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2605 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2607 if(U_FAILURE(subStatus
)) {
2608 *status
= subStatus
;
2609 } else if(subStatus
== U_ZERO_ERROR
) {
2610 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2611 if(subStatus
== U_ZERO_ERROR
) {
2612 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2613 if(subStatus
== U_ZERO_ERROR
) {
2614 #if defined(URES_TREE_DEBUG)
2615 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2616 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2618 uprv_strcpy(full
, parent
);
2620 uprv_strcpy(full
, "root");
2623 /* now, recalculate default kw if need be */
2624 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2625 const UChar
*defUstr
;
2627 /* look for default item */
2628 #if defined(URES_TREE_DEBUG)
2629 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2630 path
?path
:"ICUDATA", full
);
2632 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2633 if(U_SUCCESS(subStatus
) && defLen
) {
2634 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2635 #if defined(URES_TREE_DEBUG)
2636 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2637 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2639 uprv_strcpy(defLoc
, full
);
2641 } /* end of recalculate default KW */
2642 #if defined(URES_TREE_DEBUG)
2644 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2650 subStatus
= U_ZERO_ERROR
;
2652 uprv_strcpy(found
, parent
);
2653 uloc_getParent(found
,parent
,1023,&subStatus
);
2655 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2658 if(U_SUCCESS(*status
)) {
2660 #if defined(URES_TREE_DEBUG)
2661 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2663 *status
= U_MISSING_RESOURCE_ERROR
;
2664 } else if(omitDefault
) {
2665 #if defined(URES_TREE_DEBUG)
2666 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2668 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2669 /* found the keyword in a *child* of where the default tag was present. */
2670 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2671 /* and the default is in or in an ancestor of the current locale */
2672 #if defined(URES_TREE_DEBUG)
2673 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2679 uprv_strcpy(found
, full
);
2681 uprv_strcat(found
, "@");
2682 uprv_strcat(found
, keyword
);
2683 uprv_strcat(found
, "=");
2684 uprv_strcat(found
, kwVal
);
2685 } else if(!omitDefault
) {
2686 uprv_strcat(found
, "@");
2687 uprv_strcat(found
, keyword
);
2688 uprv_strcat(found
, "=");
2689 uprv_strcat(found
, defVal
);
2692 /* we found the default locale - no need to repeat it.*/
2697 length
= (int32_t)uprv_strlen(found
);
2699 if(U_SUCCESS(*status
)) {
2700 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2702 uprv_strncpy(result
, found
, copyLength
);
2705 *status
= U_MISSING_RESOURCE_ERROR
;
2711 return u_terminateChars(result
, resultCapacity
, length
, status
);
2714 U_CAPI UEnumeration
* U_EXPORT2
2715 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2717 #define VALUES_BUF_SIZE 2048
2718 #define VALUES_LIST_SIZE 512
2720 char valuesBuf
[VALUES_BUF_SIZE
];
2721 int32_t valuesIndex
= 0;
2722 const char *valuesList
[VALUES_LIST_SIZE
];
2723 int32_t valuesCount
= 0;
2728 UEnumeration
*locs
= NULL
;
2730 UResourceBundle item
;
2731 UResourceBundle subItem
;
2733 ures_initStackObject(&item
);
2734 ures_initStackObject(&subItem
);
2735 locs
= ures_openAvailableLocales(path
, status
);
2737 if(U_FAILURE(*status
)) {
2739 ures_close(&subItem
);
2746 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2747 UResourceBundle
*bund
= NULL
;
2748 UResourceBundle
*subPtr
= NULL
;
2749 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2750 bund
= ures_openDirect(path
, locale
, &subStatus
);
2752 #if defined(URES_TREE_DEBUG)
2753 if(!bund
|| U_FAILURE(subStatus
)) {
2754 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2755 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2759 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2761 if(!bund
|| U_FAILURE(subStatus
)) {
2762 #if defined(URES_TREE_DEBUG)
2763 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2764 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2771 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2772 && U_SUCCESS(subStatus
)) {
2775 k
= ures_getKey(subPtr
);
2777 #if defined(URES_TREE_DEBUG)
2778 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2780 if(k
== NULL
|| *k
== 0 ||
2781 uprv_strcmp(k
, DEFAULT_TAG
) == 0 || uprv_strncmp(k
, "private-", 8) == 0) {
2782 // empty or "default" or unlisted type
2785 for(i
=0; i
<valuesCount
; i
++) {
2786 if(!uprv_strcmp(valuesList
[i
],k
)) {
2787 k
= NULL
; /* found duplicate */
2792 int32_t kLen
= (int32_t)uprv_strlen(k
);
2793 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2794 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2795 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2797 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2798 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2799 valuesIndex
+= kLen
;
2800 #if defined(URES_TREE_DEBUG)
2801 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2802 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2804 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2810 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2813 ures_close(&subItem
);
2815 #if defined(URES_TREE_DEBUG)
2816 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2817 valuesIndex
, valuesCount
);
2819 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2822 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2823 U_INTERNAL UBool U_EXPORT2
2824 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2825 if(res1
==NULL
|| res2
==NULL
){
2826 return res1
==res2
; /* pointer comparision */
2828 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2829 return (res1
->fKey
==res2
->fKey
);
2831 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2835 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2838 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2839 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2841 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2845 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2848 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2851 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2854 if(res1
->fRes
!= res2
->fRes
){
2859 U_INTERNAL UResourceBundle
* U_EXPORT2
2860 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2861 UResourceBundle
* bundle
= NULL
;
2862 UResourceBundle
* ret
= NULL
;
2863 if(U_FAILURE(*status
) || res
== NULL
){
2866 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2867 if(res
->fResPath
!=NULL
){
2868 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2875 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2876 ures_getParentBundle(const UResourceBundle
* res
){
2880 return res
->fParentRes
;
2884 U_INTERNAL
void U_EXPORT2
2885 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
2888 str
= ures_getStringByKey(res
, key
, &len
, status
);
2889 if(U_SUCCESS(*status
)) {
2890 u_versionFromUString(ver
, str
);