2 ******************************************************************************
3 * Copyright (C) 1997-2010, 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
, 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;*/
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 uprv_strcpy(name
, uloc_getDefault());
335 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
336 uprv_strcpy(name
, kRootLocaleName
);
337 } else { /* otherwise, we'll open what we're given */
338 uprv_strcpy(name
, localeID
);
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);
532 char usrDataPath
[96];
536 if(U_FAILURE(*status
)) {
540 uprv_strcpy(name
, localeID
);
542 if ( usingUSRData
) {
543 if ( path
== NULL
) {
544 uprv_strcpy(usrDataPath
,U_USRDATA_NAME
);
546 uprv_strcpy(usrDataPath
,path
);
547 usrDataPath
[0] = 'u';
548 usrDataPath
[1] = 's';
549 usrDataPath
[2] = 'r';
553 umtx_lock(&resbMutex
);
555 /* We're going to skip all the locales that do not have any data */
556 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
558 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
561 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
562 u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
564 if(u1
->fBogus
== U_ZERO_ERROR
) {
568 /* the USR override data wasn't found, set it to be deleted */
569 u1
->fCountExisting
= 0;
573 while (hasChopped
&& !isRoot
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
) {
574 /* insert regular parents */
575 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
576 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
577 usrStatus
= U_ZERO_ERROR
;
578 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
580 /* Check for null pointer. */
581 if (t2
== NULL
|| ( usingUSRData
&& u2
== NULL
)) {
582 *status
= U_MEMORY_ALLOCATION_ERROR
;
586 if ( res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
587 if ( usingUSRData
&& u2
->fBogus
== U_ZERO_ERROR
) {
593 /* the USR override data wasn't found, set it to be deleted */
594 u2
->fCountExisting
= 0;
600 /* the USR override data wasn't found, set it to be deleted */
601 u2
->fCountExisting
= 0;
603 /* t2->fCountExisting have to be decremented since the call to init_entry increments
604 * it and if we hit this code, that means it is not set as the parent.
606 t2
->fCountExisting
--;
608 hasChopped
= chopLocale(name
);
612 /* we could have reached this point without having any real data */
613 /* if that is the case, we need to chain in the default locale */
614 if(r
==NULL
&& !isDefault
&& !isRoot
/*&& t1->fParent == NULL*/) {
615 /* insert default locale */
616 uprv_strcpy(name
, uloc_getDefault());
617 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
618 intStatus
= U_USING_DEFAULT_WARNING
;
619 if(r
!= NULL
) { /* the default locale exists */
623 while (hasChopped
&& t1
->fParent
== NULL
) {
624 /* insert chopped defaults */
625 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
626 /* Check for null pointer. */
628 *status
= U_MEMORY_ALLOCATION_ERROR
;
632 if ( res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
636 hasChopped
= chopLocale(name
);
641 /* we could still have r == NULL at this point - maybe even default locale is not */
644 uprv_strcpy(name
, kRootLocaleName
);
645 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
648 intStatus
= U_USING_DEFAULT_WARNING
;
650 } else { /* we don't even have the root locale */
651 *status
= U_MISSING_RESOURCE_ERROR
;
654 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
655 /* insert root locale */
656 t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
657 /* Check for null pointer. */
659 *status
= U_MEMORY_ALLOCATION_ERROR
;
663 r
->fBogus
= U_USING_DEFAULT_WARNING
;
665 hasRealData
= (UBool
)((t2
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
670 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
671 t1
->fParent
->fCountExisting
++;
673 hasRealData
= (UBool
)((t1
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
677 umtx_unlock(&resbMutex
);
679 if(U_SUCCESS(*status
)) {
680 if(U_SUCCESS(parentStatus
)) {
681 if(intStatus
!= U_ZERO_ERROR
) {
686 *status
= parentStatus
;
696 * Functions to create and destroy resource bundles.
697 * CAUTION: resbMutex must be locked when calling this function.
700 static void entryCloseInt(UResourceDataEntry
*resB
) {
701 UResourceDataEntry
*p
= resB
;
703 while(resB
!= NULL
) {
705 resB
->fCountExisting
--;
707 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
710 if(resB->fCountExisting <= 0) {
711 uhash_remove(cache, resB);
712 if(resB->fBogus == U_ZERO_ERROR) {
713 res_unload(&(resB->fData));
715 if(resB->fName != NULL) {
716 uprv_free(resB->fName);
718 if(resB->fPath != NULL) {
719 uprv_free(resB->fPath);
730 * API: closes a resource bundle and cleans up.
733 static void entryClose(UResourceDataEntry
*resB
) {
734 umtx_lock(&resbMutex
);
736 umtx_unlock(&resbMutex
);
740 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
741 if(resB->fResPath == NULL) {
742 resB->fResPath = resB->fResBuf;
743 *(resB->fResPath) = 0;
745 resB->fResPathLen = uprv_strlen(toAdd);
746 if(RES_BUFSIZE <= resB->fResPathLen+1) {
747 if(resB->fResPath == resB->fResBuf) {
748 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
750 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
753 uprv_strcpy(resB->fResPath, toAdd);
756 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
757 int32_t resPathLenOrig
= resB
->fResPathLen
;
758 if(resB
->fResPath
== NULL
) {
759 resB
->fResPath
= resB
->fResBuf
;
760 *(resB
->fResPath
) = 0;
761 resB
->fResPathLen
= 0;
763 resB
->fResPathLen
+= lenToAdd
;
764 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
765 if(resB
->fResPath
== resB
->fResBuf
) {
766 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
767 /* Check that memory was allocated correctly. */
768 if (resB
->fResPath
== NULL
) {
769 *status
= U_MEMORY_ALLOCATION_ERROR
;
772 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
774 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
775 /* Check that memory was reallocated correctly. */
777 *status
= U_MEMORY_ALLOCATION_ERROR
;
780 resB
->fResPath
= temp
;
783 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
786 static void ures_freeResPath(UResourceBundle
*resB
) {
787 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
788 uprv_free(resB
->fResPath
);
790 resB
->fResPath
= NULL
;
791 resB
->fResPathLen
= 0;
795 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
798 if(resB
->fData
!= NULL
) {
799 entryClose(resB
->fData
);
801 if(resB
->fVersion
!= NULL
) {
802 uprv_free(resB
->fVersion
);
804 ures_freeResPath(resB
);
806 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
811 /* poison the data */
812 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
818 U_CAPI
void U_EXPORT2
819 ures_close(UResourceBundle
* resB
)
821 ures_closeBundle(resB
, TRUE
);
824 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
825 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
826 const UResourceBundle
*parent
, int32_t noAlias
,
827 UResourceBundle
*resB
, UErrorCode
*status
)
829 if(status
== NULL
|| U_FAILURE(*status
)) {
832 if (parent
== NULL
) {
833 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
836 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
837 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
839 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
841 /* we have an alias, now let's cut it up */
842 char stackAlias
[200];
843 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
847 * Allocate enough space for both the char * version
848 * of the alias and parent->fResPath.
850 * We do this so that res_findResource() can modify the path,
851 * which allows us to remove redundant _res_findResource() variants
853 * res_findResource() now NUL-terminates each segment so that table keys
854 * can always be compared with strcmp() instead of strncmp().
855 * Saves code there and simplifies testing and code coverage.
859 ++len
; /* count the terminating NUL */
860 if(parent
->fResPath
!= NULL
) {
861 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
868 if(capacity
<= sizeof(stackAlias
)) {
869 capacity
= sizeof(stackAlias
);
870 chAlias
= stackAlias
;
872 chAlias
= (char *)uprv_malloc(capacity
);
874 if(chAlias
== NULL
) {
875 *status
= U_MEMORY_ALLOCATION_ERROR
;
879 u_UCharsToChars(alias
, chAlias
, len
);
881 if(*chAlias
== RES_PATH_SEPARATOR
) {
882 /* there is a path included */
883 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
885 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
891 if(uprv_strcmp(path
, "LOCALE") == 0) {
892 /* this is an XPath alias, starting with "/LOCALE/" */
893 /* it contains the path to a resource which should be looked up */
894 /* starting in the requested locale */
896 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
897 path
= realData
->fPath
; /* we will be looking in the same package */
899 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
902 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
909 /* no path, start with a locale */
911 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
916 path
= realData
->fPath
;
921 /* got almost everything, let's try to open */
922 /* first, open the bundle with real data */
923 UResourceBundle
*result
= resB
;
924 const char* temp
= NULL
;
925 UErrorCode intStatus
= U_ZERO_ERROR
;
926 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
927 if(U_SUCCESS(intStatus
)) {
928 if(keyPath
== NULL
) {
929 /* no key path. This means that we are going to
930 * to use the corresponding resource from
933 /* first, we are going to get a corresponding parent
934 * resource to the one we are searching.
936 char *aKey
= parent
->fResPath
;
938 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
940 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
945 /* we need to make keyPath from parent's fResPath and
946 * current key, if there is a key associated
948 len
= (int32_t)(uprv_strlen(key
) + 1);
951 if(chAlias
== stackAlias
) {
952 chAlias
= (char *)uprv_malloc(capacity
);
954 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
956 if(chAlias
== NULL
) {
958 *status
= U_MEMORY_ALLOCATION_ERROR
;
962 uprv_memcpy(chAlias
, key
, len
);
964 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
965 } else if(idx
!= -1) {
966 /* if there is no key, but there is an index, try to get by the index */
967 /* here we have either a table or an array, so get the element */
968 UResType type
= RES_GET_TYPE(r
);
969 if(URES_IS_TABLE(type
)) {
970 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
972 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
976 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
978 *status
= U_MISSING_RESOURCE_ERROR
;
982 /* this one is a bit trickier.
983 * we start finding keys, but after we resolve one alias, the path might continue.
985 * aliastest:alias { "testtypes/anotheralias/Sequence" }
986 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
987 * aliastest resource should finally have the sequence, not collation elements.
989 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
990 char stackPath
[URES_MAX_BUFFER_SIZE
];
991 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
992 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
993 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
994 if(pathBuf
== NULL
) {
995 *status
= U_MEMORY_ALLOCATION_ERROR
;
999 uprv_strcpy(pathBuf
, keyPath
);
1001 /* now we have fallback following here */
1003 r
= dataEntry
->fData
.rootRes
;
1004 /* this loop handles 'found' resources over several levels */
1005 while(*myPath
&& U_SUCCESS(*status
)) {
1006 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1007 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1008 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1011 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1012 dataEntry
= result
->fData
;
1014 } else { /* no resource found, we don't really want to look anymore on this level */
1018 dataEntry
= dataEntry
->fParent
;
1019 uprv_strcpy(pathBuf
, keyPath
);
1021 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1022 if(r
== RES_BOGUS
) {
1023 *status
= U_MISSING_RESOURCE_ERROR
;
1026 if(pathBuf
!= stackPath
) {
1030 } else { /* we failed to open the resource we're aliasing to */
1031 *status
= intStatus
;
1033 if(chAlias
!= stackAlias
) {
1036 if(mainRes
!= result
) {
1037 ures_close(mainRes
);
1042 /* bad alias, should be an error */
1043 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1047 *status
= U_TOO_MANY_ALIASES_ERROR
;
1052 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1055 *status
= U_MEMORY_ALLOCATION_ERROR
;
1058 ures_setIsStackObject(resB
, FALSE
);
1059 resB
->fResPath
= NULL
;
1060 resB
->fResPathLen
= 0;
1062 if(resB
->fData
!= NULL
) {
1063 entryClose(resB
->fData
);
1065 if(resB
->fVersion
!= NULL
) {
1066 uprv_free(resB
->fVersion
);
1069 weiv: if stack object was passed in, it doesn't really need to be reinited,
1070 since the purpose of initing is to remove stack junk. However, at this point
1071 we would not do anything to an allocated object, so stack object should be
1075 if(ures_isStackObject(resB) != FALSE) {
1076 ures_initStackObject(resB);
1079 if(parent
!= resB
) {
1080 ures_freeResPath(resB
);
1083 resB
->fData
= realData
;
1084 entryIncrease(resB
->fData
);
1085 resB
->fHasFallback
= FALSE
;
1086 resB
->fIsTopLevel
= FALSE
;
1089 /*resB->fParentRes = parent;*/
1090 resB
->fTopLevelData
= parent
->fTopLevelData
;
1091 if(parent
->fResPath
&& parent
!= resB
) {
1092 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1095 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1096 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1097 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1099 } else if(idx
>= 0) {
1101 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1102 ures_appendResPath(resB
, buf
, len
, status
);
1103 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1104 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1107 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1109 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1110 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1113 resB
->fVersion
= NULL
;
1115 /*resB->fParent = parent->fRes;*/
1116 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1117 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1121 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1122 UBool isStackObject
;
1123 if(U_FAILURE(*status
) || r
== original
) {
1126 if(original
!= NULL
) {
1128 isStackObject
= FALSE
;
1129 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1132 *status
= U_MEMORY_ALLOCATION_ERROR
;
1136 isStackObject
= ures_isStackObject(r
);
1137 ures_closeBundle(r
, FALSE
);
1139 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1142 if(original
->fResPath
) {
1143 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1145 ures_setIsStackObject(r
, isStackObject
);
1146 if(r
->fData
!= NULL
) {
1147 entryIncrease(r
->fData
);
1154 * Functions to retrieve data from resource bundles.
1157 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1159 if (status
==NULL
|| U_FAILURE(*status
)) {
1163 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1166 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1168 *status
= U_RESOURCE_TYPE_MISMATCH
;
1174 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1175 char *dest
, int32_t *pLength
,
1177 UErrorCode
*status
) {
1180 if (U_FAILURE(*status
)) {
1183 if (pLength
!= NULL
) {
1184 capacity
= *pLength
;
1188 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1189 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1193 if (length16
== 0) {
1194 /* empty string, return as read-only pointer */
1195 if (pLength
!= NULL
) {
1199 u_terminateChars(dest
, capacity
, 0, status
);
1205 /* We need to transform the string to the destination buffer. */
1206 if (capacity
< length16
) {
1207 /* No chance for the string to fit. Pure preflighting. */
1208 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1210 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1212 * We know the string will fit into dest because each UChar turns
1213 * into at most three UTF-8 bytes. Fill the latter part of dest
1214 * so that callers do not expect to use dest as a string pointer,
1215 * hopefully leading to more robust code for when resource bundles
1216 * may store UTF-8 natively.
1217 * (In which case dest would not be used at all.)
1219 * We do not do this if forceCopy=TRUE because then the caller
1220 * expects the string to start exactly at dest.
1222 * The test above for <= 0x2aaaaaaa prevents overflows.
1223 * The +1 is for the NUL terminator.
1225 int32_t maxLength
= 3 * length16
+ 1;
1226 if (capacity
> maxLength
) {
1227 dest
+= capacity
- maxLength
;
1228 capacity
= maxLength
;
1231 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1235 U_CAPI
const char * U_EXPORT2
1236 ures_getUTF8String(const UResourceBundle
*resB
,
1237 char *dest
, int32_t *pLength
,
1239 UErrorCode
*status
) {
1241 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1242 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1245 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1246 UErrorCode
* status
) {
1248 if (status
==NULL
|| U_FAILURE(*status
)) {
1252 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1255 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1257 *status
= U_RESOURCE_TYPE_MISMATCH
;
1262 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1263 UErrorCode
* status
) {
1265 if (status
==NULL
|| U_FAILURE(*status
)) {
1269 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1272 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1274 *status
= U_RESOURCE_TYPE_MISMATCH
;
1279 /* this function returns a signed integer */
1280 /* it performs sign extension */
1281 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1282 if (status
==NULL
|| U_FAILURE(*status
)) {
1286 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1289 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1290 *status
= U_RESOURCE_TYPE_MISMATCH
;
1293 return RES_GET_INT(resB
->fRes
);
1296 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1297 if (status
==NULL
|| U_FAILURE(*status
)) {
1301 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1304 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1305 *status
= U_RESOURCE_TYPE_MISMATCH
;
1308 return RES_GET_UINT(resB
->fRes
);
1311 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1315 return res_getPublicType(resB
->fRes
);
1318 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1326 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1334 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1335 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1336 const UChar
* result
= 0;
1337 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1338 result
= ures_getString(tempRes
, len
, status
);
1339 ures_close(tempRes
);
1342 return res_getString(&(resB
->fResData
), r
, len
);
1346 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1353 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1357 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1360 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1361 Resource r
= RES_BOGUS
;
1363 if (status
==NULL
|| U_FAILURE(*status
)) {
1367 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1371 if(resB
->fIndex
== resB
->fSize
-1) {
1372 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1375 switch(RES_GET_TYPE(resB
->fRes
)) {
1377 case URES_STRING_V2
:
1378 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1382 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1383 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1384 /* TODO: do the fallback */
1386 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1389 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1390 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1391 /* TODO: do the fallback */
1393 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1395 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1398 case URES_INT_VECTOR
:
1399 *status
= U_RESOURCE_TYPE_MISMATCH
;
1408 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1409 const char *key
= NULL
;
1410 Resource r
= RES_BOGUS
;
1412 if (status
==NULL
|| U_FAILURE(*status
)) {
1417 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1422 if(resB
->fIndex
== resB
->fSize
-1) {
1423 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1427 switch(RES_GET_TYPE(resB
->fRes
)) {
1431 case URES_STRING_V2
:
1432 case URES_INT_VECTOR
:
1433 return ures_copyResb(fillIn
, resB
, status
);
1437 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1438 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1439 /* TODO: do the fallback */
1441 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1444 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1445 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1446 /* TODO: do the fallback */
1448 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1458 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1459 const char* key
= NULL
;
1460 Resource r
= RES_BOGUS
;
1462 if (status
==NULL
|| U_FAILURE(*status
)) {
1467 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1472 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1473 switch(RES_GET_TYPE(resB
->fRes
)) {
1477 case URES_STRING_V2
:
1478 case URES_INT_VECTOR
:
1479 return ures_copyResb(fillIn
, resB
, status
);
1483 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1484 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1485 /* TODO: do the fallback */
1487 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1490 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1491 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1492 /* TODO: do the fallback */
1494 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1500 *status
= U_MISSING_RESOURCE_ERROR
;
1506 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1507 const char* key
= NULL
;
1508 Resource r
= RES_BOGUS
;
1510 if (status
==NULL
|| U_FAILURE(*status
)) {
1514 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1518 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1519 switch(RES_GET_TYPE(resB
->fRes
)) {
1521 case URES_STRING_V2
:
1522 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1526 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1527 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1528 /* TODO: do the fallback */
1530 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1533 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1534 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1535 /* TODO: do the fallback */
1537 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1539 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1542 case URES_INT_VECTOR
:
1543 *status
= U_RESOURCE_TYPE_MISMATCH
;
1546 /* must not occur */
1547 *status
= U_INTERNAL_PROGRAM_ERROR
;
1551 *status
= U_MISSING_RESOURCE_ERROR
;
1556 U_CAPI
const char * U_EXPORT2
1557 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1559 char *dest
, int32_t *pLength
,
1561 UErrorCode
*status
) {
1563 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1564 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1567 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1568 return resB->fResPath;
1571 U_CAPI UResourceBundle
* U_EXPORT2
1572 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1574 UResourceBundle
*first
= NULL
;
1575 UResourceBundle
*result
= fillIn
;
1576 char *packageName
= NULL
;
1577 char *pathToResource
= NULL
, *save
= NULL
;
1578 char *locale
= NULL
, *localeEnd
= NULL
;
1581 if(status
== NULL
|| U_FAILURE(*status
)) {
1585 length
= (int32_t)(uprv_strlen(path
)+1);
1586 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1588 if(pathToResource
== NULL
) {
1589 *status
= U_MEMORY_ALLOCATION_ERROR
;
1592 uprv_memcpy(pathToResource
, path
, length
);
1594 locale
= pathToResource
;
1595 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1597 packageName
= pathToResource
;
1598 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1599 if(pathToResource
== NULL
) {
1600 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1602 *pathToResource
= 0;
1603 locale
= pathToResource
+1;
1607 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1608 if(localeEnd
!= NULL
) {
1612 first
= ures_open(packageName
, locale
, status
);
1614 if(U_SUCCESS(*status
)) {
1616 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1618 result
= ures_copyResb(fillIn
, first
, status
);
1626 U_CAPI UResourceBundle
* U_EXPORT2
1627 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1629 Resource res
= RES_BOGUS
;
1630 UResourceBundle
*result
= fillIn
;
1633 if(status
== NULL
|| U_FAILURE(*status
)) {
1637 /* here we do looping and circular alias checking */
1638 /* this loop is here because aliasing is resolved on this level, not on res level */
1639 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1641 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1642 if(res
!= RES_BOGUS
) {
1643 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1646 *status
= U_MISSING_RESOURCE_ERROR
;
1649 } while(*path
); /* there is more stuff in the path */
1653 U_INTERNAL
const UChar
* U_EXPORT2
1654 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1657 UErrorCode
*status
) {
1659 UResourceBundle stack
;
1660 const UChar
* retVal
= NULL
;
1661 ures_initStackObject(&stack
);
1662 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1663 retVal
= ures_getString(&stack
, len
, status
);
1668 U_CAPI UResourceBundle
* U_EXPORT2
1669 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1671 UResourceBundle
*fillIn
,
1672 UErrorCode
*status
) {
1673 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1674 /*UResourceDataEntry *realData = NULL;*/
1675 const char *key
= inKey
;
1676 UResourceBundle
*helper
= NULL
;
1679 if (status
==NULL
|| U_FAILURE(*status
)) {
1683 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1687 type
= RES_GET_TYPE(resB
->fRes
);
1688 if(URES_IS_TABLE(type
)) {
1690 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1691 if(res
== RES_BOGUS
) {
1692 UResourceDataEntry
*dataEntry
= resB
->fData
;
1694 char* myPath
= path
;
1695 const char* resPath
= resB
->fResPath
;
1696 int32_t len
= resB
->fResPathLen
;
1698 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1699 dataEntry
= dataEntry
->fParent
;
1700 rootRes
= dataEntry
->fData
.rootRes
;
1702 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1703 uprv_strncpy(path
, resPath
, len
);
1704 uprv_strcpy(path
+len
, inKey
);
1708 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1709 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1710 /* We hit an alias, but we didn't finish following the path. */
1711 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1712 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1714 dataEntry
= helper
->fData
;
1715 rootRes
= helper
->fRes
;
1716 resPath
= helper
->fResPath
;
1717 len
= helper
->fResPathLen
;
1723 } while(*myPath
); /* Continue until the whole path is consumed */
1726 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1727 if(res
!= RES_BOGUS
) {
1728 /* check if resB->fResPath gives the right name here */
1729 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1730 *status
= U_USING_DEFAULT_WARNING
;
1732 *status
= U_USING_FALLBACK_WARNING
;
1735 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1737 *status
= U_MISSING_RESOURCE_ERROR
;
1740 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1744 *status
= U_RESOURCE_TYPE_MISMATCH
;
1751 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1752 Resource res
= RES_BOGUS
;
1753 UResourceDataEntry
*realData
= NULL
;
1754 const char *key
= inKey
;
1757 if (status
==NULL
|| U_FAILURE(*status
)) {
1761 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1765 type
= RES_GET_TYPE(resB
->fRes
);
1766 if(URES_IS_TABLE(type
)) {
1768 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1769 if(res
== RES_BOGUS
) {
1771 if(resB
->fHasFallback
== TRUE
) {
1772 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1773 if(U_SUCCESS(*status
)) {
1774 /* check if resB->fResPath gives the right name here */
1775 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1777 *status
= U_MISSING_RESOURCE_ERROR
;
1780 *status
= U_MISSING_RESOURCE_ERROR
;
1783 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1787 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1789 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1790 /* here should go a first attempt to locate the key using index table */
1791 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1792 if(U_SUCCESS(*status
)) {
1793 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1795 *status
= U_MISSING_RESOURCE_ERROR
;
1800 *status
= U_RESOURCE_TYPE_MISMATCH
;
1805 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1806 Resource res
= RES_BOGUS
;
1807 UResourceDataEntry
*realData
= NULL
;
1808 const char* key
= inKey
;
1811 if (status
==NULL
|| U_FAILURE(*status
)) {
1815 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1819 type
= RES_GET_TYPE(resB
->fRes
);
1820 if(URES_IS_TABLE(type
)) {
1823 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1825 if(res
== RES_BOGUS
) {
1827 if(resB
->fHasFallback
== TRUE
) {
1828 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1829 if(U_SUCCESS(*status
)) {
1830 switch (RES_GET_TYPE(res
)) {
1832 case URES_STRING_V2
:
1833 return res_getString(rd
, res
, len
);
1836 const UChar
* result
= 0;
1837 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1838 result
= ures_getString(tempRes
, len
, status
);
1839 ures_close(tempRes
);
1843 *status
= U_RESOURCE_TYPE_MISMATCH
;
1846 *status
= U_MISSING_RESOURCE_ERROR
;
1849 *status
= U_MISSING_RESOURCE_ERROR
;
1852 switch (RES_GET_TYPE(res
)) {
1854 case URES_STRING_V2
:
1855 return res_getString(&(resB
->fResData
), res
, len
);
1858 const UChar
* result
= 0;
1859 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1860 result
= ures_getString(tempRes
, len
, status
);
1861 ures_close(tempRes
);
1865 *status
= U_RESOURCE_TYPE_MISMATCH
;
1870 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1872 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1873 /* here should go a first attempt to locate the key using index table */
1874 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1875 if(U_SUCCESS(*status
)) {
1876 return res_getString(rd
, res
, len
);
1878 *status
= U_MISSING_RESOURCE_ERROR
;
1883 *status
= U_RESOURCE_TYPE_MISMATCH
;
1888 U_CAPI
const char * U_EXPORT2
1889 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
1891 char *dest
, int32_t *pLength
,
1893 UErrorCode
*status
) {
1895 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
1896 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1899 /* TODO: clean from here down */
1902 * INTERNAL: Get the name of the first real locale (not placeholder)
1903 * that has resource bundle data.
1905 U_INTERNAL
const char* U_EXPORT2
1906 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1908 if (status
==NULL
|| U_FAILURE(*status
)) {
1911 if (!resourceBundle
) {
1912 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1915 return resourceBundle
->fData
->fName
;
1919 U_CAPI
const char* U_EXPORT2
1920 ures_getLocale(const UResourceBundle
* resourceBundle
,
1923 return ures_getLocaleInternal(resourceBundle
, status
);
1927 U_CAPI
const char* U_EXPORT2
1928 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1929 ULocDataLocaleType type
,
1930 UErrorCode
* status
) {
1931 if (status
==NULL
|| U_FAILURE(*status
)) {
1934 if (!resourceBundle
) {
1935 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1939 case ULOC_ACTUAL_LOCALE
:
1940 return resourceBundle
->fData
->fName
;
1941 case ULOC_VALID_LOCALE
:
1942 return resourceBundle
->fTopLevelData
->fName
;
1943 case ULOC_REQUESTED_LOCALE
:
1946 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1952 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
1957 return resB
->fData
->fName
;
1961 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
1966 return resB
->fData
->fPath
;
1970 /* OLD API implementation */
1973 * API: This function is used to open a resource bundle
1974 * proper fallback chaining is executed while initialization.
1975 * The result is stored in cache for later fallback search.
1977 U_CAPI
void U_EXPORT2
1978 ures_openFillIn(UResourceBundle
*r
, const char* path
,
1979 const char* localeID
, UErrorCode
* status
) {
1981 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1983 UResourceDataEntry
*firstData
;
1984 UBool isStackObject
= ures_isStackObject(r
);
1986 ures_closeBundle(r
, FALSE
);
1987 uprv_memset(r
, 0, sizeof(UResourceBundle
));
1988 ures_setIsStackObject(r
, isStackObject
);
1989 r
->fHasFallback
= TRUE
;
1990 r
->fIsTopLevel
= TRUE
;
1992 r
->fData
= entryOpen(path
, localeID
, status
);
1993 if(U_FAILURE(*status
)) {
1996 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
1997 firstData
= r
->fData
;
1998 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
1999 firstData
= firstData
->fParent
;
2001 uprv_memcpy(&r
->fResData
, &firstData
->fData
, sizeof(ResourceData
));
2002 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2003 r
->fRes
= r
->fResData
.rootRes
;
2004 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2005 r
->fTopLevelData
= r
->fData
;
2009 U_CAPI UResourceBundle
* U_EXPORT2
2010 ures_open(const char* path
,
2011 const char* localeID
,
2014 char canonLocaleID
[100];
2015 UResourceDataEntry
*hasData
= NULL
;
2018 if(status
== NULL
|| U_FAILURE(*status
)) {
2022 /* first "canonicalize" the locale ID */
2023 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2024 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2025 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2029 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2031 *status
= U_MEMORY_ALLOCATION_ERROR
;
2035 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2036 r
->fHasFallback
= TRUE
;
2037 r
->fIsTopLevel
= TRUE
;
2038 ures_setIsStackObject(r
, FALSE
);
2040 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2041 if(U_FAILURE(*status
)) {
2045 r
->fTopLevelData
= r
->fData
;
2048 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
2049 hasData
= hasData
->fParent
;
2050 if(hasData
== NULL
) {
2051 /* This can happen only if fallback chain gets broken by an act of God */
2052 /* TODO: this unlikely to happen, consider removing it */
2053 entryClose(r
->fData
);
2055 *status
= U_MISSING_RESOURCE_ERROR
;
2060 uprv_memcpy(&r
->fResData
, &hasData
->fData
, sizeof(ResourceData
));
2061 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2062 r
->fRes
= r
->fResData
.rootRes
;
2063 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2065 if(r->fData->fPath != NULL) {
2066 ures_setResPath(r, r->fData->fPath);
2067 ures_appendResPath(r, RES_PATH_PACKAGE_S);
2068 ures_appendResPath(r, r->fData->fName);
2070 ures_setResPath(r, r->fData->fName);
2079 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2080 * or sought. However, alias substitution will happen!
2082 U_CAPI UResourceBundle
* U_EXPORT2
2083 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2085 UErrorCode subStatus
= U_ZERO_ERROR
;
2087 if(status
== NULL
|| U_FAILURE(*status
)) {
2091 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2093 *status
= U_MEMORY_ALLOCATION_ERROR
;
2097 r
->fHasFallback
= FALSE
;
2098 r
->fIsTopLevel
= TRUE
;
2099 ures_setIsStackObject(r
, FALSE
);
2101 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
2102 if(U_FAILURE(subStatus
)) {
2103 *status
= subStatus
;
2107 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
2108 /* we didn't find one we were looking for - so openDirect */
2110 entryClose(r
->fData
);
2112 *status
= U_MISSING_RESOURCE_ERROR
;
2118 uprv_memcpy(&r
->fResData
, &r
->fData
->fData
, sizeof(ResourceData
));
2119 /* r->fHasFallback remains FALSE here in ures_openDirect() */
2120 r
->fRes
= r
->fResData
.rootRes
;
2121 /*r->fParent = RES_BOGUS;*/
2122 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2125 /*r->fParentRes = NULL;*/
2126 r
->fTopLevelData
= r
->fData
;
2132 * API: Counts members. For arrays and tables, returns number of resources.
2133 * For strings, returns 1.
2135 U_CAPI
int32_t U_EXPORT2
2136 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2137 const char* resourceKey
,
2140 UResourceBundle resData
;
2141 ures_initStackObject(&resData
);
2142 if (status
==NULL
|| U_FAILURE(*status
)) {
2145 if(resourceBundle
== NULL
) {
2146 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2149 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2151 if(resData
.fResData
.data
!= NULL
) {
2152 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2153 ures_close(&resData
);
2156 *status
= U_MISSING_RESOURCE_ERROR
;
2157 ures_close(&resData
);
2163 * Internal function.
2164 * Return the version number associated with this ResourceBundle as a string.
2166 * @param resourceBundle The resource bundle for which the version is checked.
2167 * @return A version number string as specified in the resource bundle or its parent.
2168 * The caller does not own this string.
2169 * @see ures_getVersion
2172 U_INTERNAL
const char* U_EXPORT2
2173 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2175 if (!resourceBundle
) return NULL
;
2177 if(resourceBundle
->fVersion
== NULL
) {
2179 /* If the version ID has not been built yet, then do so. Retrieve */
2180 /* the minor version from the file. */
2181 UErrorCode status
= U_ZERO_ERROR
;
2182 int32_t minor_len
= 0;
2185 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2187 /* Determine the length of of the final version string. This is */
2188 /* the length of the major part + the length of the separator */
2189 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2192 len
= (minor_len
> 0) ? minor_len
: 1;
2194 /* Allocate the string, and build it up. */
2195 /* + 1 for zero byte */
2198 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2199 /* Check for null pointer. */
2200 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2205 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2206 resourceBundle
->fVersion
[len
] = '\0';
2209 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2213 return resourceBundle
->fVersion
;
2216 U_CAPI
const char* U_EXPORT2
2217 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2219 return ures_getVersionNumberInternal(resourceBundle
);
2222 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2225 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2228 /** Tree support functions *******************************/
2229 #define INDEX_LOCALE_NAME "res_index"
2230 #define INDEX_TAG "InstalledLocales"
2231 #define DEFAULT_TAG "default"
2233 #if defined(URES_TREE_DEBUG)
2237 typedef struct ULocalesContext
{
2238 UResourceBundle installed
;
2239 UResourceBundle curr
;
2242 static void U_CALLCONV
2243 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2244 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2245 ures_close(&ctx
->curr
);
2246 ures_close(&ctx
->installed
);
2248 uprv_free(enumerator
);
2251 static int32_t U_CALLCONV
2252 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
*status
) {
2253 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2254 return ures_getSize(&ctx
->installed
);
2257 static const char* U_CALLCONV
2258 ures_loc_nextLocale(UEnumeration
* en
,
2259 int32_t* resultLength
,
2260 UErrorCode
* status
) {
2261 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2262 UResourceBundle
*res
= &(ctx
->installed
);
2263 UResourceBundle
*k
= NULL
;
2264 const char *result
= NULL
;
2266 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2267 result
= ures_getKey(k
);
2268 len
= (int32_t)uprv_strlen(result
);
2271 *resultLength
= len
;
2276 static void U_CALLCONV
2277 ures_loc_resetLocales(UEnumeration
* en
,
2278 UErrorCode
* status
) {
2279 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2280 ures_resetIterator(res
);
2284 static const UEnumeration gLocalesEnum
= {
2287 ures_loc_closeLocales
,
2288 ures_loc_countLocales
,
2290 ures_loc_nextLocale
,
2291 ures_loc_resetLocales
2295 U_CAPI UEnumeration
* U_EXPORT2
2296 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2298 UResourceBundle
*idx
= NULL
;
2299 UEnumeration
*en
= NULL
;
2300 ULocalesContext
*myContext
= NULL
;
2302 if(U_FAILURE(*status
)) {
2305 myContext
= uprv_malloc(sizeof(ULocalesContext
));
2306 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2307 if(!en
|| !myContext
) {
2308 *status
= U_MEMORY_ALLOCATION_ERROR
;
2310 uprv_free(myContext
);
2313 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2315 ures_initStackObject(&myContext
->installed
);
2316 ures_initStackObject(&myContext
->curr
);
2317 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2318 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2319 if(U_SUCCESS(*status
)) {
2320 #if defined(URES_TREE_DEBUG)
2321 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2322 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2324 en
->context
= myContext
;
2326 #if defined(URES_TREE_DEBUG)
2327 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2329 ures_close(&myContext
->installed
);
2330 uprv_free(myContext
);
2340 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2342 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2343 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2350 U_CAPI
int32_t U_EXPORT2
2351 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2352 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2353 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2355 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2356 char defVal
[1024] = ""; /* default value for given locale */
2357 char defLoc
[1024] = ""; /* default value for given locale */
2358 char base
[1024] = ""; /* base locale */
2361 char full
[1024] = "";
2362 UResourceBundle bund1
, bund2
;
2363 UResourceBundle
*res
= NULL
;
2364 UErrorCode subStatus
= U_ZERO_ERROR
;
2366 if(U_FAILURE(*status
)) return 0;
2367 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2368 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2371 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2372 #if defined(URES_TREE_DEBUG)
2373 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2374 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2376 ures_initStackObject(&bund1
);
2377 ures_initStackObject(&bund2
);
2380 uprv_strcpy(parent
, base
);
2381 uprv_strcpy(found
, base
);
2384 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2385 *isAvailable
= TRUE
;
2386 if (U_SUCCESS(subStatus
)) {
2387 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2389 uenum_close(locEnum
);
2392 if(U_FAILURE(subStatus
)) {
2393 *status
= subStatus
;
2398 subStatus
= U_ZERO_ERROR
;
2399 res
= ures_open(path
, parent
, &subStatus
);
2400 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2401 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2403 *isAvailable
= FALSE
;
2405 isAvailable
= NULL
; /* only want to set this the first time around */
2407 #if defined(URES_TREE_DEBUG)
2408 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2410 if(U_FAILURE(subStatus
)) {
2411 *status
= subStatus
;
2412 } else if(subStatus
== U_ZERO_ERROR
) {
2413 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2414 if(subStatus
== U_ZERO_ERROR
) {
2415 const UChar
*defUstr
;
2417 /* look for default item */
2418 #if defined(URES_TREE_DEBUG)
2419 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2420 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2422 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2423 if(U_SUCCESS(subStatus
) && defLen
) {
2424 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2425 #if defined(URES_TREE_DEBUG)
2426 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2427 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2429 uprv_strcpy(defLoc
, parent
);
2431 uprv_strcpy(kwVal
, defVal
);
2432 #if defined(URES_TREE_DEBUG)
2433 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2434 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2441 subStatus
= U_ZERO_ERROR
;
2444 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2447 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2449 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2451 /* Now, see if we can find the kwVal collator.. start the search over.. */
2452 uprv_strcpy(parent
, base
);
2453 uprv_strcpy(found
, base
);
2456 subStatus
= U_ZERO_ERROR
;
2457 res
= ures_open(path
, parent
, &subStatus
);
2458 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2459 *isAvailable
= FALSE
;
2461 isAvailable
= NULL
; /* only want to set this the first time around */
2463 #if defined(URES_TREE_DEBUG)
2464 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2465 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2467 if(U_FAILURE(subStatus
)) {
2468 *status
= subStatus
;
2469 } else if(subStatus
== U_ZERO_ERROR
) {
2470 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2471 #if defined(URES_TREE_DEBUG)
2472 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2474 if(subStatus
== U_ZERO_ERROR
) {
2475 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2476 #if defined(URES_TREE_DEBUG)
2477 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2479 if(subStatus
== U_ZERO_ERROR
) {
2480 #if defined(URES_TREE_DEBUG)
2481 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2482 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2484 uprv_strcpy(full
, parent
);
2486 uprv_strcpy(full
, "root");
2488 /* now, recalculate default kw if need be */
2489 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2490 const UChar
*defUstr
;
2492 /* look for default item */
2493 #if defined(URES_TREE_DEBUG)
2494 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2495 path
?path
:"ICUDATA", full
);
2497 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2498 if(U_SUCCESS(subStatus
) && defLen
) {
2499 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2500 #if defined(URES_TREE_DEBUG)
2501 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2502 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2504 uprv_strcpy(defLoc
, full
);
2506 } /* end of recalculate default KW */
2507 #if defined(URES_TREE_DEBUG)
2509 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2513 #if defined(URES_TREE_DEBUG)
2514 fprintf(stderr
, "err=%s in %s looking for %s\n",
2515 u_errorName(subStatus
), parent
, kwVal
);
2521 subStatus
= U_ZERO_ERROR
;
2523 uprv_strcpy(found
, parent
);
2524 uloc_getParent(found
,parent
,1023,&subStatus
);
2526 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2528 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2529 #if defined(URES_TREE_DEBUG)
2530 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2532 uprv_strcpy(kwVal
, defVal
);
2533 uprv_strcpy(parent
, base
);
2534 uprv_strcpy(found
, base
);
2536 do { /* search for 'default' named item */
2537 subStatus
= U_ZERO_ERROR
;
2538 res
= ures_open(path
, parent
, &subStatus
);
2539 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2540 *isAvailable
= FALSE
;
2542 isAvailable
= NULL
; /* only want to set this the first time around */
2544 #if defined(URES_TREE_DEBUG)
2545 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2546 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2548 if(U_FAILURE(subStatus
)) {
2549 *status
= subStatus
;
2550 } else if(subStatus
== U_ZERO_ERROR
) {
2551 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2552 if(subStatus
== U_ZERO_ERROR
) {
2553 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2554 if(subStatus
== U_ZERO_ERROR
) {
2555 #if defined(URES_TREE_DEBUG)
2556 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2557 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2559 uprv_strcpy(full
, parent
);
2561 uprv_strcpy(full
, "root");
2564 /* now, recalculate default kw if need be */
2565 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2566 const UChar
*defUstr
;
2568 /* look for default item */
2569 #if defined(URES_TREE_DEBUG)
2570 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2571 path
?path
:"ICUDATA", full
);
2573 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2574 if(U_SUCCESS(subStatus
) && defLen
) {
2575 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2576 #if defined(URES_TREE_DEBUG)
2577 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2578 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2580 uprv_strcpy(defLoc
, full
);
2582 } /* end of recalculate default KW */
2583 #if defined(URES_TREE_DEBUG)
2585 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2591 subStatus
= U_ZERO_ERROR
;
2593 uprv_strcpy(found
, parent
);
2594 uloc_getParent(found
,parent
,1023,&subStatus
);
2596 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2599 if(U_SUCCESS(*status
)) {
2601 #if defined(URES_TREE_DEBUG)
2602 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2604 *status
= U_MISSING_RESOURCE_ERROR
;
2605 } else if(omitDefault
) {
2606 #if defined(URES_TREE_DEBUG)
2607 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2609 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2610 /* found the keyword in a *child* of where the default tag was present. */
2611 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2612 /* and the default is in or in an ancestor of the current locale */
2613 #if defined(URES_TREE_DEBUG)
2614 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2620 uprv_strcpy(found
, full
);
2622 uprv_strcat(found
, "@");
2623 uprv_strcat(found
, keyword
);
2624 uprv_strcat(found
, "=");
2625 uprv_strcat(found
, kwVal
);
2626 } else if(!omitDefault
) {
2627 uprv_strcat(found
, "@");
2628 uprv_strcat(found
, keyword
);
2629 uprv_strcat(found
, "=");
2630 uprv_strcat(found
, defVal
);
2633 /* we found the default locale - no need to repeat it.*/
2638 length
= (int32_t)uprv_strlen(found
);
2640 if(U_SUCCESS(*status
)) {
2641 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2643 uprv_strncpy(result
, found
, copyLength
);
2646 *status
= U_MISSING_RESOURCE_ERROR
;
2652 return u_terminateChars(result
, resultCapacity
, length
, status
);
2655 U_CAPI UEnumeration
* U_EXPORT2
2656 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2658 #define VALUES_BUF_SIZE 2048
2659 #define VALUES_LIST_SIZE 512
2661 char valuesBuf
[VALUES_BUF_SIZE
];
2662 int32_t valuesIndex
= 0;
2663 const char *valuesList
[VALUES_LIST_SIZE
];
2664 int32_t valuesCount
= 0;
2669 UEnumeration
*locs
= NULL
;
2671 UResourceBundle item
;
2672 UResourceBundle subItem
;
2674 ures_initStackObject(&item
);
2675 ures_initStackObject(&subItem
);
2676 locs
= ures_openAvailableLocales(path
, status
);
2678 if(U_FAILURE(*status
)) {
2680 ures_close(&subItem
);
2687 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2688 UResourceBundle
*bund
= NULL
;
2689 UResourceBundle
*subPtr
= NULL
;
2690 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2691 bund
= ures_openDirect(path
, locale
, &subStatus
);
2693 #if defined(URES_TREE_DEBUG)
2694 if(!bund
|| U_FAILURE(subStatus
)) {
2695 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2696 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2700 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2702 if(!bund
|| U_FAILURE(subStatus
)) {
2703 #if defined(URES_TREE_DEBUG)
2704 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2705 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2712 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2713 && U_SUCCESS(subStatus
)) {
2716 k
= ures_getKey(subPtr
);
2718 #if defined(URES_TREE_DEBUG)
2719 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2721 for(i
=0;k
&&i
<valuesCount
;i
++) {
2722 if(!uprv_strcmp(valuesList
[i
],k
)) {
2723 k
= NULL
; /* found duplicate */
2727 int32_t kLen
= (int32_t)uprv_strlen(k
);
2728 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2729 continue; /* don't need 'default'. */
2731 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2732 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2733 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2735 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2736 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2737 valuesIndex
+= kLen
;
2738 #if defined(URES_TREE_DEBUG)
2739 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2740 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2742 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2748 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2751 ures_close(&subItem
);
2753 #if defined(URES_TREE_DEBUG)
2754 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2755 valuesIndex
, valuesCount
);
2757 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2760 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2761 U_INTERNAL UBool U_EXPORT2
2762 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2763 if(res1
==NULL
|| res2
==NULL
){
2764 return res1
==res2
; /* pointer comparision */
2766 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2767 return (res1
->fKey
==res2
->fKey
);
2769 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2773 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2776 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2777 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2779 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2783 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2786 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2789 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2792 if(res1
->fRes
!= res2
->fRes
){
2797 U_INTERNAL UResourceBundle
* U_EXPORT2
2798 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2799 UResourceBundle
* bundle
= NULL
;
2800 UResourceBundle
* ret
= NULL
;
2801 if(U_FAILURE(*status
) || res
== NULL
){
2804 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2805 if(res
->fResPath
!=NULL
){
2806 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2813 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2814 ures_getParentBundle(const UResourceBundle
* res
){
2818 return res
->fParentRes
;
2822 U_INTERNAL
void U_EXPORT2
2823 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
2826 str
= ures_getStringByKey(res
, key
, &len
, status
);
2827 if(U_SUCCESS(*status
)) {
2828 u_versionFromUString(ver
, str
);