2 ******************************************************************************
3 * Copyright (C) 1997-2016, 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));
380 r
->fData
.poolBundleStrings
= r
->fPool
->fData
.p16BitUnits
;
382 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
388 if (U_SUCCESS(*status
)) {
389 /* handle the alias by trying to get out the %%Alias tag.*/
390 /* We'll try to get alias string from the bundle */
391 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
392 if (aliasres
!= RES_BOGUS
) {
393 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
394 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
395 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
396 r
->fAlias
= init_entry(aliasName
, path
, status
);
403 UResourceDataEntry
*oldR
= NULL
;
404 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
405 /* just insert it in the cache */
406 UErrorCode cacheStatus
= U_ZERO_ERROR
;
407 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
408 if (U_FAILURE(cacheStatus
)) {
409 *status
= cacheStatus
;
414 /* somebody have already inserted it while we were working, discard newly opened data */
415 /* Also, we could get here IF we opened an alias */
423 /* return the real bundle */
424 while(r
->fAlias
!= NULL
) {
427 r
->fCountExisting
++; /* we increase its reference count */
428 /* if the resource has a warning */
429 /* we don't want to overwrite a status with no error */
430 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
431 *status
= r
->fBogus
; /* set the returning status */
437 static UResourceDataEntry
*
438 getPoolEntry(const char *path
, UErrorCode
*status
) {
439 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
440 if( U_SUCCESS(*status
) &&
441 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
443 *status
= U_INVALID_FORMAT_ERROR
;
449 /* CAUTION: resbMutex must be locked when calling this function! */
450 static UResourceDataEntry
*
451 findFirstExisting(const char* path
, char* name
,
452 UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
453 UResourceDataEntry
*r
= NULL
;
454 UBool hasRealData
= FALSE
;
455 const char *defaultLoc
= uloc_getDefault();
456 *hasChopped
= TRUE
; /* we're starting with a fresh name */
458 while(*hasChopped
&& !hasRealData
) {
459 r
= init_entry(name
, path
, status
);
460 /* Null pointer test */
461 if (U_FAILURE(*status
)) {
464 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
465 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
467 /* this entry is not real. We will discard it. */
468 /* However, the parent line for this entry is */
469 /* not to be used - as there might be parent */
470 /* lines in cache from previous openings that */
471 /* are not updated yet. */
473 /*entryCloseInt(r);*/
475 *status
= U_USING_FALLBACK_WARNING
;
477 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
480 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
482 /*Fallback data stuff*/
483 *hasChopped
= chopLocale(name
);
488 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
493 resB
->fMagic1
= MAGIC1
;
494 resB
->fMagic2
= MAGIC2
;
498 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
499 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
503 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
504 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
505 ures_setIsStackObject(resB
, TRUE
);
508 static UBool
// returns U_SUCCESS(*status)
509 loadParentsExceptRoot(UResourceDataEntry
*&t1
,
510 char name
[], int32_t nameCapacity
,
511 UBool usingUSRData
, char usrDataPath
[], UErrorCode
*status
) {
512 if (U_FAILURE(*status
)) { return FALSE
; }
513 UBool hasChopped
= TRUE
;
514 while (hasChopped
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
&&
515 res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
516 Resource parentRes
= res_getResource(&t1
->fData
, "%%Parent");
517 if (parentRes
!= RES_BOGUS
) { // An explicit parent was found.
518 int32_t parentLocaleLen
= 0;
519 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), parentRes
, &parentLocaleLen
);
520 if(parentLocaleName
!= NULL
&& 0 < parentLocaleLen
&& parentLocaleLen
< nameCapacity
) {
521 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+ 1);
522 if (uprv_strcmp(name
, kRootLocaleName
) == 0) {
527 // Insert regular parents.
528 UErrorCode parentStatus
= U_ZERO_ERROR
;
529 UResourceDataEntry
*t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
530 if (U_FAILURE(parentStatus
)) {
531 *status
= parentStatus
;
534 UResourceDataEntry
*u2
= NULL
;
535 UErrorCode usrStatus
= U_ZERO_ERROR
;
536 if (usingUSRData
) { // This code inserts user override data into the inheritance chain.
537 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
540 if (usingUSRData
&& U_SUCCESS(usrStatus
) && u2
->fBogus
== U_ZERO_ERROR
) {
546 // The USR override data wasn't found, set it to be deleted.
547 u2
->fCountExisting
= 0;
551 hasChopped
= chopLocale(name
);
556 static UBool
// returns U_SUCCESS(*status)
557 insertRootBundle(UResourceDataEntry
*&t1
, UErrorCode
*status
) {
558 if (U_FAILURE(*status
)) { return FALSE
; }
559 UErrorCode parentStatus
= U_ZERO_ERROR
;
560 UResourceDataEntry
*t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
561 if (U_FAILURE(parentStatus
)) {
562 *status
= parentStatus
;
572 * Open a resource bundle for the locale;
573 * if there is not even a base language bundle, then fall back to the default locale;
574 * if there is no bundle for that either, then load the root bundle.
576 * This is the default bundle loading behavior.
578 URES_OPEN_LOCALE_DEFAULT_ROOT
,
579 // TODO: ICU ticket #11271 "consistent default locale across locale trees"
580 // Add an option to look at the main locale tree for whether to
581 // fall back to root directly (if the locale has main data) or
582 // fall back to the default locale first (if the locale does not even have main data).
584 * Open a resource bundle for the locale;
585 * if there is not even a base language bundle, then load the root bundle;
586 * never fall back to the default locale.
588 * This is used for algorithms that have good pan-Unicode default behavior,
589 * such as case mappings, collation, and segmentation (BreakIterator).
591 URES_OPEN_LOCALE_ROOT
,
593 * Open a resource bundle for the exact bundle name as requested;
594 * no fallbacks, do not load parent bundles.
596 * This is used for supplemental (non-locale) data.
600 typedef enum UResOpenType UResOpenType
;
602 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
,
603 UResOpenType openType
, UErrorCode
* status
) {
604 U_ASSERT(openType
!= URES_OPEN_DIRECT
);
605 UErrorCode intStatus
= U_ZERO_ERROR
;
606 UResourceDataEntry
*r
= NULL
;
607 UResourceDataEntry
*t1
= NULL
;
608 UBool isDefault
= FALSE
;
609 UBool isRoot
= FALSE
;
610 UBool hasRealData
= FALSE
;
611 UBool hasChopped
= TRUE
;
612 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
614 char name
[ULOC_FULLNAME_CAPACITY
];
615 char usrDataPath
[96];
619 if(U_FAILURE(*status
)) {
623 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
624 name
[sizeof(name
) - 1] = 0;
626 if ( usingUSRData
) {
627 if ( path
== NULL
) {
628 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
630 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
631 usrDataPath
[0] = 'u';
632 usrDataPath
[1] = 's';
633 usrDataPath
[2] = 'r';
634 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
638 umtx_lock(&resbMutex
);
640 /* We're going to skip all the locales that do not have any data */
641 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
643 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
646 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
647 UErrorCode usrStatus
= U_ZERO_ERROR
;
648 UResourceDataEntry
*u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
650 if(u1
->fBogus
== U_ZERO_ERROR
) {
654 /* the USR override data wasn't found, set it to be deleted */
655 u1
->fCountExisting
= 0;
659 if (hasChopped
&& !isRoot
) {
660 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
666 /* we could have reached this point without having any real data */
667 /* if that is the case, we need to chain in the default locale */
668 if(r
==NULL
&& openType
== URES_OPEN_LOCALE_DEFAULT_ROOT
&& !isDefault
&& !isRoot
) {
669 /* insert default locale */
670 uprv_strcpy(name
, uloc_getDefault());
671 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
672 intStatus
= U_USING_DEFAULT_WARNING
;
673 if(r
!= NULL
) { /* the default locale exists */
677 // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
678 if (hasChopped
&& !isRoot
) {
679 if (!loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), usingUSRData
, usrDataPath
, status
)) {
686 /* we could still have r == NULL at this point - maybe even default locale is not */
689 uprv_strcpy(name
, kRootLocaleName
);
690 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
693 intStatus
= U_USING_DEFAULT_WARNING
;
695 } else { /* we don't even have the root locale */
696 *status
= U_MISSING_RESOURCE_ERROR
;
699 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 &&
700 t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
701 if (!insertRootBundle(t1
, status
)) {
705 r
->fBogus
= U_USING_DEFAULT_WARNING
;
709 // TODO: Does this ever loop?
710 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
711 t1
->fParent
->fCountExisting
++;
716 umtx_unlock(&resbMutex
);
718 if(U_SUCCESS(*status
)) {
719 if(intStatus
!= U_ZERO_ERROR
) {
729 * Version of entryOpen() and findFirstExisting() for ures_openDirect(),
731 * Parent and root locale bundles are loaded if
732 * the requested bundle does not have the "nofallback" flag.
734 static UResourceDataEntry
*
735 entryOpenDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
737 if(U_FAILURE(*status
)) {
741 umtx_lock(&resbMutex
);
742 // findFirstExisting() without fallbacks.
743 UResourceDataEntry
*r
= init_entry(localeID
, path
, status
);
744 if(U_SUCCESS(*status
)) {
745 if(r
->fBogus
!= U_ZERO_ERROR
) {
753 // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
754 // unless it is marked with "nofallback".
755 UResourceDataEntry
*t1
= r
;
756 if(r
!= NULL
&& uprv_strcmp(localeID
, kRootLocaleName
) != 0 && // not root
757 r
->fParent
== NULL
&& !r
->fData
.noFallback
&&
758 uprv_strlen(localeID
) < ULOC_FULLNAME_CAPACITY
) {
759 char name
[ULOC_FULLNAME_CAPACITY
];
760 uprv_strcpy(name
, localeID
);
761 if(!chopLocale(name
) || uprv_strcmp(name
, kRootLocaleName
) == 0 ||
762 loadParentsExceptRoot(t1
, name
, UPRV_LENGTHOF(name
), FALSE
, NULL
, status
)) {
763 if(uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
) {
764 insertRootBundle(t1
, status
);
767 if(U_FAILURE(*status
)) {
773 // TODO: Does this ever loop?
774 while(t1
->fParent
!= NULL
) {
775 t1
->fParent
->fCountExisting
++;
779 umtx_unlock(&resbMutex
);
784 * Functions to create and destroy resource bundles.
785 * CAUTION: resbMutex must be locked when calling this function.
788 static void entryCloseInt(UResourceDataEntry
*resB
) {
789 UResourceDataEntry
*p
= resB
;
791 while(resB
!= NULL
) {
793 resB
->fCountExisting
--;
795 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
798 if(resB->fCountExisting <= 0) {
799 uhash_remove(cache, resB);
800 if(resB->fBogus == U_ZERO_ERROR) {
801 res_unload(&(resB->fData));
803 if(resB->fName != NULL) {
804 uprv_free(resB->fName);
806 if(resB->fPath != NULL) {
807 uprv_free(resB->fPath);
818 * API: closes a resource bundle and cleans up.
821 static void entryClose(UResourceDataEntry
*resB
) {
822 umtx_lock(&resbMutex
);
824 umtx_unlock(&resbMutex
);
828 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
829 if(resB->fResPath == NULL) {
830 resB->fResPath = resB->fResBuf;
831 *(resB->fResPath) = 0;
833 resB->fResPathLen = uprv_strlen(toAdd);
834 if(RES_BUFSIZE <= resB->fResPathLen+1) {
835 if(resB->fResPath == resB->fResBuf) {
836 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
838 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
841 uprv_strcpy(resB->fResPath, toAdd);
844 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
845 int32_t resPathLenOrig
= resB
->fResPathLen
;
846 if(resB
->fResPath
== NULL
) {
847 resB
->fResPath
= resB
->fResBuf
;
848 *(resB
->fResPath
) = 0;
849 resB
->fResPathLen
= 0;
851 resB
->fResPathLen
+= lenToAdd
;
852 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
853 if(resB
->fResPath
== resB
->fResBuf
) {
854 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
855 /* Check that memory was allocated correctly. */
856 if (resB
->fResPath
== NULL
) {
857 *status
= U_MEMORY_ALLOCATION_ERROR
;
860 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
862 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
863 /* Check that memory was reallocated correctly. */
865 *status
= U_MEMORY_ALLOCATION_ERROR
;
868 resB
->fResPath
= temp
;
871 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
874 static void ures_freeResPath(UResourceBundle
*resB
) {
875 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
876 uprv_free(resB
->fResPath
);
878 resB
->fResPath
= NULL
;
879 resB
->fResPathLen
= 0;
883 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
886 if(resB
->fData
!= NULL
) {
887 entryClose(resB
->fData
);
889 if(resB
->fVersion
!= NULL
) {
890 uprv_free(resB
->fVersion
);
892 ures_freeResPath(resB
);
894 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
899 /* poison the data */
900 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
906 U_CAPI
void U_EXPORT2
907 ures_close(UResourceBundle
* resB
)
909 ures_closeBundle(resB
, TRUE
);
912 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
913 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
914 const UResourceBundle
*parent
, int32_t noAlias
,
915 UResourceBundle
*resB
, UErrorCode
*status
)
917 if(status
== NULL
|| U_FAILURE(*status
)) {
920 if (parent
== NULL
) {
921 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
924 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
925 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
927 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
929 /* we have an alias, now let's cut it up */
930 char stackAlias
[200];
931 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
935 * Allocate enough space for both the char * version
936 * of the alias and parent->fResPath.
938 * We do this so that res_findResource() can modify the path,
939 * which allows us to remove redundant _res_findResource() variants
941 * res_findResource() now NUL-terminates each segment so that table keys
942 * can always be compared with strcmp() instead of strncmp().
943 * Saves code there and simplifies testing and code coverage.
947 ++len
; /* count the terminating NUL */
948 if(parent
->fResPath
!= NULL
) {
949 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
956 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
957 capacity
= (int32_t)sizeof(stackAlias
);
958 chAlias
= stackAlias
;
960 chAlias
= (char *)uprv_malloc(capacity
);
962 if(chAlias
== NULL
) {
963 *status
= U_MEMORY_ALLOCATION_ERROR
;
967 u_UCharsToChars(alias
, chAlias
, len
);
969 if(*chAlias
== RES_PATH_SEPARATOR
) {
970 /* there is a path included */
971 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
973 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
979 if(uprv_strcmp(path
, "LOCALE") == 0) {
980 /* this is an XPath alias, starting with "/LOCALE/" */
981 /* it contains the path to a resource which should be looked up */
982 /* starting in the requested locale */
984 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
985 path
= realData
->fPath
; /* we will be looking in the same package */
987 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
990 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
997 /* no path, start with a locale */
999 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1004 path
= realData
->fPath
;
1009 /* got almost everything, let's try to open */
1010 /* first, open the bundle with real data */
1011 UResourceBundle
*result
= resB
;
1012 const char* temp
= NULL
;
1013 UErrorCode intStatus
= U_ZERO_ERROR
;
1014 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
1015 if(U_SUCCESS(intStatus
)) {
1016 if(keyPath
== NULL
) {
1017 /* no key path. This means that we are going to
1018 * to use the corresponding resource from
1021 /* first, we are going to get a corresponding parent
1022 * resource to the one we are searching.
1024 char *aKey
= parent
->fResPath
;
1026 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
1028 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
1033 /* we need to make keyPath from parent's fResPath and
1034 * current key, if there is a key associated
1036 len
= (int32_t)(uprv_strlen(key
) + 1);
1037 if(len
> capacity
) {
1039 if(chAlias
== stackAlias
) {
1040 chAlias
= (char *)uprv_malloc(capacity
);
1042 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
1044 if(chAlias
== NULL
) {
1045 ures_close(mainRes
);
1046 *status
= U_MEMORY_ALLOCATION_ERROR
;
1050 uprv_memcpy(chAlias
, key
, len
);
1052 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
1053 } else if(idx
!= -1) {
1054 /* if there is no key, but there is an index, try to get by the index */
1055 /* here we have either a table or an array, so get the element */
1056 int32_t type
= RES_GET_TYPE(r
);
1057 if(URES_IS_TABLE(type
)) {
1058 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
1059 } else { /* array */
1060 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
1063 if(r
!= RES_BOGUS
) {
1064 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
1066 *status
= U_MISSING_RESOURCE_ERROR
;
1070 /* this one is a bit trickier.
1071 * we start finding keys, but after we resolve one alias, the path might continue.
1073 * aliastest:alias { "testtypes/anotheralias/Sequence" }
1074 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1075 * aliastest resource should finally have the sequence, not collation elements.
1077 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1078 char stackPath
[URES_MAX_BUFFER_SIZE
];
1079 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1080 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
1081 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1082 if(pathBuf
== NULL
) {
1083 *status
= U_MEMORY_ALLOCATION_ERROR
;
1087 uprv_strcpy(pathBuf
, keyPath
);
1089 /* now we have fallback following here */
1091 r
= dataEntry
->fData
.rootRes
;
1092 /* this loop handles 'found' resources over several levels */
1093 while(*myPath
&& U_SUCCESS(*status
)) {
1094 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1095 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1096 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1099 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1100 dataEntry
= result
->fData
;
1102 } else { /* no resource found, we don't really want to look anymore on this level */
1106 dataEntry
= dataEntry
->fParent
;
1107 uprv_strcpy(pathBuf
, keyPath
);
1109 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1110 if(r
== RES_BOGUS
) {
1111 *status
= U_MISSING_RESOURCE_ERROR
;
1114 if(pathBuf
!= stackPath
) {
1118 } else { /* we failed to open the resource we're aliasing to */
1119 *status
= intStatus
;
1121 if(chAlias
!= stackAlias
) {
1124 if(mainRes
!= result
) {
1125 ures_close(mainRes
);
1130 /* bad alias, should be an error */
1131 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1135 *status
= U_TOO_MANY_ALIASES_ERROR
;
1140 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1143 *status
= U_MEMORY_ALLOCATION_ERROR
;
1146 ures_setIsStackObject(resB
, FALSE
);
1147 resB
->fResPath
= NULL
;
1148 resB
->fResPathLen
= 0;
1150 if(resB
->fData
!= NULL
) {
1151 entryClose(resB
->fData
);
1153 if(resB
->fVersion
!= NULL
) {
1154 uprv_free(resB
->fVersion
);
1157 weiv: if stack object was passed in, it doesn't really need to be reinited,
1158 since the purpose of initing is to remove stack junk. However, at this point
1159 we would not do anything to an allocated object, so stack object should be
1163 if(ures_isStackObject(resB) != FALSE) {
1164 ures_initStackObject(resB);
1167 if(parent
!= resB
) {
1168 ures_freeResPath(resB
);
1171 resB
->fData
= realData
;
1172 entryIncrease(resB
->fData
);
1173 resB
->fHasFallback
= FALSE
;
1174 resB
->fIsTopLevel
= FALSE
;
1177 /*resB->fParentRes = parent;*/
1178 resB
->fTopLevelData
= parent
->fTopLevelData
;
1179 if(parent
->fResPath
&& parent
!= resB
) {
1180 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1183 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1184 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1185 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1187 } else if(idx
>= 0) {
1189 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1190 ures_appendResPath(resB
, buf
, len
, status
);
1191 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1192 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1195 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1197 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1198 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1201 resB
->fVersion
= NULL
;
1203 /*resB->fParent = parent->fRes;*/
1204 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1205 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1209 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1210 UBool isStackObject
;
1211 if(U_FAILURE(*status
) || r
== original
) {
1214 if(original
!= NULL
) {
1216 isStackObject
= FALSE
;
1217 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1220 *status
= U_MEMORY_ALLOCATION_ERROR
;
1224 isStackObject
= ures_isStackObject(r
);
1225 ures_closeBundle(r
, FALSE
);
1227 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1230 if(original
->fResPath
) {
1231 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1233 ures_setIsStackObject(r
, isStackObject
);
1234 if(r
->fData
!= NULL
) {
1235 entryIncrease(r
->fData
);
1242 * Functions to retrieve data from resource bundles.
1245 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1247 if (status
==NULL
|| U_FAILURE(*status
)) {
1251 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1254 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1256 *status
= U_RESOURCE_TYPE_MISMATCH
;
1262 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1263 char *dest
, int32_t *pLength
,
1265 UErrorCode
*status
) {
1268 if (U_FAILURE(*status
)) {
1271 if (pLength
!= NULL
) {
1272 capacity
= *pLength
;
1276 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1277 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1281 if (length16
== 0) {
1282 /* empty string, return as read-only pointer */
1283 if (pLength
!= NULL
) {
1287 u_terminateChars(dest
, capacity
, 0, status
);
1293 /* We need to transform the string to the destination buffer. */
1294 if (capacity
< length16
) {
1295 /* No chance for the string to fit. Pure preflighting. */
1296 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1298 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1300 * We know the string will fit into dest because each UChar turns
1301 * into at most three UTF-8 bytes. Fill the latter part of dest
1302 * so that callers do not expect to use dest as a string pointer,
1303 * hopefully leading to more robust code for when resource bundles
1304 * may store UTF-8 natively.
1305 * (In which case dest would not be used at all.)
1307 * We do not do this if forceCopy=TRUE because then the caller
1308 * expects the string to start exactly at dest.
1310 * The test above for <= 0x2aaaaaaa prevents overflows.
1311 * The +1 is for the NUL terminator.
1313 int32_t maxLength
= 3 * length16
+ 1;
1314 if (capacity
> maxLength
) {
1315 dest
+= capacity
- maxLength
;
1316 capacity
= maxLength
;
1319 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1323 U_CAPI
const char * U_EXPORT2
1324 ures_getUTF8String(const UResourceBundle
*resB
,
1325 char *dest
, int32_t *pLength
,
1327 UErrorCode
*status
) {
1329 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1330 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1333 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1334 UErrorCode
* status
) {
1336 if (status
==NULL
|| U_FAILURE(*status
)) {
1340 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1343 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1345 *status
= U_RESOURCE_TYPE_MISMATCH
;
1350 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1351 UErrorCode
* status
) {
1353 if (status
==NULL
|| U_FAILURE(*status
)) {
1357 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1360 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1362 *status
= U_RESOURCE_TYPE_MISMATCH
;
1367 /* this function returns a signed integer */
1368 /* it performs sign extension */
1369 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1370 if (status
==NULL
|| U_FAILURE(*status
)) {
1374 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1377 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1378 *status
= U_RESOURCE_TYPE_MISMATCH
;
1381 return RES_GET_INT(resB
->fRes
);
1384 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1385 if (status
==NULL
|| U_FAILURE(*status
)) {
1389 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1392 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1393 *status
= U_RESOURCE_TYPE_MISMATCH
;
1396 return RES_GET_UINT(resB
->fRes
);
1399 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1403 return res_getPublicType(resB
->fRes
);
1406 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1414 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1422 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1423 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1424 const UChar
* result
= 0;
1425 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1426 result
= ures_getString(tempRes
, len
, status
);
1427 ures_close(tempRes
);
1430 return res_getString(&(resB
->fResData
), r
, len
);
1434 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1441 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1445 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1448 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1449 Resource r
= RES_BOGUS
;
1451 if (status
==NULL
|| U_FAILURE(*status
)) {
1455 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1459 if(resB
->fIndex
== resB
->fSize
-1) {
1460 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1463 switch(RES_GET_TYPE(resB
->fRes
)) {
1465 case URES_STRING_V2
:
1466 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1470 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1471 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1472 /* TODO: do the fallback */
1474 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1477 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1478 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1479 /* TODO: do the fallback */
1481 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1483 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1486 case URES_INT_VECTOR
:
1487 *status
= U_RESOURCE_TYPE_MISMATCH
;
1497 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1498 const char *key
= NULL
;
1499 Resource r
= RES_BOGUS
;
1501 if (status
==NULL
|| U_FAILURE(*status
)) {
1506 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1511 if(resB
->fIndex
== resB
->fSize
-1) {
1512 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1516 switch(RES_GET_TYPE(resB
->fRes
)) {
1520 case URES_STRING_V2
:
1521 case URES_INT_VECTOR
:
1522 return ures_copyResb(fillIn
, resB
, status
);
1526 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1527 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1528 /* TODO: do the fallback */
1530 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1533 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1534 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1535 /* TODO: do the fallback */
1537 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1547 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1548 const char* key
= NULL
;
1549 Resource r
= RES_BOGUS
;
1551 if (status
==NULL
|| U_FAILURE(*status
)) {
1556 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1561 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1562 switch(RES_GET_TYPE(resB
->fRes
)) {
1566 case URES_STRING_V2
:
1567 case URES_INT_VECTOR
:
1568 return ures_copyResb(fillIn
, resB
, status
);
1572 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1573 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1574 /* TODO: do the fallback */
1576 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1579 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1580 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1581 /* TODO: do the fallback */
1583 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1589 *status
= U_MISSING_RESOURCE_ERROR
;
1595 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1596 const char* key
= NULL
;
1597 Resource r
= RES_BOGUS
;
1599 if (status
==NULL
|| U_FAILURE(*status
)) {
1603 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1607 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1608 switch(RES_GET_TYPE(resB
->fRes
)) {
1610 case URES_STRING_V2
:
1611 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1615 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1616 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1617 /* TODO: do the fallback */
1619 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1622 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1623 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1624 /* TODO: do the fallback */
1626 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1628 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1631 case URES_INT_VECTOR
:
1632 *status
= U_RESOURCE_TYPE_MISMATCH
;
1635 /* must not occur */
1636 *status
= U_INTERNAL_PROGRAM_ERROR
;
1640 *status
= U_MISSING_RESOURCE_ERROR
;
1645 U_CAPI
const char * U_EXPORT2
1646 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1648 char *dest
, int32_t *pLength
,
1650 UErrorCode
*status
) {
1652 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1653 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1656 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1657 return resB->fResPath;
1660 U_CAPI UResourceBundle
* U_EXPORT2
1661 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1663 UResourceBundle
*first
= NULL
;
1664 UResourceBundle
*result
= fillIn
;
1665 char *packageName
= NULL
;
1666 char *pathToResource
= NULL
, *save
= NULL
;
1667 char *locale
= NULL
, *localeEnd
= NULL
;
1670 if(status
== NULL
|| U_FAILURE(*status
)) {
1674 length
= (int32_t)(uprv_strlen(path
)+1);
1675 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1677 if(pathToResource
== NULL
) {
1678 *status
= U_MEMORY_ALLOCATION_ERROR
;
1681 uprv_memcpy(pathToResource
, path
, length
);
1683 locale
= pathToResource
;
1684 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1686 packageName
= pathToResource
;
1687 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1688 if(pathToResource
== NULL
) {
1689 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1691 *pathToResource
= 0;
1692 locale
= pathToResource
+1;
1696 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1697 if(localeEnd
!= NULL
) {
1701 first
= ures_open(packageName
, locale
, status
);
1703 if(U_SUCCESS(*status
)) {
1705 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1707 result
= ures_copyResb(fillIn
, first
, status
);
1715 U_CAPI UResourceBundle
* U_EXPORT2
1716 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1718 Resource res
= RES_BOGUS
;
1719 UResourceBundle
*result
= fillIn
;
1722 if(status
== NULL
|| U_FAILURE(*status
)) {
1726 /* here we do looping and circular alias checking */
1727 /* this loop is here because aliasing is resolved on this level, not on res level */
1728 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1730 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1731 if(res
!= RES_BOGUS
) {
1732 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1735 *status
= U_MISSING_RESOURCE_ERROR
;
1738 } while(*path
); /* there is more stuff in the path */
1742 U_INTERNAL
const UChar
* U_EXPORT2
1743 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1746 UErrorCode
*status
) {
1748 UResourceBundle stack
;
1749 const UChar
* retVal
= NULL
;
1750 ures_initStackObject(&stack
);
1751 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1753 retVal
= ures_getString(&stack
, &length
, status
);
1755 if (U_FAILURE(*status
)) {
1758 if (length
== 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1761 *status
= U_MISSING_RESOURCE_ERROR
;
1770 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1772 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1773 Resource resource
= table
; /* The current resource */
1774 icu::CharString path
;
1775 UErrorCode errorCode
= U_ZERO_ERROR
;
1776 path
.append(key
, errorCode
);
1777 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1778 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1779 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1780 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1781 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1782 if (nextPathPart
!= NULL
) {
1783 *nextPathPart
= 0; /* Terminating null for this part of path. */
1786 nextPathPart
= uprv_strchr(pathPart
, 0);
1789 const char *pathP
= pathPart
;
1790 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
1791 type
= (UResType
)RES_GET_TYPE(resource
);
1792 pathPart
= nextPathPart
;
1800 U_CAPI UResourceBundle
* U_EXPORT2
1801 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1803 UResourceBundle
*fillIn
,
1804 UErrorCode
*status
) {
1805 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1806 /*UResourceDataEntry *realData = NULL;*/
1807 UResourceBundle
*helper
= NULL
;
1809 if (status
==NULL
|| U_FAILURE(*status
)) {
1813 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1817 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1818 if(URES_IS_TABLE(type
)) {
1819 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
1820 const char* key
= inKey
;
1821 if(res
== RES_BOGUS
) {
1822 UResourceDataEntry
*dataEntry
= resB
->fData
;
1824 char *myPath
= NULL
;
1825 const char* resPath
= resB
->fResPath
;
1826 int32_t len
= resB
->fResPathLen
;
1827 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1828 dataEntry
= dataEntry
->fParent
;
1829 rootRes
= dataEntry
->fData
.rootRes
;
1831 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1834 path
.append(resPath
, len
, *status
);
1836 path
.append(inKey
, *status
);
1837 if (U_FAILURE(*status
)) {
1841 myPath
= path
.data();
1844 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1845 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1846 /* We hit an alias, but we didn't finish following the path. */
1847 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1848 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1850 dataEntry
= helper
->fData
;
1851 rootRes
= helper
->fRes
;
1852 resPath
= helper
->fResPath
;
1853 len
= helper
->fResPathLen
;
1859 } while(*myPath
); /* Continue until the whole path is consumed */
1862 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1863 if(res
!= RES_BOGUS
) {
1864 /* check if resB->fResPath gives the right name here */
1865 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1866 *status
= U_USING_DEFAULT_WARNING
;
1868 *status
= U_USING_FALLBACK_WARNING
;
1871 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1873 *status
= U_MISSING_RESOURCE_ERROR
;
1876 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1880 *status
= U_RESOURCE_TYPE_MISMATCH
;
1888 void getAllContainerItemsWithFallback(
1889 const UResourceBundle
*bundle
, ResourceDataValue
&value
,
1890 ResourceArraySink
*arraySink
, ResourceTableSink
*tableSink
,
1891 UErrorCode
&errorCode
) {
1892 if (U_FAILURE(errorCode
)) { return; }
1893 // We recursively enumerate child-first,
1894 // only storing parent items in the absence of child items.
1895 // We store a placeholder value for the no-fallback/no-inheritance marker
1896 // to prevent a parent item from being stored.
1898 // It would be possible to recursively enumerate parent-first,
1899 // overriding parent items with child items.
1900 // When we see the no-fallback/no-inheritance marker,
1901 // then we would remove the parent's item.
1902 // We would deserialize parent values even though they are overridden in a child bundle.
1903 UResType expectedType
= arraySink
!= NULL
? URES_ARRAY
: URES_TABLE
;
1904 if (ures_getType(bundle
) == expectedType
) {
1905 value
.pResData
= &bundle
->fResData
;
1906 if (arraySink
!= NULL
) {
1907 ures_getAllArrayItems(&bundle
->fResData
, bundle
->fRes
, value
, *arraySink
, errorCode
);
1908 } else /* tableSink != NULL */ {
1909 ures_getAllTableItems(&bundle
->fResData
, bundle
->fRes
, value
, *tableSink
, errorCode
);
1912 UResourceDataEntry
*entry
= bundle
->fData
->fParent
;
1913 if (entry
!= NULL
&& U_SUCCESS(entry
->fBogus
)) {
1914 // We might try to query the sink whether
1915 // any fallback from the parent bundle is still possible.
1917 // Turn the parent UResourceDataEntry into a UResourceBundle,
1918 // much like in ures_openWithType().
1919 // TODO: See if we can refactor ures_getByKeyWithFallback()
1920 // and pull out an inner function that takes and returns a UResourceDataEntry
1921 // so that we need not create UResourceBundle objects.
1922 UResourceBundle parentBundle
;
1923 ures_initStackObject(&parentBundle
);
1924 parentBundle
.fTopLevelData
= parentBundle
.fData
= entry
;
1925 // TODO: What is the difference between bundle fData and fTopLevelData?
1926 uprv_memcpy(&parentBundle
.fResData
, &entry
->fData
, sizeof(ResourceData
));
1927 // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1928 parentBundle
.fHasFallback
= !parentBundle
.fResData
.noFallback
;
1929 parentBundle
.fIsTopLevel
= TRUE
;
1930 parentBundle
.fRes
= parentBundle
.fResData
.rootRes
;
1931 parentBundle
.fSize
= res_countArrayItems(&(parentBundle
.fResData
), parentBundle
.fRes
);
1932 parentBundle
.fIndex
= -1;
1933 entryIncrease(entry
);
1935 // Look up the container item in the parent bundle.
1936 UResourceBundle containerBundle
;
1937 ures_initStackObject(&containerBundle
);
1938 const UResourceBundle
*rb
;
1939 if (bundle
->fResPath
== NULL
|| *bundle
->fResPath
== 0) {
1942 rb
= ures_getByKeyWithFallback(&parentBundle
, bundle
->fResPath
,
1943 &containerBundle
, &errorCode
);
1945 if (U_SUCCESS(errorCode
) && ures_getType(rb
) == expectedType
) {
1946 getAllContainerItemsWithFallback(rb
, value
,
1947 arraySink
, tableSink
, errorCode
);
1949 ures_close(&containerBundle
);
1950 ures_close(&parentBundle
);
1954 void getAllContainerItemsWithFallback(
1955 const UResourceBundle
*bundle
, const char *path
,
1956 ResourceArraySink
*arraySink
, ResourceTableSink
*tableSink
,
1957 UErrorCode
&errorCode
) {
1958 if (U_FAILURE(errorCode
)) { return; }
1960 errorCode
= U_ILLEGAL_ARGUMENT_ERROR
;
1963 UResourceBundle stackBundle
;
1964 ures_initStackObject(&stackBundle
);
1965 const UResourceBundle
*rb
;
1970 rb
= ures_getByKeyWithFallback(bundle
, path
, &stackBundle
, &errorCode
);
1971 if (U_FAILURE(errorCode
)) {
1972 ures_close(&stackBundle
);
1976 UResType expectedType
= arraySink
!= NULL
? URES_ARRAY
: URES_TABLE
;
1977 if (ures_getType(rb
) != expectedType
) {
1978 errorCode
= U_RESOURCE_TYPE_MISMATCH
;
1979 ures_close(&stackBundle
);
1982 // Get all table items with fallback.
1983 ResourceDataValue value
;
1984 getAllContainerItemsWithFallback(rb
, value
, arraySink
, tableSink
, errorCode
);
1985 ures_close(&stackBundle
);
1990 U_CAPI
void U_EXPORT2
1991 ures_getAllArrayItemsWithFallback(const UResourceBundle
*bundle
, const char *path
,
1992 ResourceArraySink
&sink
, UErrorCode
&errorCode
) {
1993 getAllContainerItemsWithFallback(bundle
, path
, &sink
, NULL
, errorCode
);
1996 U_CAPI
void U_EXPORT2
1997 ures_getAllTableItemsWithFallback(const UResourceBundle
*bundle
, const char *path
,
1998 ResourceTableSink
&sink
, UErrorCode
&errorCode
) {
1999 getAllContainerItemsWithFallback(bundle
, path
, NULL
, &sink
, errorCode
);
2002 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
2003 Resource res
= RES_BOGUS
;
2004 UResourceDataEntry
*realData
= NULL
;
2005 const char *key
= inKey
;
2007 if (status
==NULL
|| U_FAILURE(*status
)) {
2011 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2015 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2016 if(URES_IS_TABLE(type
)) {
2018 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2019 if(res
== RES_BOGUS
) {
2021 if(resB
->fHasFallback
== TRUE
) {
2022 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2023 if(U_SUCCESS(*status
)) {
2024 /* check if resB->fResPath gives the right name here */
2025 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
2027 *status
= U_MISSING_RESOURCE_ERROR
;
2030 *status
= U_MISSING_RESOURCE_ERROR
;
2033 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
2037 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2039 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2040 /* here should go a first attempt to locate the key using index table */
2041 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2042 if(U_SUCCESS(*status
)) {
2043 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
2045 *status
= U_MISSING_RESOURCE_ERROR
;
2050 *status
= U_RESOURCE_TYPE_MISMATCH
;
2055 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
2056 Resource res
= RES_BOGUS
;
2057 UResourceDataEntry
*realData
= NULL
;
2058 const char* key
= inKey
;
2060 if (status
==NULL
|| U_FAILURE(*status
)) {
2064 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2068 int32_t type
= RES_GET_TYPE(resB
->fRes
);
2069 if(URES_IS_TABLE(type
)) {
2072 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
2074 if(res
== RES_BOGUS
) {
2076 if(resB
->fHasFallback
== TRUE
) {
2077 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2078 if(U_SUCCESS(*status
)) {
2079 switch (RES_GET_TYPE(res
)) {
2081 case URES_STRING_V2
:
2082 return res_getString(rd
, 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
;
2095 *status
= U_MISSING_RESOURCE_ERROR
;
2098 *status
= U_MISSING_RESOURCE_ERROR
;
2101 switch (RES_GET_TYPE(res
)) {
2103 case URES_STRING_V2
:
2104 return res_getString(&(resB
->fResData
), res
, len
);
2107 const UChar
* result
= 0;
2108 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
2109 result
= ures_getString(tempRes
, len
, status
);
2110 ures_close(tempRes
);
2114 *status
= U_RESOURCE_TYPE_MISMATCH
;
2119 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2121 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
2122 /* here should go a first attempt to locate the key using index table */
2123 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
2124 if(U_SUCCESS(*status
)) {
2125 return res_getString(rd
, res
, len
);
2127 *status
= U_MISSING_RESOURCE_ERROR
;
2132 *status
= U_RESOURCE_TYPE_MISMATCH
;
2137 U_CAPI
const char * U_EXPORT2
2138 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
2140 char *dest
, int32_t *pLength
,
2142 UErrorCode
*status
) {
2144 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
2145 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
2148 /* TODO: clean from here down */
2151 * INTERNAL: Get the name of the first real locale (not placeholder)
2152 * that has resource bundle data.
2154 U_INTERNAL
const char* U_EXPORT2
2155 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
2157 if (status
==NULL
|| U_FAILURE(*status
)) {
2160 if (!resourceBundle
) {
2161 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2164 return resourceBundle
->fData
->fName
;
2168 U_CAPI
const char* U_EXPORT2
2169 ures_getLocale(const UResourceBundle
* resourceBundle
,
2172 return ures_getLocaleInternal(resourceBundle
, status
);
2176 U_CAPI
const char* U_EXPORT2
2177 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
2178 ULocDataLocaleType type
,
2179 UErrorCode
* status
) {
2180 if (status
==NULL
|| U_FAILURE(*status
)) {
2183 if (!resourceBundle
) {
2184 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2188 case ULOC_ACTUAL_LOCALE
:
2189 return resourceBundle
->fData
->fName
;
2190 case ULOC_VALID_LOCALE
:
2191 return resourceBundle
->fTopLevelData
->fName
;
2192 case ULOC_REQUESTED_LOCALE
:
2194 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2200 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
2205 return resB
->fData
->fName
;
2209 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2214 return resB
->fData
->fPath
;
2218 static UResourceBundle
*
2219 ures_openWithType(UResourceBundle
*r
, const char* path
, const char* localeID
,
2220 UResOpenType openType
, UErrorCode
* status
) {
2221 if(U_FAILURE(*status
)) {
2225 UResourceDataEntry
*entry
;
2226 if(openType
!= URES_OPEN_DIRECT
) {
2227 /* first "canonicalize" the locale ID */
2228 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2229 uloc_getBaseName(localeID
, canonLocaleID
, UPRV_LENGTHOF(canonLocaleID
), status
);
2230 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2231 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2234 entry
= entryOpen(path
, canonLocaleID
, openType
, status
);
2236 entry
= entryOpenDirect(path
, localeID
, status
);
2238 if(U_FAILURE(*status
)) {
2242 *status
= U_MISSING_RESOURCE_ERROR
;
2246 UBool isStackObject
;
2248 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2251 *status
= U_MEMORY_ALLOCATION_ERROR
;
2254 isStackObject
= FALSE
;
2256 isStackObject
= ures_isStackObject(r
);
2257 ures_closeBundle(r
, FALSE
);
2259 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2260 ures_setIsStackObject(r
, isStackObject
);
2262 r
->fTopLevelData
= r
->fData
= entry
;
2263 uprv_memcpy(&r
->fResData
, &entry
->fData
, sizeof(ResourceData
));
2264 r
->fHasFallback
= openType
!= URES_OPEN_DIRECT
&& !r
->fResData
.noFallback
;
2265 r
->fIsTopLevel
= TRUE
;
2266 r
->fRes
= r
->fResData
.rootRes
;
2267 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2273 U_CAPI UResourceBundle
* U_EXPORT2
2274 ures_open(const char* path
, const char* localeID
, UErrorCode
* status
) {
2275 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2278 U_CAPI UResourceBundle
* U_EXPORT2
2279 ures_openNoDefault(const char* path
, const char* localeID
, UErrorCode
* status
) {
2280 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_LOCALE_ROOT
, status
);
2284 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2285 * or sought. However, alias substitution will happen!
2287 U_CAPI UResourceBundle
* U_EXPORT2
2288 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2289 return ures_openWithType(NULL
, path
, localeID
, URES_OPEN_DIRECT
, status
);
2293 * API: This function is used to open a resource bundle
2294 * proper fallback chaining is executed while initialization.
2295 * The result is stored in cache for later fallback search.
2297 U_CAPI
void U_EXPORT2
2298 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2299 const char* localeID
, UErrorCode
* status
) {
2300 if(U_SUCCESS(*status
) && r
== NULL
) {
2301 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2304 ures_openWithType(r
, path
, localeID
, URES_OPEN_LOCALE_DEFAULT_ROOT
, status
);
2308 * API: Counts members. For arrays and tables, returns number of resources.
2309 * For strings, returns 1.
2311 U_CAPI
int32_t U_EXPORT2
2312 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2313 const char* resourceKey
,
2316 UResourceBundle resData
;
2317 ures_initStackObject(&resData
);
2318 if (status
==NULL
|| U_FAILURE(*status
)) {
2321 if(resourceBundle
== NULL
) {
2322 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2325 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2327 if(resData
.fResData
.data
!= NULL
) {
2328 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2329 ures_close(&resData
);
2332 *status
= U_MISSING_RESOURCE_ERROR
;
2333 ures_close(&resData
);
2339 * Internal function.
2340 * Return the version number associated with this ResourceBundle as a string.
2342 * @param resourceBundle The resource bundle for which the version is checked.
2343 * @return A version number string as specified in the resource bundle or its parent.
2344 * The caller does not own this string.
2345 * @see ures_getVersion
2348 U_INTERNAL
const char* U_EXPORT2
2349 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2351 if (!resourceBundle
) return NULL
;
2353 if(resourceBundle
->fVersion
== NULL
) {
2355 /* If the version ID has not been built yet, then do so. Retrieve */
2356 /* the minor version from the file. */
2357 UErrorCode status
= U_ZERO_ERROR
;
2358 int32_t minor_len
= 0;
2361 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2363 /* Determine the length of of the final version string. This is */
2364 /* the length of the major part + the length of the separator */
2365 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2368 len
= (minor_len
> 0) ? minor_len
: 1;
2370 /* Allocate the string, and build it up. */
2371 /* + 1 for zero byte */
2374 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2375 /* Check for null pointer. */
2376 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2381 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2382 resourceBundle
->fVersion
[len
] = '\0';
2385 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2389 return resourceBundle
->fVersion
;
2392 U_CAPI
const char* U_EXPORT2
2393 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2395 return ures_getVersionNumberInternal(resourceBundle
);
2398 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2401 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2404 /** Tree support functions *******************************/
2405 #define INDEX_LOCALE_NAME "res_index"
2406 #define INDEX_TAG "InstalledLocales"
2407 #define DEFAULT_TAG "default"
2409 #if defined(URES_TREE_DEBUG)
2413 typedef struct ULocalesContext
{
2414 UResourceBundle installed
;
2415 UResourceBundle curr
;
2418 static void U_CALLCONV
2419 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2420 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2421 ures_close(&ctx
->curr
);
2422 ures_close(&ctx
->installed
);
2424 uprv_free(enumerator
);
2427 static int32_t U_CALLCONV
2428 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2429 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2430 return ures_getSize(&ctx
->installed
);
2433 static const char* U_CALLCONV
2434 ures_loc_nextLocale(UEnumeration
* en
,
2435 int32_t* resultLength
,
2436 UErrorCode
* status
) {
2437 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2438 UResourceBundle
*res
= &(ctx
->installed
);
2439 UResourceBundle
*k
= NULL
;
2440 const char *result
= NULL
;
2442 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2443 result
= ures_getKey(k
);
2444 len
= (int32_t)uprv_strlen(result
);
2447 *resultLength
= len
;
2452 static void U_CALLCONV
2453 ures_loc_resetLocales(UEnumeration
* en
,
2454 UErrorCode
* /*status*/) {
2455 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2456 ures_resetIterator(res
);
2460 static const UEnumeration gLocalesEnum
= {
2463 ures_loc_closeLocales
,
2464 ures_loc_countLocales
,
2466 ures_loc_nextLocale
,
2467 ures_loc_resetLocales
2471 U_CAPI UEnumeration
* U_EXPORT2
2472 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2474 UResourceBundle
*idx
= NULL
;
2475 UEnumeration
*en
= NULL
;
2476 ULocalesContext
*myContext
= NULL
;
2478 if(U_FAILURE(*status
)) {
2481 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2482 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2483 if(!en
|| !myContext
) {
2484 *status
= U_MEMORY_ALLOCATION_ERROR
;
2486 uprv_free(myContext
);
2489 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2491 ures_initStackObject(&myContext
->installed
);
2492 ures_initStackObject(&myContext
->curr
);
2493 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2494 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2495 if(U_SUCCESS(*status
)) {
2496 #if defined(URES_TREE_DEBUG)
2497 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2498 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2500 en
->context
= myContext
;
2502 #if defined(URES_TREE_DEBUG)
2503 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2505 ures_close(&myContext
->installed
);
2506 uprv_free(myContext
);
2516 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2518 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2519 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2526 U_CAPI
int32_t U_EXPORT2
2527 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2528 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2529 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2531 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2532 char defVal
[1024] = ""; /* default value for given locale */
2533 char defLoc
[1024] = ""; /* default value for given locale */
2534 char base
[1024] = ""; /* base locale */
2537 char full
[1024] = "";
2538 UResourceBundle bund1
, bund2
;
2539 UResourceBundle
*res
= NULL
;
2540 UErrorCode subStatus
= U_ZERO_ERROR
;
2542 if(U_FAILURE(*status
)) return 0;
2543 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2544 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2547 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2548 #if defined(URES_TREE_DEBUG)
2549 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2550 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2552 ures_initStackObject(&bund1
);
2553 ures_initStackObject(&bund2
);
2556 uprv_strcpy(parent
, base
);
2557 uprv_strcpy(found
, base
);
2560 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2561 *isAvailable
= TRUE
;
2562 if (U_SUCCESS(subStatus
)) {
2563 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2565 uenum_close(locEnum
);
2568 if(U_FAILURE(subStatus
)) {
2569 *status
= subStatus
;
2574 subStatus
= U_ZERO_ERROR
;
2575 res
= ures_open(path
, parent
, &subStatus
);
2576 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2577 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2579 *isAvailable
= FALSE
;
2581 isAvailable
= NULL
; /* only want to set this the first time around */
2583 #if defined(URES_TREE_DEBUG)
2584 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2586 if(U_FAILURE(subStatus
)) {
2587 *status
= subStatus
;
2588 } else if(subStatus
== U_ZERO_ERROR
) {
2589 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2590 if(subStatus
== U_ZERO_ERROR
) {
2591 const UChar
*defUstr
;
2593 /* look for default item */
2594 #if defined(URES_TREE_DEBUG)
2595 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2596 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2598 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2599 if(U_SUCCESS(subStatus
) && defLen
) {
2600 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2601 #if defined(URES_TREE_DEBUG)
2602 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2603 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2605 uprv_strcpy(defLoc
, parent
);
2607 uprv_strcpy(kwVal
, defVal
);
2608 #if defined(URES_TREE_DEBUG)
2609 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2610 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2617 subStatus
= U_ZERO_ERROR
;
2620 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2623 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2625 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2627 /* Now, see if we can find the kwVal collator.. start the search over.. */
2628 uprv_strcpy(parent
, base
);
2629 uprv_strcpy(found
, base
);
2632 subStatus
= U_ZERO_ERROR
;
2633 res
= ures_open(path
, parent
, &subStatus
);
2634 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2635 *isAvailable
= FALSE
;
2637 isAvailable
= NULL
; /* only want to set this the first time around */
2639 #if defined(URES_TREE_DEBUG)
2640 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2641 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2643 if(U_FAILURE(subStatus
)) {
2644 *status
= subStatus
;
2645 } else if(subStatus
== U_ZERO_ERROR
) {
2646 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2647 #if defined(URES_TREE_DEBUG)
2648 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2650 if(subStatus
== U_ZERO_ERROR
) {
2651 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2652 #if defined(URES_TREE_DEBUG)
2653 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2655 if(subStatus
== U_ZERO_ERROR
) {
2656 #if defined(URES_TREE_DEBUG)
2657 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2658 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2660 uprv_strcpy(full
, parent
);
2662 uprv_strcpy(full
, "root");
2664 /* now, recalculate default kw if need be */
2665 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2666 const UChar
*defUstr
;
2668 /* look for default item */
2669 #if defined(URES_TREE_DEBUG)
2670 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2671 path
?path
:"ICUDATA", full
);
2673 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2674 if(U_SUCCESS(subStatus
) && defLen
) {
2675 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2676 #if defined(URES_TREE_DEBUG)
2677 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2678 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2680 uprv_strcpy(defLoc
, full
);
2682 } /* end of recalculate default KW */
2683 #if defined(URES_TREE_DEBUG)
2685 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2689 #if defined(URES_TREE_DEBUG)
2690 fprintf(stderr
, "err=%s in %s looking for %s\n",
2691 u_errorName(subStatus
), parent
, kwVal
);
2697 subStatus
= U_ZERO_ERROR
;
2699 uprv_strcpy(found
, parent
);
2700 uloc_getParent(found
,parent
,1023,&subStatus
);
2702 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2704 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2705 #if defined(URES_TREE_DEBUG)
2706 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2708 uprv_strcpy(kwVal
, defVal
);
2709 uprv_strcpy(parent
, base
);
2710 uprv_strcpy(found
, base
);
2712 do { /* search for 'default' named item */
2713 subStatus
= U_ZERO_ERROR
;
2714 res
= ures_open(path
, parent
, &subStatus
);
2715 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2716 *isAvailable
= FALSE
;
2718 isAvailable
= NULL
; /* only want to set this the first time around */
2720 #if defined(URES_TREE_DEBUG)
2721 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2722 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2724 if(U_FAILURE(subStatus
)) {
2725 *status
= subStatus
;
2726 } else if(subStatus
== U_ZERO_ERROR
) {
2727 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2728 if(subStatus
== U_ZERO_ERROR
) {
2729 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2730 if(subStatus
== U_ZERO_ERROR
) {
2731 #if defined(URES_TREE_DEBUG)
2732 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2733 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2735 uprv_strcpy(full
, parent
);
2737 uprv_strcpy(full
, "root");
2740 /* now, recalculate default kw if need be */
2741 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2742 const UChar
*defUstr
;
2744 /* look for default item */
2745 #if defined(URES_TREE_DEBUG)
2746 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2747 path
?path
:"ICUDATA", full
);
2749 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2750 if(U_SUCCESS(subStatus
) && defLen
) {
2751 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2752 #if defined(URES_TREE_DEBUG)
2753 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2754 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2756 uprv_strcpy(defLoc
, full
);
2758 } /* end of recalculate default KW */
2759 #if defined(URES_TREE_DEBUG)
2761 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2767 subStatus
= U_ZERO_ERROR
;
2769 uprv_strcpy(found
, parent
);
2770 uloc_getParent(found
,parent
,1023,&subStatus
);
2772 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2775 if(U_SUCCESS(*status
)) {
2777 #if defined(URES_TREE_DEBUG)
2778 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2780 *status
= U_MISSING_RESOURCE_ERROR
;
2781 } else if(omitDefault
) {
2782 #if defined(URES_TREE_DEBUG)
2783 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2785 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2786 /* found the keyword in a *child* of where the default tag was present. */
2787 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2788 /* and the default is in or in an ancestor of the current locale */
2789 #if defined(URES_TREE_DEBUG)
2790 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2796 uprv_strcpy(found
, full
);
2798 uprv_strcat(found
, "@");
2799 uprv_strcat(found
, keyword
);
2800 uprv_strcat(found
, "=");
2801 uprv_strcat(found
, kwVal
);
2802 } else if(!omitDefault
) {
2803 uprv_strcat(found
, "@");
2804 uprv_strcat(found
, keyword
);
2805 uprv_strcat(found
, "=");
2806 uprv_strcat(found
, defVal
);
2809 /* we found the default locale - no need to repeat it.*/
2814 length
= (int32_t)uprv_strlen(found
);
2816 if(U_SUCCESS(*status
)) {
2817 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2819 uprv_strncpy(result
, found
, copyLength
);
2822 *status
= U_MISSING_RESOURCE_ERROR
;
2828 return u_terminateChars(result
, resultCapacity
, length
, status
);
2831 U_CAPI UEnumeration
* U_EXPORT2
2832 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2834 #define VALUES_BUF_SIZE 2048
2835 #define VALUES_LIST_SIZE 512
2837 char valuesBuf
[VALUES_BUF_SIZE
];
2838 int32_t valuesIndex
= 0;
2839 const char *valuesList
[VALUES_LIST_SIZE
];
2840 int32_t valuesCount
= 0;
2845 UEnumeration
*locs
= NULL
;
2847 UResourceBundle item
;
2848 UResourceBundle subItem
;
2850 ures_initStackObject(&item
);
2851 ures_initStackObject(&subItem
);
2852 locs
= ures_openAvailableLocales(path
, status
);
2854 if(U_FAILURE(*status
)) {
2856 ures_close(&subItem
);
2863 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2864 UResourceBundle
*bund
= NULL
;
2865 UResourceBundle
*subPtr
= NULL
;
2866 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2867 bund
= ures_openDirect(path
, locale
, &subStatus
);
2869 #if defined(URES_TREE_DEBUG)
2870 if(!bund
|| U_FAILURE(subStatus
)) {
2871 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2872 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2876 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2878 if(!bund
|| U_FAILURE(subStatus
)) {
2879 #if defined(URES_TREE_DEBUG)
2880 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2881 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2888 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2889 && U_SUCCESS(subStatus
)) {
2892 k
= ures_getKey(subPtr
);
2894 #if defined(URES_TREE_DEBUG)
2895 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2897 if(k
== NULL
|| *k
== 0 ||
2898 uprv_strcmp(k
, DEFAULT_TAG
) == 0 || uprv_strncmp(k
, "private-", 8) == 0) {
2899 // empty or "default" or unlisted type
2902 for(i
=0; i
<valuesCount
; i
++) {
2903 if(!uprv_strcmp(valuesList
[i
],k
)) {
2904 k
= NULL
; /* found duplicate */
2909 int32_t kLen
= (int32_t)uprv_strlen(k
);
2910 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2911 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2912 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2914 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2915 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2916 valuesIndex
+= kLen
;
2917 #if defined(URES_TREE_DEBUG)
2918 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2919 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2921 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2927 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2930 ures_close(&subItem
);
2932 #if defined(URES_TREE_DEBUG)
2933 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2934 valuesIndex
, valuesCount
);
2936 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2939 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2940 U_INTERNAL UBool U_EXPORT2
2941 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2942 if(res1
==NULL
|| res2
==NULL
){
2943 return res1
==res2
; /* pointer comparision */
2945 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2946 return (res1
->fKey
==res2
->fKey
);
2948 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2952 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2955 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2956 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2958 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2962 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2965 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2968 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2971 if(res1
->fRes
!= res2
->fRes
){
2976 U_INTERNAL UResourceBundle
* U_EXPORT2
2977 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2978 UResourceBundle
* bundle
= NULL
;
2979 UResourceBundle
* ret
= NULL
;
2980 if(U_FAILURE(*status
) || res
== NULL
){
2983 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2984 if(res
->fResPath
!=NULL
){
2985 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2992 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2993 ures_getParentBundle(const UResourceBundle
* res
){
2997 return res
->fParentRes
;
3001 U_INTERNAL
void U_EXPORT2
3002 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
3005 str
= ures_getStringByKey(res
, key
, &len
, status
);
3006 if(U_SUCCESS(*status
)) {
3007 u_versionFromUString(ver
, str
);