2 ******************************************************************************
3 * Copyright (C) 1997-2012, 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"
31 #include "unicode/uenum.h"
39 Static cache for already opened resource bundles - mostly for keeping fallback info
40 TODO: This cache should probably be removed when the deprecated code is
43 static UHashtable
*cache
= NULL
;
45 static UMTX resbMutex
= NULL
;
47 /* INTERNAL: hashes an entry */
48 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
49 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
50 UHashTok namekey
, pathkey
;
51 namekey
.pointer
= b
->fName
;
52 pathkey
.pointer
= b
->fPath
;
53 return uhash_hashChars(namekey
)+37*uhash_hashChars(pathkey
);
56 /* INTERNAL: compares two entries */
57 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
58 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
59 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
60 UHashTok name1
, name2
, path1
, path2
;
61 name1
.pointer
= b1
->fName
;
62 name2
.pointer
= b2
->fName
;
63 path1
.pointer
= b1
->fPath
;
64 path2
.pointer
= b2
->fPath
;
65 return (UBool
)(uhash_compareChars(name1
, name2
) &&
66 uhash_compareChars(path1
, path2
));
71 * Internal function, gets parts of locale name according
72 * to the position of '_' character
74 static UBool
chopLocale(char *name
) {
75 char *i
= uprv_strrchr(name
, '_');
88 static void entryIncrease(UResourceDataEntry
*entry
) {
89 umtx_lock(&resbMutex
);
90 entry
->fCountExisting
++;
91 while(entry
->fParent
!= NULL
) {
92 entry
= entry
->fParent
;
93 entry
->fCountExisting
++;
95 umtx_unlock(&resbMutex
);
99 * Internal function. Tries to find a resource in given Resource
100 * Bundle, as well as in its parents
102 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
103 UResourceDataEntry
*resB
= resBundle
->fData
;
108 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
109 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
112 if(resBundle
->fHasFallback
== TRUE
) {
113 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
114 resB
= resB
->fParent
;
115 if(resB
->fBogus
== U_ZERO_ERROR
) {
117 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
122 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
124 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
125 *status
= U_USING_DEFAULT_WARNING
;
127 *status
= U_USING_FALLBACK_WARNING
;
131 return (&(resB
->fData
));
132 } else { /* If resource is not found, we need to give an error */
133 *status
= U_MISSING_RESOURCE_ERROR
;
137 *status
= U_MISSING_RESOURCE_ERROR
;
143 free_entry(UResourceDataEntry
*entry
) {
144 UResourceDataEntry
*alias
;
145 res_unload(&(entry
->fData
));
146 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
147 uprv_free(entry
->fName
);
149 if(entry
->fPath
!= NULL
) {
150 uprv_free(entry
->fPath
);
152 if(entry
->fPool
!= NULL
) {
153 --entry
->fPool
->fCountExisting
;
155 alias
= entry
->fAlias
;
157 while(alias
->fAlias
!= NULL
) {
158 alias
= alias
->fAlias
;
160 --alias
->fCountExisting
;
165 /* Works just like ucnv_flushCache() */
166 static int32_t ures_flushCache()
168 UResourceDataEntry
*resB
;
170 int32_t rbDeletedNum
= 0;
171 const UHashElement
*e
;
174 /*if shared data hasn't even been lazy evaluated yet
177 umtx_lock(&resbMutex
);
179 umtx_unlock(&resbMutex
);
185 /*creates an enumeration to iterate through every element in the table */
187 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
189 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
190 /* Deletes only if reference counter == 0
191 * Don't worry about the children of this node.
192 * Those will eventually get deleted too, if not already.
193 * Don't worry about the parents of this node.
194 * Those will eventually get deleted too, if not already.
196 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
197 /* some resource bundles are still open somewhere. */
199 if (resB
->fCountExisting
== 0) {
202 uhash_removeElement(cache
, e
);
207 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
208 * got decremented by free_entry().
210 } while(deletedMore
);
211 umtx_unlock(&resbMutex
);
219 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
220 UBool cacheNotEmpty
= FALSE
;
222 const UHashElement
*e
;
223 UResourceDataEntry
*resB
;
225 umtx_lock(&resbMutex
);
227 umtx_unlock(&resbMutex
);
228 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
232 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
234 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
235 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
237 (void*)resB
, resB
->fCountExisting
,
238 resB
->fName
?resB
->fName
:"NULL",
239 resB
->fPath
?resB
->fPath
:"NULL",
242 (void*)resB
->fParent
);
245 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
247 umtx_unlock(&resbMutex
);
249 return cacheNotEmpty
;
254 static UBool U_CALLCONV
ures_cleanup(void)
258 if (cache
!= NULL
&& uhash_count(cache
) == 0) {
263 if (cache
== NULL
&& resbMutex
!= NULL
) {
264 umtx_destroy(&resbMutex
);
266 return (cache
== NULL
);
269 /** INTERNAL: Initializes the cache for resources */
270 static void initCache(UErrorCode
*status
) {
271 UBool makeCache
= FALSE
;
272 UMTX_CHECK(&resbMutex
, (cache
== NULL
), makeCache
);
274 UHashtable
*newCache
= uhash_open(hashEntry
, compareEntries
, NULL
, status
);
275 if (U_FAILURE(*status
)) {
278 umtx_lock(&resbMutex
);
282 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
284 umtx_unlock(&resbMutex
);
285 if(newCache
!= NULL
) {
286 uhash_close(newCache
);
291 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
293 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
294 int32_t len
= (int32_t)uprv_strlen(name
);
295 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
296 uprv_free(res
->fName
);
298 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
299 res
->fName
= res
->fNameBuffer
;
302 res
->fName
= (char *)uprv_malloc(len
+1);
304 if(res
->fName
== NULL
) {
305 *status
= U_MEMORY_ALLOCATION_ERROR
;
307 uprv_strcpy(res
->fName
, name
);
311 static UResourceDataEntry
*
312 getPoolEntry(const char *path
, UErrorCode
*status
);
315 * INTERNAL: Inits and opens an entry from a data DLL.
316 * CAUTION: resbMutex must be locked when calling this function.
318 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
319 UResourceDataEntry
*r
= NULL
;
320 UResourceDataEntry find
;
321 /*int32_t hashValue;*/
323 char aliasName
[100] = { 0 };
324 int32_t aliasLen
= 0;
325 /*UBool isAlias = FALSE;*/
326 /*UHashTok hashkey; */
328 if(U_FAILURE(*status
)) {
332 /* here we try to deduce the right locale name */
333 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
334 name
= uloc_getDefault();
335 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
336 name
= kRootLocaleName
;
337 } else { /* otherwise, we'll open what we're given */
341 find
.fName
= (char *)name
;
342 find
.fPath
= (char *)path
;
344 /* calculate the hash value of the entry */
345 /*hashkey.pointer = (void *)&find;*/
346 /*hashValue = hashEntry(hashkey);*/
348 /* check to see if we already have this entry */
349 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
351 /* if the entry is not yet in the hash table, we'll try to construct a new one */
352 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
354 *status
= U_MEMORY_ALLOCATION_ERROR
;
358 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
359 /*r->fHashKey = hashValue;*/
361 setEntryName(r
, name
, status
);
362 if (U_FAILURE(*status
)) {
368 r
->fPath
= (char *)uprv_strdup(path
);
369 if(r
->fPath
== NULL
) {
370 *status
= U_MEMORY_ALLOCATION_ERROR
;
376 /* this is the actual loading */
377 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
379 if (U_FAILURE(*status
)) {
380 /* we have no such entry in dll, so it will always use fallback */
381 *status
= U_USING_FALLBACK_WARNING
;
382 r
->fBogus
= U_USING_FALLBACK_WARNING
;
383 } else { /* if we have a regular entry */
385 if (r
->fData
.usesPoolBundle
) {
386 r
->fPool
= getPoolEntry(r
->fPath
, status
);
387 if (U_SUCCESS(*status
)) {
388 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
389 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
390 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
392 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
398 if (U_SUCCESS(*status
)) {
399 /* handle the alias by trying to get out the %%Alias tag.*/
400 /* We'll try to get alias string from the bundle */
401 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
402 if (aliasres
!= RES_BOGUS
) {
403 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
404 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
405 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
406 r
->fAlias
= init_entry(aliasName
, path
, status
);
413 UResourceDataEntry
*oldR
= NULL
;
414 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
415 /* just insert it in the cache */
416 UErrorCode cacheStatus
= U_ZERO_ERROR
;
417 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
418 if (U_FAILURE(cacheStatus
)) {
419 *status
= cacheStatus
;
424 /* somebody have already inserted it while we were working, discard newly opened data */
425 /* Also, we could get here IF we opened an alias */
433 /* return the real bundle */
434 while(r
->fAlias
!= NULL
) {
437 r
->fCountExisting
++; /* we increase its reference count */
438 /* if the resource has a warning */
439 /* we don't want to overwrite a status with no error */
440 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
441 *status
= r
->fBogus
; /* set the returning status */
447 static UResourceDataEntry
*
448 getPoolEntry(const char *path
, UErrorCode
*status
) {
449 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
450 if( U_SUCCESS(*status
) &&
451 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
453 *status
= U_INVALID_FORMAT_ERROR
;
459 /* CAUTION: resbMutex must be locked when calling this function! */
460 static UResourceDataEntry
*findFirstExisting(const char* path
, char* name
, UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
461 UResourceDataEntry
*r
= NULL
;
462 UBool hasRealData
= FALSE
;
463 const char *defaultLoc
= uloc_getDefault();
464 *hasChopped
= TRUE
; /* we're starting with a fresh name */
466 while(*hasChopped
&& !hasRealData
) {
467 r
= init_entry(name
, path
, status
);
468 /* Null pointer test */
469 if (U_FAILURE(*status
)) {
472 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
473 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
475 /* this entry is not real. We will discard it. */
476 /* However, the parent line for this entry is */
477 /* not to be used - as there might be parent */
478 /* lines in cache from previous openings that */
479 /* are not updated yet. */
481 /*entryCloseInt(r);*/
483 *status
= U_USING_FALLBACK_WARNING
;
485 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
488 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
490 /*Fallback data stuff*/
491 *hasChopped
= chopLocale(name
);
496 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
501 resB
->fMagic1
= MAGIC1
;
502 resB
->fMagic2
= MAGIC2
;
506 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
507 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
511 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
512 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
513 ures_setIsStackObject(resB
, TRUE
);
516 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
, UErrorCode
* status
) {
517 UErrorCode intStatus
= U_ZERO_ERROR
;
518 UErrorCode parentStatus
= U_ZERO_ERROR
;
519 UErrorCode usrStatus
= U_ZERO_ERROR
;
520 UResourceDataEntry
*r
= NULL
;
521 UResourceDataEntry
*t1
= NULL
;
522 UResourceDataEntry
*t2
= NULL
;
523 UResourceDataEntry
*u1
= NULL
;
524 UResourceDataEntry
*u2
= NULL
;
525 UBool isDefault
= FALSE
;
526 UBool isRoot
= FALSE
;
527 UBool hasRealData
= FALSE
;
528 UBool hasChopped
= TRUE
;
529 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
531 char name
[ULOC_FULLNAME_CAPACITY
];
532 char usrDataPath
[96];
536 if(U_FAILURE(*status
)) {
540 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
541 name
[sizeof(name
) - 1] = 0;
543 if ( usingUSRData
) {
544 if ( path
== NULL
) {
545 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
547 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
548 usrDataPath
[0] = 'u';
549 usrDataPath
[1] = 's';
550 usrDataPath
[2] = 'r';
551 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
555 umtx_lock(&resbMutex
);
557 /* We're going to skip all the locales that do not have any data */
558 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
560 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
563 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
564 u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
566 if(u1
->fBogus
== U_ZERO_ERROR
) {
570 /* the USR override data wasn't found, set it to be deleted */
571 u1
->fCountExisting
= 0;
575 while (hasChopped
&& !isRoot
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
) {
576 if ( res_getResource(&t1
->fData
,"%%Parent") != RES_BOGUS
) { /* An explicit parent was found */
577 int32_t parentLocaleLen
= 0;
578 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), res_getResource(&t1
->fData
,"%%Parent") , &parentLocaleLen
);
579 if(parentLocaleName
!= NULL
&& parentLocaleLen
> 0) {
580 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+1);
581 if ( !uprv_strcmp(name
,"root") ) { /* If parent is root, we just terminate the loop */
587 /* insert regular parents */
588 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
589 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
590 usrStatus
= U_ZERO_ERROR
;
591 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
593 /* Check for null pointer. */
594 if (t2
== NULL
|| ( usingUSRData
&& u2
== NULL
)) {
595 *status
= U_MEMORY_ALLOCATION_ERROR
;
599 if ( usingUSRData
&& u2
->fBogus
== U_ZERO_ERROR
) {
605 /* the USR override data wasn't found, set it to be deleted */
606 u2
->fCountExisting
= 0;
610 hasChopped
= chopLocale(name
);
614 /* we could have reached this point without having any real data */
615 /* if that is the case, we need to chain in the default locale */
616 if(r
==NULL
&& !isDefault
&& !isRoot
/*&& t1->fParent == NULL*/) {
617 /* insert default locale */
618 uprv_strcpy(name
, uloc_getDefault());
619 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
620 intStatus
= U_USING_DEFAULT_WARNING
;
621 if(r
!= NULL
) { /* the default locale exists */
625 while (hasChopped
&& t1
->fParent
== NULL
) {
626 if ( res_getResource(&t1
->fData
,"%%Parent") != RES_BOGUS
) { /* An explicit parent was found */
627 int32_t parentLocaleLen
= 0;
628 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), res_getResource(&t1
->fData
,"%%Parent") , &parentLocaleLen
);
629 if(parentLocaleName
!= NULL
&& parentLocaleLen
> 0) {
630 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+1);
631 if ( !uprv_strcmp(name
,"root") ) { /* If parent is root, we just terminate the loop */
637 /* insert chopped defaults */
638 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
639 /* Check for null pointer. */
641 *status
= U_MEMORY_ALLOCATION_ERROR
;
645 if ( res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
649 hasChopped
= chopLocale(name
);
654 /* we could still have r == NULL at this point - maybe even default locale is not */
657 uprv_strcpy(name
, kRootLocaleName
);
658 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
661 intStatus
= U_USING_DEFAULT_WARNING
;
663 } else { /* we don't even have the root locale */
664 *status
= U_MISSING_RESOURCE_ERROR
;
667 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
668 /* insert root locale */
669 t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
670 /* Check for null pointer. */
672 *status
= U_MEMORY_ALLOCATION_ERROR
;
676 r
->fBogus
= U_USING_DEFAULT_WARNING
;
678 hasRealData
= (UBool
)((t2
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
683 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
684 t1
->fParent
->fCountExisting
++;
686 hasRealData
= (UBool
)((t1
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
690 umtx_unlock(&resbMutex
);
692 if(U_SUCCESS(*status
)) {
693 if(U_SUCCESS(parentStatus
)) {
694 if(intStatus
!= U_ZERO_ERROR
) {
699 *status
= parentStatus
;
709 * Functions to create and destroy resource bundles.
710 * CAUTION: resbMutex must be locked when calling this function.
713 static void entryCloseInt(UResourceDataEntry
*resB
) {
714 UResourceDataEntry
*p
= resB
;
716 while(resB
!= NULL
) {
718 resB
->fCountExisting
--;
720 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
723 if(resB->fCountExisting <= 0) {
724 uhash_remove(cache, resB);
725 if(resB->fBogus == U_ZERO_ERROR) {
726 res_unload(&(resB->fData));
728 if(resB->fName != NULL) {
729 uprv_free(resB->fName);
731 if(resB->fPath != NULL) {
732 uprv_free(resB->fPath);
743 * API: closes a resource bundle and cleans up.
746 static void entryClose(UResourceDataEntry
*resB
) {
747 umtx_lock(&resbMutex
);
749 umtx_unlock(&resbMutex
);
753 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
754 if(resB->fResPath == NULL) {
755 resB->fResPath = resB->fResBuf;
756 *(resB->fResPath) = 0;
758 resB->fResPathLen = uprv_strlen(toAdd);
759 if(RES_BUFSIZE <= resB->fResPathLen+1) {
760 if(resB->fResPath == resB->fResBuf) {
761 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
763 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
766 uprv_strcpy(resB->fResPath, toAdd);
769 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
770 int32_t resPathLenOrig
= resB
->fResPathLen
;
771 if(resB
->fResPath
== NULL
) {
772 resB
->fResPath
= resB
->fResBuf
;
773 *(resB
->fResPath
) = 0;
774 resB
->fResPathLen
= 0;
776 resB
->fResPathLen
+= lenToAdd
;
777 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
778 if(resB
->fResPath
== resB
->fResBuf
) {
779 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
780 /* Check that memory was allocated correctly. */
781 if (resB
->fResPath
== NULL
) {
782 *status
= U_MEMORY_ALLOCATION_ERROR
;
785 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
787 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
788 /* Check that memory was reallocated correctly. */
790 *status
= U_MEMORY_ALLOCATION_ERROR
;
793 resB
->fResPath
= temp
;
796 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
799 static void ures_freeResPath(UResourceBundle
*resB
) {
800 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
801 uprv_free(resB
->fResPath
);
803 resB
->fResPath
= NULL
;
804 resB
->fResPathLen
= 0;
808 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
811 if(resB
->fData
!= NULL
) {
812 entryClose(resB
->fData
);
814 if(resB
->fVersion
!= NULL
) {
815 uprv_free(resB
->fVersion
);
817 ures_freeResPath(resB
);
819 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
824 /* poison the data */
825 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
831 U_CAPI
void U_EXPORT2
832 ures_close(UResourceBundle
* resB
)
834 ures_closeBundle(resB
, TRUE
);
837 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
838 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
839 const UResourceBundle
*parent
, int32_t noAlias
,
840 UResourceBundle
*resB
, UErrorCode
*status
)
842 if(status
== NULL
|| U_FAILURE(*status
)) {
845 if (parent
== NULL
) {
846 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
849 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
850 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
852 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
854 /* we have an alias, now let's cut it up */
855 char stackAlias
[200];
856 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
860 * Allocate enough space for both the char * version
861 * of the alias and parent->fResPath.
863 * We do this so that res_findResource() can modify the path,
864 * which allows us to remove redundant _res_findResource() variants
866 * res_findResource() now NUL-terminates each segment so that table keys
867 * can always be compared with strcmp() instead of strncmp().
868 * Saves code there and simplifies testing and code coverage.
872 ++len
; /* count the terminating NUL */
873 if(parent
->fResPath
!= NULL
) {
874 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
881 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
882 capacity
= (int32_t)sizeof(stackAlias
);
883 chAlias
= stackAlias
;
885 chAlias
= (char *)uprv_malloc(capacity
);
887 if(chAlias
== NULL
) {
888 *status
= U_MEMORY_ALLOCATION_ERROR
;
892 u_UCharsToChars(alias
, chAlias
, len
);
894 if(*chAlias
== RES_PATH_SEPARATOR
) {
895 /* there is a path included */
896 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
898 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
904 if(uprv_strcmp(path
, "LOCALE") == 0) {
905 /* this is an XPath alias, starting with "/LOCALE/" */
906 /* it contains the path to a resource which should be looked up */
907 /* starting in the requested locale */
909 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
910 path
= realData
->fPath
; /* we will be looking in the same package */
912 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
915 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
922 /* no path, start with a locale */
924 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
929 path
= realData
->fPath
;
934 /* got almost everything, let's try to open */
935 /* first, open the bundle with real data */
936 UResourceBundle
*result
= resB
;
937 const char* temp
= NULL
;
938 UErrorCode intStatus
= U_ZERO_ERROR
;
939 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
940 if(U_SUCCESS(intStatus
)) {
941 if(keyPath
== NULL
) {
942 /* no key path. This means that we are going to
943 * to use the corresponding resource from
946 /* first, we are going to get a corresponding parent
947 * resource to the one we are searching.
949 char *aKey
= parent
->fResPath
;
951 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
953 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
958 /* we need to make keyPath from parent's fResPath and
959 * current key, if there is a key associated
961 len
= (int32_t)(uprv_strlen(key
) + 1);
964 if(chAlias
== stackAlias
) {
965 chAlias
= (char *)uprv_malloc(capacity
);
967 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
969 if(chAlias
== NULL
) {
971 *status
= U_MEMORY_ALLOCATION_ERROR
;
975 uprv_memcpy(chAlias
, key
, len
);
977 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
978 } else if(idx
!= -1) {
979 /* if there is no key, but there is an index, try to get by the index */
980 /* here we have either a table or an array, so get the element */
981 int32_t type
= RES_GET_TYPE(r
);
982 if(URES_IS_TABLE(type
)) {
983 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
985 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
989 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
991 *status
= U_MISSING_RESOURCE_ERROR
;
995 /* this one is a bit trickier.
996 * we start finding keys, but after we resolve one alias, the path might continue.
998 * aliastest:alias { "testtypes/anotheralias/Sequence" }
999 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1000 * aliastest resource should finally have the sequence, not collation elements.
1002 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1003 char stackPath
[URES_MAX_BUFFER_SIZE
];
1004 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1005 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
1006 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1007 if(pathBuf
== NULL
) {
1008 *status
= U_MEMORY_ALLOCATION_ERROR
;
1012 uprv_strcpy(pathBuf
, keyPath
);
1014 /* now we have fallback following here */
1016 r
= dataEntry
->fData
.rootRes
;
1017 /* this loop handles 'found' resources over several levels */
1018 while(*myPath
&& U_SUCCESS(*status
)) {
1019 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1020 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1021 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1024 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1025 dataEntry
= result
->fData
;
1027 } else { /* no resource found, we don't really want to look anymore on this level */
1031 dataEntry
= dataEntry
->fParent
;
1032 uprv_strcpy(pathBuf
, keyPath
);
1034 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1035 if(r
== RES_BOGUS
) {
1036 *status
= U_MISSING_RESOURCE_ERROR
;
1039 if(pathBuf
!= stackPath
) {
1043 } else { /* we failed to open the resource we're aliasing to */
1044 *status
= intStatus
;
1046 if(chAlias
!= stackAlias
) {
1049 if(mainRes
!= result
) {
1050 ures_close(mainRes
);
1055 /* bad alias, should be an error */
1056 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1060 *status
= U_TOO_MANY_ALIASES_ERROR
;
1065 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1068 *status
= U_MEMORY_ALLOCATION_ERROR
;
1071 ures_setIsStackObject(resB
, FALSE
);
1072 resB
->fResPath
= NULL
;
1073 resB
->fResPathLen
= 0;
1075 if(resB
->fData
!= NULL
) {
1076 entryClose(resB
->fData
);
1078 if(resB
->fVersion
!= NULL
) {
1079 uprv_free(resB
->fVersion
);
1082 weiv: if stack object was passed in, it doesn't really need to be reinited,
1083 since the purpose of initing is to remove stack junk. However, at this point
1084 we would not do anything to an allocated object, so stack object should be
1088 if(ures_isStackObject(resB) != FALSE) {
1089 ures_initStackObject(resB);
1092 if(parent
!= resB
) {
1093 ures_freeResPath(resB
);
1096 resB
->fData
= realData
;
1097 entryIncrease(resB
->fData
);
1098 resB
->fHasFallback
= FALSE
;
1099 resB
->fIsTopLevel
= FALSE
;
1102 /*resB->fParentRes = parent;*/
1103 resB
->fTopLevelData
= parent
->fTopLevelData
;
1104 if(parent
->fResPath
&& parent
!= resB
) {
1105 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1108 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1109 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1110 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1112 } else if(idx
>= 0) {
1114 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1115 ures_appendResPath(resB
, buf
, len
, status
);
1116 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1117 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1120 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1122 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1123 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1126 resB
->fVersion
= NULL
;
1128 /*resB->fParent = parent->fRes;*/
1129 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1130 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1134 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1135 UBool isStackObject
;
1136 if(U_FAILURE(*status
) || r
== original
) {
1139 if(original
!= NULL
) {
1141 isStackObject
= FALSE
;
1142 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1145 *status
= U_MEMORY_ALLOCATION_ERROR
;
1149 isStackObject
= ures_isStackObject(r
);
1150 ures_closeBundle(r
, FALSE
);
1152 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1155 if(original
->fResPath
) {
1156 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1158 ures_setIsStackObject(r
, isStackObject
);
1159 if(r
->fData
!= NULL
) {
1160 entryIncrease(r
->fData
);
1167 * Functions to retrieve data from resource bundles.
1170 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1172 if (status
==NULL
|| U_FAILURE(*status
)) {
1176 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1179 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1181 *status
= U_RESOURCE_TYPE_MISMATCH
;
1187 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1188 char *dest
, int32_t *pLength
,
1190 UErrorCode
*status
) {
1193 if (U_FAILURE(*status
)) {
1196 if (pLength
!= NULL
) {
1197 capacity
= *pLength
;
1201 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1202 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1206 if (length16
== 0) {
1207 /* empty string, return as read-only pointer */
1208 if (pLength
!= NULL
) {
1212 u_terminateChars(dest
, capacity
, 0, status
);
1218 /* We need to transform the string to the destination buffer. */
1219 if (capacity
< length16
) {
1220 /* No chance for the string to fit. Pure preflighting. */
1221 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1223 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1225 * We know the string will fit into dest because each UChar turns
1226 * into at most three UTF-8 bytes. Fill the latter part of dest
1227 * so that callers do not expect to use dest as a string pointer,
1228 * hopefully leading to more robust code for when resource bundles
1229 * may store UTF-8 natively.
1230 * (In which case dest would not be used at all.)
1232 * We do not do this if forceCopy=TRUE because then the caller
1233 * expects the string to start exactly at dest.
1235 * The test above for <= 0x2aaaaaaa prevents overflows.
1236 * The +1 is for the NUL terminator.
1238 int32_t maxLength
= 3 * length16
+ 1;
1239 if (capacity
> maxLength
) {
1240 dest
+= capacity
- maxLength
;
1241 capacity
= maxLength
;
1244 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1248 U_CAPI
const char * U_EXPORT2
1249 ures_getUTF8String(const UResourceBundle
*resB
,
1250 char *dest
, int32_t *pLength
,
1252 UErrorCode
*status
) {
1254 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1255 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1258 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1259 UErrorCode
* status
) {
1261 if (status
==NULL
|| U_FAILURE(*status
)) {
1265 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1268 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1270 *status
= U_RESOURCE_TYPE_MISMATCH
;
1275 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1276 UErrorCode
* status
) {
1278 if (status
==NULL
|| U_FAILURE(*status
)) {
1282 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1285 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1287 *status
= U_RESOURCE_TYPE_MISMATCH
;
1292 /* this function returns a signed integer */
1293 /* it performs sign extension */
1294 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1295 if (status
==NULL
|| U_FAILURE(*status
)) {
1299 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1302 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1303 *status
= U_RESOURCE_TYPE_MISMATCH
;
1306 return RES_GET_INT(resB
->fRes
);
1309 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1310 if (status
==NULL
|| U_FAILURE(*status
)) {
1314 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1317 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1318 *status
= U_RESOURCE_TYPE_MISMATCH
;
1321 return RES_GET_UINT(resB
->fRes
);
1324 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1328 return res_getPublicType(resB
->fRes
);
1331 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1339 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1347 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1348 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1349 const UChar
* result
= 0;
1350 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1351 result
= ures_getString(tempRes
, len
, status
);
1352 ures_close(tempRes
);
1355 return res_getString(&(resB
->fResData
), r
, len
);
1359 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1366 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1370 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1373 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1374 Resource r
= RES_BOGUS
;
1376 if (status
==NULL
|| U_FAILURE(*status
)) {
1380 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1384 if(resB
->fIndex
== resB
->fSize
-1) {
1385 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1388 switch(RES_GET_TYPE(resB
->fRes
)) {
1390 case URES_STRING_V2
:
1391 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1395 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1396 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1397 /* TODO: do the fallback */
1399 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1402 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1403 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1404 /* TODO: do the fallback */
1406 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1408 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1411 case URES_INT_VECTOR
:
1412 *status
= U_RESOURCE_TYPE_MISMATCH
;
1413 default: /*fall through*/
1421 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1422 const char *key
= NULL
;
1423 Resource r
= RES_BOGUS
;
1425 if (status
==NULL
|| U_FAILURE(*status
)) {
1430 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1435 if(resB
->fIndex
== resB
->fSize
-1) {
1436 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1440 switch(RES_GET_TYPE(resB
->fRes
)) {
1444 case URES_STRING_V2
:
1445 case URES_INT_VECTOR
:
1446 return ures_copyResb(fillIn
, resB
, status
);
1450 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1451 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1452 /* TODO: do the fallback */
1454 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1457 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1458 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1459 /* TODO: do the fallback */
1461 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1471 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1472 const char* key
= NULL
;
1473 Resource r
= RES_BOGUS
;
1475 if (status
==NULL
|| U_FAILURE(*status
)) {
1480 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1485 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1486 switch(RES_GET_TYPE(resB
->fRes
)) {
1490 case URES_STRING_V2
:
1491 case URES_INT_VECTOR
:
1492 return ures_copyResb(fillIn
, resB
, status
);
1496 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1497 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1498 /* TODO: do the fallback */
1500 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1503 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1504 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1505 /* TODO: do the fallback */
1507 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1513 *status
= U_MISSING_RESOURCE_ERROR
;
1519 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1520 const char* key
= NULL
;
1521 Resource r
= RES_BOGUS
;
1523 if (status
==NULL
|| U_FAILURE(*status
)) {
1527 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1531 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1532 switch(RES_GET_TYPE(resB
->fRes
)) {
1534 case URES_STRING_V2
:
1535 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1539 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1540 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1541 /* TODO: do the fallback */
1543 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1546 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1547 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1548 /* TODO: do the fallback */
1550 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1552 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1555 case URES_INT_VECTOR
:
1556 *status
= U_RESOURCE_TYPE_MISMATCH
;
1559 /* must not occur */
1560 *status
= U_INTERNAL_PROGRAM_ERROR
;
1564 *status
= U_MISSING_RESOURCE_ERROR
;
1569 U_CAPI
const char * U_EXPORT2
1570 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1572 char *dest
, int32_t *pLength
,
1574 UErrorCode
*status
) {
1576 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1577 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1580 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1581 return resB->fResPath;
1584 U_CAPI UResourceBundle
* U_EXPORT2
1585 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1587 UResourceBundle
*first
= NULL
;
1588 UResourceBundle
*result
= fillIn
;
1589 char *packageName
= NULL
;
1590 char *pathToResource
= NULL
, *save
= NULL
;
1591 char *locale
= NULL
, *localeEnd
= NULL
;
1594 if(status
== NULL
|| U_FAILURE(*status
)) {
1598 length
= (int32_t)(uprv_strlen(path
)+1);
1599 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1601 if(pathToResource
== NULL
) {
1602 *status
= U_MEMORY_ALLOCATION_ERROR
;
1605 uprv_memcpy(pathToResource
, path
, length
);
1607 locale
= pathToResource
;
1608 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1610 packageName
= pathToResource
;
1611 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1612 if(pathToResource
== NULL
) {
1613 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1615 *pathToResource
= 0;
1616 locale
= pathToResource
+1;
1620 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1621 if(localeEnd
!= NULL
) {
1625 first
= ures_open(packageName
, locale
, status
);
1627 if(U_SUCCESS(*status
)) {
1629 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1631 result
= ures_copyResb(fillIn
, first
, status
);
1639 U_CAPI UResourceBundle
* U_EXPORT2
1640 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1642 Resource res
= RES_BOGUS
;
1643 UResourceBundle
*result
= fillIn
;
1646 if(status
== NULL
|| U_FAILURE(*status
)) {
1650 /* here we do looping and circular alias checking */
1651 /* this loop is here because aliasing is resolved on this level, not on res level */
1652 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1654 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1655 if(res
!= RES_BOGUS
) {
1656 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1659 *status
= U_MISSING_RESOURCE_ERROR
;
1662 } while(*path
); /* there is more stuff in the path */
1666 U_INTERNAL
const UChar
* U_EXPORT2
1667 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1670 UErrorCode
*status
) {
1672 UResourceBundle stack
;
1673 const UChar
* retVal
= NULL
;
1674 ures_initStackObject(&stack
);
1675 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1676 retVal
= ures_getString(&stack
, len
, status
);
1678 if ( retVal
!= NULL
&& u_strlen(retVal
) == 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1681 *status
= U_MISSING_RESOURCE_ERROR
;
1686 U_CAPI UResourceBundle
* U_EXPORT2
1687 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1689 UResourceBundle
*fillIn
,
1690 UErrorCode
*status
) {
1691 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1692 /*UResourceDataEntry *realData = NULL;*/
1693 const char *key
= inKey
;
1694 UResourceBundle
*helper
= NULL
;
1696 if (status
==NULL
|| U_FAILURE(*status
)) {
1700 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1704 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1705 if(URES_IS_TABLE(type
)) {
1707 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1708 if(res
== RES_BOGUS
) {
1709 UResourceDataEntry
*dataEntry
= resB
->fData
;
1711 char* myPath
= path
;
1712 const char* resPath
= resB
->fResPath
;
1713 int32_t len
= resB
->fResPathLen
;
1715 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1716 dataEntry
= dataEntry
->fParent
;
1717 rootRes
= dataEntry
->fData
.rootRes
;
1719 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1720 uprv_strncpy(path
, resPath
, len
);
1721 uprv_strcpy(path
+len
, inKey
);
1725 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1726 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1727 /* We hit an alias, but we didn't finish following the path. */
1728 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1729 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1731 dataEntry
= helper
->fData
;
1732 rootRes
= helper
->fRes
;
1733 resPath
= helper
->fResPath
;
1734 len
= helper
->fResPathLen
;
1740 } while(*myPath
); /* Continue until the whole path is consumed */
1743 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1744 if(res
!= RES_BOGUS
) {
1745 /* check if resB->fResPath gives the right name here */
1746 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1747 *status
= U_USING_DEFAULT_WARNING
;
1749 *status
= U_USING_FALLBACK_WARNING
;
1752 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1754 *status
= U_MISSING_RESOURCE_ERROR
;
1757 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1761 *status
= U_RESOURCE_TYPE_MISMATCH
;
1768 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1769 Resource res
= RES_BOGUS
;
1770 UResourceDataEntry
*realData
= NULL
;
1771 const char *key
= inKey
;
1773 if (status
==NULL
|| U_FAILURE(*status
)) {
1777 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1781 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1782 if(URES_IS_TABLE(type
)) {
1784 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1785 if(res
== RES_BOGUS
) {
1787 if(resB
->fHasFallback
== TRUE
) {
1788 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1789 if(U_SUCCESS(*status
)) {
1790 /* check if resB->fResPath gives the right name here */
1791 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1793 *status
= U_MISSING_RESOURCE_ERROR
;
1796 *status
= U_MISSING_RESOURCE_ERROR
;
1799 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1803 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1805 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1806 /* here should go a first attempt to locate the key using index table */
1807 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1808 if(U_SUCCESS(*status
)) {
1809 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1811 *status
= U_MISSING_RESOURCE_ERROR
;
1816 *status
= U_RESOURCE_TYPE_MISMATCH
;
1821 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1822 Resource res
= RES_BOGUS
;
1823 UResourceDataEntry
*realData
= NULL
;
1824 const char* key
= inKey
;
1826 if (status
==NULL
|| U_FAILURE(*status
)) {
1830 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1834 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1835 if(URES_IS_TABLE(type
)) {
1838 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1840 if(res
== RES_BOGUS
) {
1842 if(resB
->fHasFallback
== TRUE
) {
1843 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1844 if(U_SUCCESS(*status
)) {
1845 switch (RES_GET_TYPE(res
)) {
1847 case URES_STRING_V2
:
1848 return res_getString(rd
, res
, len
);
1851 const UChar
* result
= 0;
1852 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1853 result
= ures_getString(tempRes
, len
, status
);
1854 ures_close(tempRes
);
1858 *status
= U_RESOURCE_TYPE_MISMATCH
;
1861 *status
= U_MISSING_RESOURCE_ERROR
;
1864 *status
= U_MISSING_RESOURCE_ERROR
;
1867 switch (RES_GET_TYPE(res
)) {
1869 case URES_STRING_V2
:
1870 return res_getString(&(resB
->fResData
), res
, len
);
1873 const UChar
* result
= 0;
1874 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1875 result
= ures_getString(tempRes
, len
, status
);
1876 ures_close(tempRes
);
1880 *status
= U_RESOURCE_TYPE_MISMATCH
;
1885 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1887 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1888 /* here should go a first attempt to locate the key using index table */
1889 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1890 if(U_SUCCESS(*status
)) {
1891 return res_getString(rd
, res
, len
);
1893 *status
= U_MISSING_RESOURCE_ERROR
;
1898 *status
= U_RESOURCE_TYPE_MISMATCH
;
1903 U_CAPI
const char * U_EXPORT2
1904 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
1906 char *dest
, int32_t *pLength
,
1908 UErrorCode
*status
) {
1910 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
1911 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1914 /* TODO: clean from here down */
1917 * INTERNAL: Get the name of the first real locale (not placeholder)
1918 * that has resource bundle data.
1920 U_INTERNAL
const char* U_EXPORT2
1921 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1923 if (status
==NULL
|| U_FAILURE(*status
)) {
1926 if (!resourceBundle
) {
1927 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1930 return resourceBundle
->fData
->fName
;
1934 U_CAPI
const char* U_EXPORT2
1935 ures_getLocale(const UResourceBundle
* resourceBundle
,
1938 return ures_getLocaleInternal(resourceBundle
, status
);
1942 U_CAPI
const char* U_EXPORT2
1943 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1944 ULocDataLocaleType type
,
1945 UErrorCode
* status
) {
1946 if (status
==NULL
|| U_FAILURE(*status
)) {
1949 if (!resourceBundle
) {
1950 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1954 case ULOC_ACTUAL_LOCALE
:
1955 return resourceBundle
->fData
->fName
;
1956 case ULOC_VALID_LOCALE
:
1957 return resourceBundle
->fTopLevelData
->fName
;
1958 case ULOC_REQUESTED_LOCALE
:
1961 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1967 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
1972 return resB
->fData
->fName
;
1976 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
1981 return resB
->fData
->fPath
;
1985 /* OLD API implementation */
1988 * API: This function is used to open a resource bundle
1989 * proper fallback chaining is executed while initialization.
1990 * The result is stored in cache for later fallback search.
1992 U_CAPI
void U_EXPORT2
1993 ures_openFillIn(UResourceBundle
*r
, const char* path
,
1994 const char* localeID
, UErrorCode
* status
) {
1996 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1998 UResourceDataEntry
*firstData
;
1999 UBool isStackObject
= ures_isStackObject(r
);
2000 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2002 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2003 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2004 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2008 ures_closeBundle(r
, FALSE
);
2009 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2010 ures_setIsStackObject(r
, isStackObject
);
2011 r
->fHasFallback
= TRUE
;
2012 r
->fIsTopLevel
= TRUE
;
2014 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2015 if(U_FAILURE(*status
)) {
2018 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
2019 firstData
= r
->fData
;
2020 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
2021 firstData
= firstData
->fParent
;
2023 uprv_memcpy(&r
->fResData
, &firstData
->fData
, sizeof(ResourceData
));
2024 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2025 r
->fRes
= r
->fResData
.rootRes
;
2026 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2027 r
->fTopLevelData
= r
->fData
;
2031 U_CAPI UResourceBundle
* U_EXPORT2
2032 ures_open(const char* path
,
2033 const char* localeID
,
2036 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2037 UResourceDataEntry
*hasData
= NULL
;
2040 if(status
== NULL
|| U_FAILURE(*status
)) {
2044 /* first "canonicalize" the locale ID */
2045 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2046 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2047 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2051 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2053 *status
= U_MEMORY_ALLOCATION_ERROR
;
2057 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2058 r
->fHasFallback
= TRUE
;
2059 r
->fIsTopLevel
= TRUE
;
2060 ures_setIsStackObject(r
, FALSE
);
2062 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2063 if(U_FAILURE(*status
)) {
2067 r
->fTopLevelData
= r
->fData
;
2070 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
2071 hasData
= hasData
->fParent
;
2072 if(hasData
== NULL
) {
2073 /* This can happen only if fallback chain gets broken by an act of God */
2074 /* TODO: this unlikely to happen, consider removing it */
2075 entryClose(r
->fData
);
2077 *status
= U_MISSING_RESOURCE_ERROR
;
2082 uprv_memcpy(&r
->fResData
, &hasData
->fData
, sizeof(ResourceData
));
2083 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2084 r
->fRes
= r
->fResData
.rootRes
;
2085 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2087 if(r->fData->fPath != NULL) {
2088 ures_setResPath(r, r->fData->fPath);
2089 ures_appendResPath(r, RES_PATH_PACKAGE_S);
2090 ures_appendResPath(r, r->fData->fName);
2092 ures_setResPath(r, r->fData->fName);
2101 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2102 * or sought. However, alias substitution will happen!
2104 U_CAPI UResourceBundle
* U_EXPORT2
2105 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2107 UErrorCode subStatus
= U_ZERO_ERROR
;
2109 if(status
== NULL
|| U_FAILURE(*status
)) {
2113 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2115 *status
= U_MEMORY_ALLOCATION_ERROR
;
2119 r
->fHasFallback
= FALSE
;
2120 r
->fIsTopLevel
= TRUE
;
2121 ures_setIsStackObject(r
, FALSE
);
2123 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
2124 if(U_FAILURE(subStatus
)) {
2125 *status
= subStatus
;
2129 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
2130 /* we didn't find one we were looking for - so openDirect */
2132 entryClose(r
->fData
);
2134 *status
= U_MISSING_RESOURCE_ERROR
;
2140 uprv_memcpy(&r
->fResData
, &r
->fData
->fData
, sizeof(ResourceData
));
2141 /* r->fHasFallback remains FALSE here in ures_openDirect() */
2142 r
->fRes
= r
->fResData
.rootRes
;
2143 /*r->fParent = RES_BOGUS;*/
2144 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2147 /*r->fParentRes = NULL;*/
2148 r
->fTopLevelData
= r
->fData
;
2154 * API: Counts members. For arrays and tables, returns number of resources.
2155 * For strings, returns 1.
2157 U_CAPI
int32_t U_EXPORT2
2158 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2159 const char* resourceKey
,
2162 UResourceBundle resData
;
2163 ures_initStackObject(&resData
);
2164 if (status
==NULL
|| U_FAILURE(*status
)) {
2167 if(resourceBundle
== NULL
) {
2168 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2171 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2173 if(resData
.fResData
.data
!= NULL
) {
2174 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2175 ures_close(&resData
);
2178 *status
= U_MISSING_RESOURCE_ERROR
;
2179 ures_close(&resData
);
2185 * Internal function.
2186 * Return the version number associated with this ResourceBundle as a string.
2188 * @param resourceBundle The resource bundle for which the version is checked.
2189 * @return A version number string as specified in the resource bundle or its parent.
2190 * The caller does not own this string.
2191 * @see ures_getVersion
2194 U_INTERNAL
const char* U_EXPORT2
2195 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2197 if (!resourceBundle
) return NULL
;
2199 if(resourceBundle
->fVersion
== NULL
) {
2201 /* If the version ID has not been built yet, then do so. Retrieve */
2202 /* the minor version from the file. */
2203 UErrorCode status
= U_ZERO_ERROR
;
2204 int32_t minor_len
= 0;
2207 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2209 /* Determine the length of of the final version string. This is */
2210 /* the length of the major part + the length of the separator */
2211 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2214 len
= (minor_len
> 0) ? minor_len
: 1;
2216 /* Allocate the string, and build it up. */
2217 /* + 1 for zero byte */
2220 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2221 /* Check for null pointer. */
2222 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2227 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2228 resourceBundle
->fVersion
[len
] = '\0';
2231 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2235 return resourceBundle
->fVersion
;
2238 U_CAPI
const char* U_EXPORT2
2239 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2241 return ures_getVersionNumberInternal(resourceBundle
);
2244 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2247 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2250 /** Tree support functions *******************************/
2251 #define INDEX_LOCALE_NAME "res_index"
2252 #define INDEX_TAG "InstalledLocales"
2253 #define DEFAULT_TAG "default"
2255 #if defined(URES_TREE_DEBUG)
2259 typedef struct ULocalesContext
{
2260 UResourceBundle installed
;
2261 UResourceBundle curr
;
2264 static void U_CALLCONV
2265 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2266 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2267 ures_close(&ctx
->curr
);
2268 ures_close(&ctx
->installed
);
2270 uprv_free(enumerator
);
2273 static int32_t U_CALLCONV
2274 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2275 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2276 return ures_getSize(&ctx
->installed
);
2279 static const char* U_CALLCONV
2280 ures_loc_nextLocale(UEnumeration
* en
,
2281 int32_t* resultLength
,
2282 UErrorCode
* status
) {
2283 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2284 UResourceBundle
*res
= &(ctx
->installed
);
2285 UResourceBundle
*k
= NULL
;
2286 const char *result
= NULL
;
2288 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2289 result
= ures_getKey(k
);
2290 len
= (int32_t)uprv_strlen(result
);
2293 *resultLength
= len
;
2298 static void U_CALLCONV
2299 ures_loc_resetLocales(UEnumeration
* en
,
2300 UErrorCode
* /*status*/) {
2301 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2302 ures_resetIterator(res
);
2306 static const UEnumeration gLocalesEnum
= {
2309 ures_loc_closeLocales
,
2310 ures_loc_countLocales
,
2312 ures_loc_nextLocale
,
2313 ures_loc_resetLocales
2317 U_CAPI UEnumeration
* U_EXPORT2
2318 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2320 UResourceBundle
*idx
= NULL
;
2321 UEnumeration
*en
= NULL
;
2322 ULocalesContext
*myContext
= NULL
;
2324 if(U_FAILURE(*status
)) {
2327 myContext
= reinterpret_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2328 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2329 if(!en
|| !myContext
) {
2330 *status
= U_MEMORY_ALLOCATION_ERROR
;
2332 uprv_free(myContext
);
2335 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2337 ures_initStackObject(&myContext
->installed
);
2338 ures_initStackObject(&myContext
->curr
);
2339 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2340 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2341 if(U_SUCCESS(*status
)) {
2342 #if defined(URES_TREE_DEBUG)
2343 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2344 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2346 en
->context
= myContext
;
2348 #if defined(URES_TREE_DEBUG)
2349 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2351 ures_close(&myContext
->installed
);
2352 uprv_free(myContext
);
2362 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2364 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2365 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2372 U_CAPI
int32_t U_EXPORT2
2373 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2374 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2375 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2377 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2378 char defVal
[1024] = ""; /* default value for given locale */
2379 char defLoc
[1024] = ""; /* default value for given locale */
2380 char base
[1024] = ""; /* base locale */
2383 char full
[1024] = "";
2384 UResourceBundle bund1
, bund2
;
2385 UResourceBundle
*res
= NULL
;
2386 UErrorCode subStatus
= U_ZERO_ERROR
;
2388 if(U_FAILURE(*status
)) return 0;
2389 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2390 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2393 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2394 #if defined(URES_TREE_DEBUG)
2395 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2396 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2398 ures_initStackObject(&bund1
);
2399 ures_initStackObject(&bund2
);
2402 uprv_strcpy(parent
, base
);
2403 uprv_strcpy(found
, base
);
2406 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2407 *isAvailable
= TRUE
;
2408 if (U_SUCCESS(subStatus
)) {
2409 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2411 uenum_close(locEnum
);
2414 if(U_FAILURE(subStatus
)) {
2415 *status
= subStatus
;
2420 subStatus
= U_ZERO_ERROR
;
2421 res
= ures_open(path
, parent
, &subStatus
);
2422 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2423 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2425 *isAvailable
= FALSE
;
2427 isAvailable
= NULL
; /* only want to set this the first time around */
2429 #if defined(URES_TREE_DEBUG)
2430 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2432 if(U_FAILURE(subStatus
)) {
2433 *status
= subStatus
;
2434 } else if(subStatus
== U_ZERO_ERROR
) {
2435 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2436 if(subStatus
== U_ZERO_ERROR
) {
2437 const UChar
*defUstr
;
2439 /* look for default item */
2440 #if defined(URES_TREE_DEBUG)
2441 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2442 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2444 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2445 if(U_SUCCESS(subStatus
) && defLen
) {
2446 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2447 #if defined(URES_TREE_DEBUG)
2448 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2449 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2451 uprv_strcpy(defLoc
, parent
);
2453 uprv_strcpy(kwVal
, defVal
);
2454 #if defined(URES_TREE_DEBUG)
2455 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2456 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2463 subStatus
= U_ZERO_ERROR
;
2466 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2469 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2471 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2473 /* Now, see if we can find the kwVal collator.. start the search over.. */
2474 uprv_strcpy(parent
, base
);
2475 uprv_strcpy(found
, base
);
2478 subStatus
= U_ZERO_ERROR
;
2479 res
= ures_open(path
, parent
, &subStatus
);
2480 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2481 *isAvailable
= FALSE
;
2483 isAvailable
= NULL
; /* only want to set this the first time around */
2485 #if defined(URES_TREE_DEBUG)
2486 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2487 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2489 if(U_FAILURE(subStatus
)) {
2490 *status
= subStatus
;
2491 } else if(subStatus
== U_ZERO_ERROR
) {
2492 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2493 #if defined(URES_TREE_DEBUG)
2494 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2496 if(subStatus
== U_ZERO_ERROR
) {
2497 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2498 #if defined(URES_TREE_DEBUG)
2499 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2501 if(subStatus
== U_ZERO_ERROR
) {
2502 #if defined(URES_TREE_DEBUG)
2503 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2504 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2506 uprv_strcpy(full
, parent
);
2508 uprv_strcpy(full
, "root");
2510 /* now, recalculate default kw if need be */
2511 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2512 const UChar
*defUstr
;
2514 /* look for default item */
2515 #if defined(URES_TREE_DEBUG)
2516 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2517 path
?path
:"ICUDATA", full
);
2519 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2520 if(U_SUCCESS(subStatus
) && defLen
) {
2521 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2522 #if defined(URES_TREE_DEBUG)
2523 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2524 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2526 uprv_strcpy(defLoc
, full
);
2528 } /* end of recalculate default KW */
2529 #if defined(URES_TREE_DEBUG)
2531 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2535 #if defined(URES_TREE_DEBUG)
2536 fprintf(stderr
, "err=%s in %s looking for %s\n",
2537 u_errorName(subStatus
), parent
, kwVal
);
2543 subStatus
= U_ZERO_ERROR
;
2545 uprv_strcpy(found
, parent
);
2546 uloc_getParent(found
,parent
,1023,&subStatus
);
2548 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2550 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2551 #if defined(URES_TREE_DEBUG)
2552 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2554 uprv_strcpy(kwVal
, defVal
);
2555 uprv_strcpy(parent
, base
);
2556 uprv_strcpy(found
, base
);
2558 do { /* search for 'default' named item */
2559 subStatus
= U_ZERO_ERROR
;
2560 res
= ures_open(path
, parent
, &subStatus
);
2561 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2562 *isAvailable
= FALSE
;
2564 isAvailable
= NULL
; /* only want to set this the first time around */
2566 #if defined(URES_TREE_DEBUG)
2567 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2568 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2570 if(U_FAILURE(subStatus
)) {
2571 *status
= subStatus
;
2572 } else if(subStatus
== U_ZERO_ERROR
) {
2573 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2574 if(subStatus
== U_ZERO_ERROR
) {
2575 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2576 if(subStatus
== U_ZERO_ERROR
) {
2577 #if defined(URES_TREE_DEBUG)
2578 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2579 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2581 uprv_strcpy(full
, parent
);
2583 uprv_strcpy(full
, "root");
2586 /* now, recalculate default kw if need be */
2587 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2588 const UChar
*defUstr
;
2590 /* look for default item */
2591 #if defined(URES_TREE_DEBUG)
2592 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2593 path
?path
:"ICUDATA", full
);
2595 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2596 if(U_SUCCESS(subStatus
) && defLen
) {
2597 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2598 #if defined(URES_TREE_DEBUG)
2599 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2600 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2602 uprv_strcpy(defLoc
, full
);
2604 } /* end of recalculate default KW */
2605 #if defined(URES_TREE_DEBUG)
2607 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2613 subStatus
= U_ZERO_ERROR
;
2615 uprv_strcpy(found
, parent
);
2616 uloc_getParent(found
,parent
,1023,&subStatus
);
2618 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2621 if(U_SUCCESS(*status
)) {
2623 #if defined(URES_TREE_DEBUG)
2624 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2626 *status
= U_MISSING_RESOURCE_ERROR
;
2627 } else if(omitDefault
) {
2628 #if defined(URES_TREE_DEBUG)
2629 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2631 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2632 /* found the keyword in a *child* of where the default tag was present. */
2633 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2634 /* and the default is in or in an ancestor of the current locale */
2635 #if defined(URES_TREE_DEBUG)
2636 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2642 uprv_strcpy(found
, full
);
2644 uprv_strcat(found
, "@");
2645 uprv_strcat(found
, keyword
);
2646 uprv_strcat(found
, "=");
2647 uprv_strcat(found
, kwVal
);
2648 } else if(!omitDefault
) {
2649 uprv_strcat(found
, "@");
2650 uprv_strcat(found
, keyword
);
2651 uprv_strcat(found
, "=");
2652 uprv_strcat(found
, defVal
);
2655 /* we found the default locale - no need to repeat it.*/
2660 length
= (int32_t)uprv_strlen(found
);
2662 if(U_SUCCESS(*status
)) {
2663 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2665 uprv_strncpy(result
, found
, copyLength
);
2668 *status
= U_MISSING_RESOURCE_ERROR
;
2674 return u_terminateChars(result
, resultCapacity
, length
, status
);
2677 U_CAPI UEnumeration
* U_EXPORT2
2678 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2680 #define VALUES_BUF_SIZE 2048
2681 #define VALUES_LIST_SIZE 512
2683 char valuesBuf
[VALUES_BUF_SIZE
];
2684 int32_t valuesIndex
= 0;
2685 const char *valuesList
[VALUES_LIST_SIZE
];
2686 int32_t valuesCount
= 0;
2691 UEnumeration
*locs
= NULL
;
2693 UResourceBundle item
;
2694 UResourceBundle subItem
;
2696 ures_initStackObject(&item
);
2697 ures_initStackObject(&subItem
);
2698 locs
= ures_openAvailableLocales(path
, status
);
2700 if(U_FAILURE(*status
)) {
2702 ures_close(&subItem
);
2709 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2710 UResourceBundle
*bund
= NULL
;
2711 UResourceBundle
*subPtr
= NULL
;
2712 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2713 bund
= ures_openDirect(path
, locale
, &subStatus
);
2715 #if defined(URES_TREE_DEBUG)
2716 if(!bund
|| U_FAILURE(subStatus
)) {
2717 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2718 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2722 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2724 if(!bund
|| U_FAILURE(subStatus
)) {
2725 #if defined(URES_TREE_DEBUG)
2726 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2727 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2734 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2735 && U_SUCCESS(subStatus
)) {
2738 k
= ures_getKey(subPtr
);
2740 #if defined(URES_TREE_DEBUG)
2741 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2743 for(i
=0;k
&&i
<valuesCount
;i
++) {
2744 if(!uprv_strcmp(valuesList
[i
],k
)) {
2745 k
= NULL
; /* found duplicate */
2749 int32_t kLen
= (int32_t)uprv_strlen(k
);
2750 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2751 continue; /* don't need 'default'. */
2753 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2754 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2755 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2757 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2758 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2759 valuesIndex
+= kLen
;
2760 #if defined(URES_TREE_DEBUG)
2761 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2762 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2764 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2770 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2773 ures_close(&subItem
);
2775 #if defined(URES_TREE_DEBUG)
2776 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2777 valuesIndex
, valuesCount
);
2779 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2782 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2783 U_INTERNAL UBool U_EXPORT2
2784 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2785 if(res1
==NULL
|| res2
==NULL
){
2786 return res1
==res2
; /* pointer comparision */
2788 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2789 return (res1
->fKey
==res2
->fKey
);
2791 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2795 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2798 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2799 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2801 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2805 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2808 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2811 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2814 if(res1
->fRes
!= res2
->fRes
){
2819 U_INTERNAL UResourceBundle
* U_EXPORT2
2820 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2821 UResourceBundle
* bundle
= NULL
;
2822 UResourceBundle
* ret
= NULL
;
2823 if(U_FAILURE(*status
) || res
== NULL
){
2826 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2827 if(res
->fResPath
!=NULL
){
2828 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2835 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2836 ures_getParentBundle(const UResourceBundle
* res
){
2840 return res
->fParentRes
;
2844 U_INTERNAL
void U_EXPORT2
2845 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
2848 str
= ures_getStringByKey(res
, key
, &len
, status
);
2849 if(U_SUCCESS(*status
)) {
2850 u_versionFromUString(ver
, str
);