2 ******************************************************************************
3 * Copyright (C) 1997-2013, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 04/01/97 aliu Creation.
13 * 06/14/99 stephen Removed functions taking a filename suffix.
14 * 07/20/99 stephen Changed for UResourceBundle typedef'd to void*
15 * 11/09/99 weiv Added ures_getLocale()
16 * March 2000 weiv Total overhaul - using data in DLLs
17 * 06/20/2000 helena OS/400 port changes; mostly typecast.
18 * 06/24/02 weiv Added support for resource sharing
19 ******************************************************************************
22 #include "unicode/ustring.h"
23 #include "unicode/ucnv.h"
32 #include "unicode/uenum.h"
41 Static cache for already opened resource bundles - mostly for keeping fallback info
42 TODO: This cache should probably be removed when the deprecated code is
45 static UHashtable
*cache
= NULL
;
46 static icu::UInitOnce gCacheInitOnce
;
48 static UMutex resbMutex
= U_MUTEX_INITIALIZER
;
50 /* INTERNAL: hashes an entry */
51 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
52 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
53 UHashTok namekey
, pathkey
;
54 namekey
.pointer
= b
->fName
;
55 pathkey
.pointer
= b
->fPath
;
56 return uhash_hashChars(namekey
)+37*uhash_hashChars(pathkey
);
59 /* INTERNAL: compares two entries */
60 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
61 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
62 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
63 UHashTok name1
, name2
, path1
, path2
;
64 name1
.pointer
= b1
->fName
;
65 name2
.pointer
= b2
->fName
;
66 path1
.pointer
= b1
->fPath
;
67 path2
.pointer
= b2
->fPath
;
68 return (UBool
)(uhash_compareChars(name1
, name2
) &&
69 uhash_compareChars(path1
, path2
));
74 * Internal function, gets parts of locale name according
75 * to the position of '_' character
77 static UBool
chopLocale(char *name
) {
78 char *i
= uprv_strrchr(name
, '_');
91 static void entryIncrease(UResourceDataEntry
*entry
) {
92 umtx_lock(&resbMutex
);
93 entry
->fCountExisting
++;
94 while(entry
->fParent
!= NULL
) {
95 entry
= entry
->fParent
;
96 entry
->fCountExisting
++;
98 umtx_unlock(&resbMutex
);
102 * Internal function. Tries to find a resource in given Resource
103 * Bundle, as well as in its parents
105 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
106 UResourceDataEntry
*resB
= resBundle
->fData
;
111 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
112 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
115 if(resBundle
->fHasFallback
== TRUE
) {
116 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
117 resB
= resB
->fParent
;
118 if(resB
->fBogus
== U_ZERO_ERROR
) {
120 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
125 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
127 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
128 *status
= U_USING_DEFAULT_WARNING
;
130 *status
= U_USING_FALLBACK_WARNING
;
134 return (&(resB
->fData
));
135 } else { /* If resource is not found, we need to give an error */
136 *status
= U_MISSING_RESOURCE_ERROR
;
140 *status
= U_MISSING_RESOURCE_ERROR
;
146 free_entry(UResourceDataEntry
*entry
) {
147 UResourceDataEntry
*alias
;
148 res_unload(&(entry
->fData
));
149 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
150 uprv_free(entry
->fName
);
152 if(entry
->fPath
!= NULL
) {
153 uprv_free(entry
->fPath
);
155 if(entry
->fPool
!= NULL
) {
156 --entry
->fPool
->fCountExisting
;
158 alias
= entry
->fAlias
;
160 while(alias
->fAlias
!= NULL
) {
161 alias
= alias
->fAlias
;
163 --alias
->fCountExisting
;
168 /* Works just like ucnv_flushCache() */
169 static int32_t ures_flushCache()
171 UResourceDataEntry
*resB
;
173 int32_t rbDeletedNum
= 0;
174 const UHashElement
*e
;
177 /*if shared data hasn't even been lazy evaluated yet
180 umtx_lock(&resbMutex
);
182 umtx_unlock(&resbMutex
);
188 /*creates an enumeration to iterate through every element in the table */
190 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
192 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
193 /* Deletes only if reference counter == 0
194 * Don't worry about the children of this node.
195 * Those will eventually get deleted too, if not already.
196 * Don't worry about the parents of this node.
197 * Those will eventually get deleted too, if not already.
199 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
200 /* some resource bundles are still open somewhere. */
202 if (resB
->fCountExisting
== 0) {
205 uhash_removeElement(cache
, e
);
210 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
211 * got decremented by free_entry().
213 } while(deletedMore
);
214 umtx_unlock(&resbMutex
);
222 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
223 UBool cacheNotEmpty
= FALSE
;
225 const UHashElement
*e
;
226 UResourceDataEntry
*resB
;
228 umtx_lock(&resbMutex
);
230 umtx_unlock(&resbMutex
);
231 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
235 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
237 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
238 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
240 (void*)resB
, resB
->fCountExisting
,
241 resB
->fName
?resB
->fName
:"NULL",
242 resB
->fPath
?resB
->fPath
:"NULL",
245 (void*)resB
->fParent
);
248 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
250 umtx_unlock(&resbMutex
);
252 return cacheNotEmpty
;
257 static UBool U_CALLCONV
ures_cleanup(void)
264 gCacheInitOnce
.reset();
268 /** INTERNAL: Initializes the cache for resources */
269 static void createCache(UErrorCode
&status
) {
270 U_ASSERT(cache
== NULL
);
271 cache
= uhash_open(hashEntry
, compareEntries
, NULL
, &status
);
272 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
275 static void initCache(UErrorCode
*status
) {
276 umtx_initOnce(gCacheInitOnce
, &createCache
, *status
);
279 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
281 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
282 int32_t len
= (int32_t)uprv_strlen(name
);
283 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
284 uprv_free(res
->fName
);
286 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
287 res
->fName
= res
->fNameBuffer
;
290 res
->fName
= (char *)uprv_malloc(len
+1);
292 if(res
->fName
== NULL
) {
293 *status
= U_MEMORY_ALLOCATION_ERROR
;
295 uprv_strcpy(res
->fName
, name
);
299 static UResourceDataEntry
*
300 getPoolEntry(const char *path
, UErrorCode
*status
);
303 * INTERNAL: Inits and opens an entry from a data DLL.
304 * CAUTION: resbMutex must be locked when calling this function.
306 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
307 UResourceDataEntry
*r
= NULL
;
308 UResourceDataEntry find
;
309 /*int32_t hashValue;*/
311 char aliasName
[100] = { 0 };
312 int32_t aliasLen
= 0;
313 /*UBool isAlias = FALSE;*/
314 /*UHashTok hashkey; */
316 if(U_FAILURE(*status
)) {
320 /* here we try to deduce the right locale name */
321 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
322 name
= uloc_getDefault();
323 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
324 name
= kRootLocaleName
;
325 } else { /* otherwise, we'll open what we're given */
329 find
.fName
= (char *)name
;
330 find
.fPath
= (char *)path
;
332 /* calculate the hash value of the entry */
333 /*hashkey.pointer = (void *)&find;*/
334 /*hashValue = hashEntry(hashkey);*/
336 /* check to see if we already have this entry */
337 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
339 /* if the entry is not yet in the hash table, we'll try to construct a new one */
340 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
342 *status
= U_MEMORY_ALLOCATION_ERROR
;
346 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
347 /*r->fHashKey = hashValue;*/
349 setEntryName(r
, name
, status
);
350 if (U_FAILURE(*status
)) {
356 r
->fPath
= (char *)uprv_strdup(path
);
357 if(r
->fPath
== NULL
) {
358 *status
= U_MEMORY_ALLOCATION_ERROR
;
364 /* this is the actual loading */
365 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
367 if (U_FAILURE(*status
)) {
368 /* we have no such entry in dll, so it will always use fallback */
369 *status
= U_USING_FALLBACK_WARNING
;
370 r
->fBogus
= U_USING_FALLBACK_WARNING
;
371 } else { /* if we have a regular entry */
373 if (r
->fData
.usesPoolBundle
) {
374 r
->fPool
= getPoolEntry(r
->fPath
, status
);
375 if (U_SUCCESS(*status
)) {
376 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
377 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
378 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
380 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
386 if (U_SUCCESS(*status
)) {
387 /* handle the alias by trying to get out the %%Alias tag.*/
388 /* We'll try to get alias string from the bundle */
389 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
390 if (aliasres
!= RES_BOGUS
) {
391 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
392 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
393 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
394 r
->fAlias
= init_entry(aliasName
, path
, status
);
401 UResourceDataEntry
*oldR
= NULL
;
402 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
403 /* just insert it in the cache */
404 UErrorCode cacheStatus
= U_ZERO_ERROR
;
405 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
406 if (U_FAILURE(cacheStatus
)) {
407 *status
= cacheStatus
;
412 /* somebody have already inserted it while we were working, discard newly opened data */
413 /* Also, we could get here IF we opened an alias */
421 /* return the real bundle */
422 while(r
->fAlias
!= NULL
) {
425 r
->fCountExisting
++; /* we increase its reference count */
426 /* if the resource has a warning */
427 /* we don't want to overwrite a status with no error */
428 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
429 *status
= r
->fBogus
; /* set the returning status */
435 static UResourceDataEntry
*
436 getPoolEntry(const char *path
, UErrorCode
*status
) {
437 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
438 if( U_SUCCESS(*status
) &&
439 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
441 *status
= U_INVALID_FORMAT_ERROR
;
447 /* CAUTION: resbMutex must be locked when calling this function! */
448 static UResourceDataEntry
*findFirstExisting(const char* path
, char* name
, UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
449 UResourceDataEntry
*r
= NULL
;
450 UBool hasRealData
= FALSE
;
451 const char *defaultLoc
= uloc_getDefault();
452 *hasChopped
= TRUE
; /* we're starting with a fresh name */
454 while(*hasChopped
&& !hasRealData
) {
455 r
= init_entry(name
, path
, status
);
456 /* Null pointer test */
457 if (U_FAILURE(*status
)) {
460 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
461 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
463 /* this entry is not real. We will discard it. */
464 /* However, the parent line for this entry is */
465 /* not to be used - as there might be parent */
466 /* lines in cache from previous openings that */
467 /* are not updated yet. */
469 /*entryCloseInt(r);*/
471 *status
= U_USING_FALLBACK_WARNING
;
473 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
476 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
478 /*Fallback data stuff*/
479 *hasChopped
= chopLocale(name
);
484 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
489 resB
->fMagic1
= MAGIC1
;
490 resB
->fMagic2
= MAGIC2
;
494 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
495 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
499 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
500 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
501 ures_setIsStackObject(resB
, TRUE
);
504 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
, UErrorCode
* status
) {
505 UErrorCode intStatus
= U_ZERO_ERROR
;
506 UErrorCode parentStatus
= U_ZERO_ERROR
;
507 UErrorCode usrStatus
= U_ZERO_ERROR
;
508 UResourceDataEntry
*r
= NULL
;
509 UResourceDataEntry
*t1
= NULL
;
510 UResourceDataEntry
*t2
= NULL
;
511 UResourceDataEntry
*u1
= NULL
;
512 UResourceDataEntry
*u2
= NULL
;
513 UBool isDefault
= FALSE
;
514 UBool isRoot
= FALSE
;
515 UBool hasRealData
= FALSE
;
516 UBool hasChopped
= TRUE
;
517 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
519 char name
[ULOC_FULLNAME_CAPACITY
];
520 char usrDataPath
[96];
524 if(U_FAILURE(*status
)) {
528 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
529 name
[sizeof(name
) - 1] = 0;
531 if ( usingUSRData
) {
532 if ( path
== NULL
) {
533 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
535 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
536 usrDataPath
[0] = 'u';
537 usrDataPath
[1] = 's';
538 usrDataPath
[2] = 'r';
539 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
543 umtx_lock(&resbMutex
);
545 /* We're going to skip all the locales that do not have any data */
546 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
548 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
551 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
552 u1
= init_entry(t1
->fName
, usrDataPath
, &usrStatus
);
554 if(u1
->fBogus
== U_ZERO_ERROR
) {
558 /* the USR override data wasn't found, set it to be deleted */
559 u1
->fCountExisting
= 0;
563 while (hasChopped
&& !isRoot
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
) {
564 if ( res_getResource(&t1
->fData
,"%%Parent") != RES_BOGUS
) { /* An explicit parent was found */
565 int32_t parentLocaleLen
= 0;
566 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), res_getResource(&t1
->fData
,"%%Parent") , &parentLocaleLen
);
567 if(parentLocaleName
!= NULL
&& parentLocaleLen
> 0) {
568 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+1);
569 if ( !uprv_strcmp(name
,"root") ) { /* If parent is root, we just terminate the loop */
575 /* insert regular parents */
576 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
577 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
578 usrStatus
= U_ZERO_ERROR
;
579 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
581 /* Check for null pointer. */
582 if (t2
== NULL
|| ( usingUSRData
&& u2
== NULL
)) {
583 *status
= U_MEMORY_ALLOCATION_ERROR
;
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;
598 hasChopped
= chopLocale(name
);
602 /* we could have reached this point without having any real data */
603 /* if that is the case, we need to chain in the default locale */
604 if(r
==NULL
&& !isDefault
&& !isRoot
/*&& t1->fParent == NULL*/) {
605 /* insert default locale */
606 uprv_strcpy(name
, uloc_getDefault());
607 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
608 intStatus
= U_USING_DEFAULT_WARNING
;
609 if(r
!= NULL
) { /* the default locale exists */
613 while (hasChopped
&& t1
->fParent
== NULL
) {
614 if ( res_getResource(&t1
->fData
,"%%Parent") != RES_BOGUS
) { /* An explicit parent was found */
615 int32_t parentLocaleLen
= 0;
616 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), res_getResource(&t1
->fData
,"%%Parent") , &parentLocaleLen
);
617 if(parentLocaleName
!= NULL
&& parentLocaleLen
> 0) {
618 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+1);
619 if ( !uprv_strcmp(name
,"root") ) { /* If parent is root, we just terminate the loop */
625 /* insert chopped defaults */
626 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
627 /* Check for null pointer. */
629 *status
= U_MEMORY_ALLOCATION_ERROR
;
633 if ( res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
637 hasChopped
= chopLocale(name
);
642 /* we could still have r == NULL at this point - maybe even default locale is not */
645 uprv_strcpy(name
, kRootLocaleName
);
646 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
649 intStatus
= U_USING_DEFAULT_WARNING
;
651 } else { /* we don't even have the root locale */
652 *status
= U_MISSING_RESOURCE_ERROR
;
655 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
656 /* insert root locale */
657 t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
658 /* Check for null pointer. */
660 *status
= U_MEMORY_ALLOCATION_ERROR
;
664 r
->fBogus
= U_USING_DEFAULT_WARNING
;
666 hasRealData
= (UBool
)((t2
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
671 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
672 t1
->fParent
->fCountExisting
++;
674 hasRealData
= (UBool
)((t1
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
678 umtx_unlock(&resbMutex
);
680 if(U_SUCCESS(*status
)) {
681 if(U_SUCCESS(parentStatus
)) {
682 if(intStatus
!= U_ZERO_ERROR
) {
687 *status
= parentStatus
;
697 * Functions to create and destroy resource bundles.
698 * CAUTION: resbMutex must be locked when calling this function.
701 static void entryCloseInt(UResourceDataEntry
*resB
) {
702 UResourceDataEntry
*p
= resB
;
704 while(resB
!= NULL
) {
706 resB
->fCountExisting
--;
708 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
711 if(resB->fCountExisting <= 0) {
712 uhash_remove(cache, resB);
713 if(resB->fBogus == U_ZERO_ERROR) {
714 res_unload(&(resB->fData));
716 if(resB->fName != NULL) {
717 uprv_free(resB->fName);
719 if(resB->fPath != NULL) {
720 uprv_free(resB->fPath);
731 * API: closes a resource bundle and cleans up.
734 static void entryClose(UResourceDataEntry
*resB
) {
735 umtx_lock(&resbMutex
);
737 umtx_unlock(&resbMutex
);
741 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
742 if(resB->fResPath == NULL) {
743 resB->fResPath = resB->fResBuf;
744 *(resB->fResPath) = 0;
746 resB->fResPathLen = uprv_strlen(toAdd);
747 if(RES_BUFSIZE <= resB->fResPathLen+1) {
748 if(resB->fResPath == resB->fResBuf) {
749 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
751 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
754 uprv_strcpy(resB->fResPath, toAdd);
757 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
758 int32_t resPathLenOrig
= resB
->fResPathLen
;
759 if(resB
->fResPath
== NULL
) {
760 resB
->fResPath
= resB
->fResBuf
;
761 *(resB
->fResPath
) = 0;
762 resB
->fResPathLen
= 0;
764 resB
->fResPathLen
+= lenToAdd
;
765 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
766 if(resB
->fResPath
== resB
->fResBuf
) {
767 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
768 /* Check that memory was allocated correctly. */
769 if (resB
->fResPath
== NULL
) {
770 *status
= U_MEMORY_ALLOCATION_ERROR
;
773 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
775 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
776 /* Check that memory was reallocated correctly. */
778 *status
= U_MEMORY_ALLOCATION_ERROR
;
781 resB
->fResPath
= temp
;
784 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
787 static void ures_freeResPath(UResourceBundle
*resB
) {
788 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
789 uprv_free(resB
->fResPath
);
791 resB
->fResPath
= NULL
;
792 resB
->fResPathLen
= 0;
796 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
799 if(resB
->fData
!= NULL
) {
800 entryClose(resB
->fData
);
802 if(resB
->fVersion
!= NULL
) {
803 uprv_free(resB
->fVersion
);
805 ures_freeResPath(resB
);
807 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
812 /* poison the data */
813 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
819 U_CAPI
void U_EXPORT2
820 ures_close(UResourceBundle
* resB
)
822 ures_closeBundle(resB
, TRUE
);
825 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
826 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
827 const UResourceBundle
*parent
, int32_t noAlias
,
828 UResourceBundle
*resB
, UErrorCode
*status
)
830 if(status
== NULL
|| U_FAILURE(*status
)) {
833 if (parent
== NULL
) {
834 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
837 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
838 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
840 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
842 /* we have an alias, now let's cut it up */
843 char stackAlias
[200];
844 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
848 * Allocate enough space for both the char * version
849 * of the alias and parent->fResPath.
851 * We do this so that res_findResource() can modify the path,
852 * which allows us to remove redundant _res_findResource() variants
854 * res_findResource() now NUL-terminates each segment so that table keys
855 * can always be compared with strcmp() instead of strncmp().
856 * Saves code there and simplifies testing and code coverage.
860 ++len
; /* count the terminating NUL */
861 if(parent
->fResPath
!= NULL
) {
862 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
869 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
870 capacity
= (int32_t)sizeof(stackAlias
);
871 chAlias
= stackAlias
;
873 chAlias
= (char *)uprv_malloc(capacity
);
875 if(chAlias
== NULL
) {
876 *status
= U_MEMORY_ALLOCATION_ERROR
;
880 u_UCharsToChars(alias
, chAlias
, len
);
882 if(*chAlias
== RES_PATH_SEPARATOR
) {
883 /* there is a path included */
884 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
886 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
892 if(uprv_strcmp(path
, "LOCALE") == 0) {
893 /* this is an XPath alias, starting with "/LOCALE/" */
894 /* it contains the path to a resource which should be looked up */
895 /* starting in the requested locale */
897 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
898 path
= realData
->fPath
; /* we will be looking in the same package */
900 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
903 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
910 /* no path, start with a locale */
912 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
917 path
= realData
->fPath
;
922 /* got almost everything, let's try to open */
923 /* first, open the bundle with real data */
924 UResourceBundle
*result
= resB
;
925 const char* temp
= NULL
;
926 UErrorCode intStatus
= U_ZERO_ERROR
;
927 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
928 if(U_SUCCESS(intStatus
)) {
929 if(keyPath
== NULL
) {
930 /* no key path. This means that we are going to
931 * to use the corresponding resource from
934 /* first, we are going to get a corresponding parent
935 * resource to the one we are searching.
937 char *aKey
= parent
->fResPath
;
939 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
941 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
946 /* we need to make keyPath from parent's fResPath and
947 * current key, if there is a key associated
949 len
= (int32_t)(uprv_strlen(key
) + 1);
952 if(chAlias
== stackAlias
) {
953 chAlias
= (char *)uprv_malloc(capacity
);
955 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
957 if(chAlias
== NULL
) {
959 *status
= U_MEMORY_ALLOCATION_ERROR
;
963 uprv_memcpy(chAlias
, key
, len
);
965 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
966 } else if(idx
!= -1) {
967 /* if there is no key, but there is an index, try to get by the index */
968 /* here we have either a table or an array, so get the element */
969 int32_t type
= RES_GET_TYPE(r
);
970 if(URES_IS_TABLE(type
)) {
971 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
973 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
977 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
979 *status
= U_MISSING_RESOURCE_ERROR
;
983 /* this one is a bit trickier.
984 * we start finding keys, but after we resolve one alias, the path might continue.
986 * aliastest:alias { "testtypes/anotheralias/Sequence" }
987 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
988 * aliastest resource should finally have the sequence, not collation elements.
990 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
991 char stackPath
[URES_MAX_BUFFER_SIZE
];
992 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
993 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
994 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
995 if(pathBuf
== NULL
) {
996 *status
= U_MEMORY_ALLOCATION_ERROR
;
1000 uprv_strcpy(pathBuf
, keyPath
);
1002 /* now we have fallback following here */
1004 r
= dataEntry
->fData
.rootRes
;
1005 /* this loop handles 'found' resources over several levels */
1006 while(*myPath
&& U_SUCCESS(*status
)) {
1007 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1008 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1009 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1012 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1013 dataEntry
= result
->fData
;
1015 } else { /* no resource found, we don't really want to look anymore on this level */
1019 dataEntry
= dataEntry
->fParent
;
1020 uprv_strcpy(pathBuf
, keyPath
);
1022 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1023 if(r
== RES_BOGUS
) {
1024 *status
= U_MISSING_RESOURCE_ERROR
;
1027 if(pathBuf
!= stackPath
) {
1031 } else { /* we failed to open the resource we're aliasing to */
1032 *status
= intStatus
;
1034 if(chAlias
!= stackAlias
) {
1037 if(mainRes
!= result
) {
1038 ures_close(mainRes
);
1043 /* bad alias, should be an error */
1044 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1048 *status
= U_TOO_MANY_ALIASES_ERROR
;
1053 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1056 *status
= U_MEMORY_ALLOCATION_ERROR
;
1059 ures_setIsStackObject(resB
, FALSE
);
1060 resB
->fResPath
= NULL
;
1061 resB
->fResPathLen
= 0;
1063 if(resB
->fData
!= NULL
) {
1064 entryClose(resB
->fData
);
1066 if(resB
->fVersion
!= NULL
) {
1067 uprv_free(resB
->fVersion
);
1070 weiv: if stack object was passed in, it doesn't really need to be reinited,
1071 since the purpose of initing is to remove stack junk. However, at this point
1072 we would not do anything to an allocated object, so stack object should be
1076 if(ures_isStackObject(resB) != FALSE) {
1077 ures_initStackObject(resB);
1080 if(parent
!= resB
) {
1081 ures_freeResPath(resB
);
1084 resB
->fData
= realData
;
1085 entryIncrease(resB
->fData
);
1086 resB
->fHasFallback
= FALSE
;
1087 resB
->fIsTopLevel
= FALSE
;
1090 /*resB->fParentRes = parent;*/
1091 resB
->fTopLevelData
= parent
->fTopLevelData
;
1092 if(parent
->fResPath
&& parent
!= resB
) {
1093 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1096 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1097 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1098 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1100 } else if(idx
>= 0) {
1102 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1103 ures_appendResPath(resB
, buf
, len
, status
);
1104 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1105 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1108 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1110 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1111 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1114 resB
->fVersion
= NULL
;
1116 /*resB->fParent = parent->fRes;*/
1117 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1118 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1122 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1123 UBool isStackObject
;
1124 if(U_FAILURE(*status
) || r
== original
) {
1127 if(original
!= NULL
) {
1129 isStackObject
= FALSE
;
1130 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1133 *status
= U_MEMORY_ALLOCATION_ERROR
;
1137 isStackObject
= ures_isStackObject(r
);
1138 ures_closeBundle(r
, FALSE
);
1140 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1143 if(original
->fResPath
) {
1144 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1146 ures_setIsStackObject(r
, isStackObject
);
1147 if(r
->fData
!= NULL
) {
1148 entryIncrease(r
->fData
);
1155 * Functions to retrieve data from resource bundles.
1158 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1160 if (status
==NULL
|| U_FAILURE(*status
)) {
1164 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1167 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1169 *status
= U_RESOURCE_TYPE_MISMATCH
;
1175 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1176 char *dest
, int32_t *pLength
,
1178 UErrorCode
*status
) {
1181 if (U_FAILURE(*status
)) {
1184 if (pLength
!= NULL
) {
1185 capacity
= *pLength
;
1189 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1190 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1194 if (length16
== 0) {
1195 /* empty string, return as read-only pointer */
1196 if (pLength
!= NULL
) {
1200 u_terminateChars(dest
, capacity
, 0, status
);
1206 /* We need to transform the string to the destination buffer. */
1207 if (capacity
< length16
) {
1208 /* No chance for the string to fit. Pure preflighting. */
1209 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1211 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1213 * We know the string will fit into dest because each UChar turns
1214 * into at most three UTF-8 bytes. Fill the latter part of dest
1215 * so that callers do not expect to use dest as a string pointer,
1216 * hopefully leading to more robust code for when resource bundles
1217 * may store UTF-8 natively.
1218 * (In which case dest would not be used at all.)
1220 * We do not do this if forceCopy=TRUE because then the caller
1221 * expects the string to start exactly at dest.
1223 * The test above for <= 0x2aaaaaaa prevents overflows.
1224 * The +1 is for the NUL terminator.
1226 int32_t maxLength
= 3 * length16
+ 1;
1227 if (capacity
> maxLength
) {
1228 dest
+= capacity
- maxLength
;
1229 capacity
= maxLength
;
1232 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1236 U_CAPI
const char * U_EXPORT2
1237 ures_getUTF8String(const UResourceBundle
*resB
,
1238 char *dest
, int32_t *pLength
,
1240 UErrorCode
*status
) {
1242 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1243 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1246 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1247 UErrorCode
* status
) {
1249 if (status
==NULL
|| U_FAILURE(*status
)) {
1253 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1256 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1258 *status
= U_RESOURCE_TYPE_MISMATCH
;
1263 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1264 UErrorCode
* status
) {
1266 if (status
==NULL
|| U_FAILURE(*status
)) {
1270 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1273 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1275 *status
= U_RESOURCE_TYPE_MISMATCH
;
1280 /* this function returns a signed integer */
1281 /* it performs sign extension */
1282 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1283 if (status
==NULL
|| U_FAILURE(*status
)) {
1287 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1290 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1291 *status
= U_RESOURCE_TYPE_MISMATCH
;
1294 return RES_GET_INT(resB
->fRes
);
1297 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1298 if (status
==NULL
|| U_FAILURE(*status
)) {
1302 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1305 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1306 *status
= U_RESOURCE_TYPE_MISMATCH
;
1309 return RES_GET_UINT(resB
->fRes
);
1312 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1316 return res_getPublicType(resB
->fRes
);
1319 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1327 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1335 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1336 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1337 const UChar
* result
= 0;
1338 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1339 result
= ures_getString(tempRes
, len
, status
);
1340 ures_close(tempRes
);
1343 return res_getString(&(resB
->fResData
), r
, len
);
1347 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1354 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1358 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1361 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1362 Resource r
= RES_BOGUS
;
1364 if (status
==NULL
|| U_FAILURE(*status
)) {
1368 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1372 if(resB
->fIndex
== resB
->fSize
-1) {
1373 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1376 switch(RES_GET_TYPE(resB
->fRes
)) {
1378 case URES_STRING_V2
:
1379 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1383 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1384 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1385 /* TODO: do the fallback */
1387 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1390 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1391 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1392 /* TODO: do the fallback */
1394 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1396 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1399 case URES_INT_VECTOR
:
1400 *status
= U_RESOURCE_TYPE_MISMATCH
;
1401 default: /*fall through*/
1409 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1410 const char *key
= NULL
;
1411 Resource r
= RES_BOGUS
;
1413 if (status
==NULL
|| U_FAILURE(*status
)) {
1418 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1423 if(resB
->fIndex
== resB
->fSize
-1) {
1424 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1428 switch(RES_GET_TYPE(resB
->fRes
)) {
1432 case URES_STRING_V2
:
1433 case URES_INT_VECTOR
:
1434 return ures_copyResb(fillIn
, resB
, status
);
1438 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1439 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1440 /* TODO: do the fallback */
1442 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1445 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1446 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1447 /* TODO: do the fallback */
1449 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1459 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1460 const char* key
= NULL
;
1461 Resource r
= RES_BOGUS
;
1463 if (status
==NULL
|| U_FAILURE(*status
)) {
1468 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1473 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1474 switch(RES_GET_TYPE(resB
->fRes
)) {
1478 case URES_STRING_V2
:
1479 case URES_INT_VECTOR
:
1480 return ures_copyResb(fillIn
, resB
, status
);
1484 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1485 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1486 /* TODO: do the fallback */
1488 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1491 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1492 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1493 /* TODO: do the fallback */
1495 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1501 *status
= U_MISSING_RESOURCE_ERROR
;
1507 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1508 const char* key
= NULL
;
1509 Resource r
= RES_BOGUS
;
1511 if (status
==NULL
|| U_FAILURE(*status
)) {
1515 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1519 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1520 switch(RES_GET_TYPE(resB
->fRes
)) {
1522 case URES_STRING_V2
:
1523 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1527 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1528 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1529 /* TODO: do the fallback */
1531 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1534 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1535 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1536 /* TODO: do the fallback */
1538 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1540 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1543 case URES_INT_VECTOR
:
1544 *status
= U_RESOURCE_TYPE_MISMATCH
;
1547 /* must not occur */
1548 *status
= U_INTERNAL_PROGRAM_ERROR
;
1552 *status
= U_MISSING_RESOURCE_ERROR
;
1557 U_CAPI
const char * U_EXPORT2
1558 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1560 char *dest
, int32_t *pLength
,
1562 UErrorCode
*status
) {
1564 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1565 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1568 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1569 return resB->fResPath;
1572 U_CAPI UResourceBundle
* U_EXPORT2
1573 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1575 UResourceBundle
*first
= NULL
;
1576 UResourceBundle
*result
= fillIn
;
1577 char *packageName
= NULL
;
1578 char *pathToResource
= NULL
, *save
= NULL
;
1579 char *locale
= NULL
, *localeEnd
= NULL
;
1582 if(status
== NULL
|| U_FAILURE(*status
)) {
1586 length
= (int32_t)(uprv_strlen(path
)+1);
1587 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1589 if(pathToResource
== NULL
) {
1590 *status
= U_MEMORY_ALLOCATION_ERROR
;
1593 uprv_memcpy(pathToResource
, path
, length
);
1595 locale
= pathToResource
;
1596 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1598 packageName
= pathToResource
;
1599 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1600 if(pathToResource
== NULL
) {
1601 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1603 *pathToResource
= 0;
1604 locale
= pathToResource
+1;
1608 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1609 if(localeEnd
!= NULL
) {
1613 first
= ures_open(packageName
, locale
, status
);
1615 if(U_SUCCESS(*status
)) {
1617 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1619 result
= ures_copyResb(fillIn
, first
, status
);
1627 U_CAPI UResourceBundle
* U_EXPORT2
1628 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1630 Resource res
= RES_BOGUS
;
1631 UResourceBundle
*result
= fillIn
;
1634 if(status
== NULL
|| U_FAILURE(*status
)) {
1638 /* here we do looping and circular alias checking */
1639 /* this loop is here because aliasing is resolved on this level, not on res level */
1640 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1642 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1643 if(res
!= RES_BOGUS
) {
1644 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1647 *status
= U_MISSING_RESOURCE_ERROR
;
1650 } while(*path
); /* there is more stuff in the path */
1654 U_INTERNAL
const UChar
* U_EXPORT2
1655 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1658 UErrorCode
*status
) {
1660 UResourceBundle stack
;
1661 const UChar
* retVal
= NULL
;
1662 ures_initStackObject(&stack
);
1663 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1665 retVal
= ures_getString(&stack
, &length
, status
);
1667 if (U_FAILURE(*status
)) {
1670 if (length
== 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1673 *status
= U_MISSING_RESOURCE_ERROR
;
1682 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1684 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1685 Resource resource
= table
; /* The current resource */
1686 icu::CharString path
;
1687 UErrorCode errorCode
= U_ZERO_ERROR
;
1688 path
.append(key
, errorCode
);
1689 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1690 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1691 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1692 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1693 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1694 if (nextPathPart
!= NULL
) {
1695 *nextPathPart
= 0; /* Terminating null for this part of path. */
1698 nextPathPart
= uprv_strchr(pathPart
, 0);
1701 const char *pathP
= pathPart
;
1702 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
1703 type
= (UResType
)RES_GET_TYPE(resource
);
1704 pathPart
= nextPathPart
;
1712 U_CAPI UResourceBundle
* U_EXPORT2
1713 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1715 UResourceBundle
*fillIn
,
1716 UErrorCode
*status
) {
1717 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1718 /*UResourceDataEntry *realData = NULL;*/
1719 UResourceBundle
*helper
= NULL
;
1721 if (status
==NULL
|| U_FAILURE(*status
)) {
1725 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1729 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1730 if(URES_IS_TABLE(type
)) {
1731 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
1732 const char* key
= inKey
;
1733 if(res
== RES_BOGUS
) {
1734 UResourceDataEntry
*dataEntry
= resB
->fData
;
1736 char* myPath
= path
;
1737 const char* resPath
= resB
->fResPath
;
1738 int32_t len
= resB
->fResPathLen
;
1739 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1740 dataEntry
= dataEntry
->fParent
;
1741 rootRes
= dataEntry
->fData
.rootRes
;
1743 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1745 uprv_memcpy(path
, resPath
, len
);
1747 uprv_strcpy(path
+len
, inKey
);
1751 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1752 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1753 /* We hit an alias, but we didn't finish following the path. */
1754 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1755 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1757 dataEntry
= helper
->fData
;
1758 rootRes
= helper
->fRes
;
1759 resPath
= helper
->fResPath
;
1760 len
= helper
->fResPathLen
;
1766 } while(*myPath
); /* Continue until the whole path is consumed */
1769 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1770 if(res
!= RES_BOGUS
) {
1771 /* check if resB->fResPath gives the right name here */
1772 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1773 *status
= U_USING_DEFAULT_WARNING
;
1775 *status
= U_USING_FALLBACK_WARNING
;
1778 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1780 *status
= U_MISSING_RESOURCE_ERROR
;
1783 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1787 *status
= U_RESOURCE_TYPE_MISMATCH
;
1794 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1795 Resource res
= RES_BOGUS
;
1796 UResourceDataEntry
*realData
= NULL
;
1797 const char *key
= inKey
;
1799 if (status
==NULL
|| U_FAILURE(*status
)) {
1803 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1807 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1808 if(URES_IS_TABLE(type
)) {
1810 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1811 if(res
== RES_BOGUS
) {
1813 if(resB
->fHasFallback
== TRUE
) {
1814 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1815 if(U_SUCCESS(*status
)) {
1816 /* check if resB->fResPath gives the right name here */
1817 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1819 *status
= U_MISSING_RESOURCE_ERROR
;
1822 *status
= U_MISSING_RESOURCE_ERROR
;
1825 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1829 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1831 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1832 /* here should go a first attempt to locate the key using index table */
1833 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1834 if(U_SUCCESS(*status
)) {
1835 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1837 *status
= U_MISSING_RESOURCE_ERROR
;
1842 *status
= U_RESOURCE_TYPE_MISMATCH
;
1847 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1848 Resource res
= RES_BOGUS
;
1849 UResourceDataEntry
*realData
= NULL
;
1850 const char* key
= inKey
;
1852 if (status
==NULL
|| U_FAILURE(*status
)) {
1856 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1860 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1861 if(URES_IS_TABLE(type
)) {
1864 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1866 if(res
== RES_BOGUS
) {
1868 if(resB
->fHasFallback
== TRUE
) {
1869 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1870 if(U_SUCCESS(*status
)) {
1871 switch (RES_GET_TYPE(res
)) {
1873 case URES_STRING_V2
:
1874 return res_getString(rd
, res
, len
);
1877 const UChar
* result
= 0;
1878 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1879 result
= ures_getString(tempRes
, len
, status
);
1880 ures_close(tempRes
);
1884 *status
= U_RESOURCE_TYPE_MISMATCH
;
1887 *status
= U_MISSING_RESOURCE_ERROR
;
1890 *status
= U_MISSING_RESOURCE_ERROR
;
1893 switch (RES_GET_TYPE(res
)) {
1895 case URES_STRING_V2
:
1896 return res_getString(&(resB
->fResData
), res
, len
);
1899 const UChar
* result
= 0;
1900 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1901 result
= ures_getString(tempRes
, len
, status
);
1902 ures_close(tempRes
);
1906 *status
= U_RESOURCE_TYPE_MISMATCH
;
1911 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1913 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1914 /* here should go a first attempt to locate the key using index table */
1915 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1916 if(U_SUCCESS(*status
)) {
1917 return res_getString(rd
, res
, len
);
1919 *status
= U_MISSING_RESOURCE_ERROR
;
1924 *status
= U_RESOURCE_TYPE_MISMATCH
;
1929 U_CAPI
const char * U_EXPORT2
1930 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
1932 char *dest
, int32_t *pLength
,
1934 UErrorCode
*status
) {
1936 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
1937 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1940 /* TODO: clean from here down */
1943 * INTERNAL: Get the name of the first real locale (not placeholder)
1944 * that has resource bundle data.
1946 U_INTERNAL
const char* U_EXPORT2
1947 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1949 if (status
==NULL
|| U_FAILURE(*status
)) {
1952 if (!resourceBundle
) {
1953 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1956 return resourceBundle
->fData
->fName
;
1960 U_CAPI
const char* U_EXPORT2
1961 ures_getLocale(const UResourceBundle
* resourceBundle
,
1964 return ures_getLocaleInternal(resourceBundle
, status
);
1968 U_CAPI
const char* U_EXPORT2
1969 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1970 ULocDataLocaleType type
,
1971 UErrorCode
* status
) {
1972 if (status
==NULL
|| U_FAILURE(*status
)) {
1975 if (!resourceBundle
) {
1976 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1980 case ULOC_ACTUAL_LOCALE
:
1981 return resourceBundle
->fData
->fName
;
1982 case ULOC_VALID_LOCALE
:
1983 return resourceBundle
->fTopLevelData
->fName
;
1984 case ULOC_REQUESTED_LOCALE
:
1987 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1993 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
1998 return resB
->fData
->fName
;
2002 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2007 return resB
->fData
->fPath
;
2011 /* OLD API implementation */
2014 * API: This function is used to open a resource bundle
2015 * proper fallback chaining is executed while initialization.
2016 * The result is stored in cache for later fallback search.
2018 U_CAPI
void U_EXPORT2
2019 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2020 const char* localeID
, UErrorCode
* status
) {
2022 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2024 UResourceDataEntry
*firstData
;
2025 UBool isStackObject
= ures_isStackObject(r
);
2026 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2028 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2029 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2030 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2034 ures_closeBundle(r
, FALSE
);
2035 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2036 ures_setIsStackObject(r
, isStackObject
);
2037 r
->fHasFallback
= TRUE
;
2038 r
->fIsTopLevel
= TRUE
;
2040 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2041 if(U_FAILURE(*status
)) {
2044 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
2045 firstData
= r
->fData
;
2046 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
2047 firstData
= firstData
->fParent
;
2049 uprv_memcpy(&r
->fResData
, &firstData
->fData
, sizeof(ResourceData
));
2050 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2051 r
->fRes
= r
->fResData
.rootRes
;
2052 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2053 r
->fTopLevelData
= r
->fData
;
2057 U_CAPI UResourceBundle
* U_EXPORT2
2058 ures_open(const char* path
,
2059 const char* localeID
,
2062 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2063 UResourceDataEntry
*hasData
= NULL
;
2066 if(status
== NULL
|| U_FAILURE(*status
)) {
2070 /* first "canonicalize" the locale ID */
2071 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2072 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2073 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2077 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2079 *status
= U_MEMORY_ALLOCATION_ERROR
;
2083 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2084 r
->fHasFallback
= TRUE
;
2085 r
->fIsTopLevel
= TRUE
;
2086 ures_setIsStackObject(r
, FALSE
);
2088 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2089 if(U_FAILURE(*status
)) {
2093 r
->fTopLevelData
= r
->fData
;
2096 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
2097 hasData
= hasData
->fParent
;
2098 if(hasData
== NULL
) {
2099 /* This can happen only if fallback chain gets broken by an act of God */
2100 /* TODO: this unlikely to happen, consider removing it */
2101 entryClose(r
->fData
);
2103 *status
= U_MISSING_RESOURCE_ERROR
;
2108 uprv_memcpy(&r
->fResData
, &hasData
->fData
, sizeof(ResourceData
));
2109 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2110 r
->fRes
= r
->fResData
.rootRes
;
2111 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2113 if(r->fData->fPath != NULL) {
2114 ures_setResPath(r, r->fData->fPath);
2115 ures_appendResPath(r, RES_PATH_PACKAGE_S);
2116 ures_appendResPath(r, r->fData->fName);
2118 ures_setResPath(r, r->fData->fName);
2127 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2128 * or sought. However, alias substitution will happen!
2130 U_CAPI UResourceBundle
* U_EXPORT2
2131 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2133 UErrorCode subStatus
= U_ZERO_ERROR
;
2135 if(status
== NULL
|| U_FAILURE(*status
)) {
2139 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2141 *status
= U_MEMORY_ALLOCATION_ERROR
;
2145 r
->fHasFallback
= FALSE
;
2146 r
->fIsTopLevel
= TRUE
;
2147 ures_setIsStackObject(r
, FALSE
);
2149 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
2150 if(U_FAILURE(subStatus
)) {
2151 *status
= subStatus
;
2155 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
2156 /* we didn't find one we were looking for - so openDirect */
2158 entryClose(r
->fData
);
2160 *status
= U_MISSING_RESOURCE_ERROR
;
2166 uprv_memcpy(&r
->fResData
, &r
->fData
->fData
, sizeof(ResourceData
));
2167 /* r->fHasFallback remains FALSE here in ures_openDirect() */
2168 r
->fRes
= r
->fResData
.rootRes
;
2169 /*r->fParent = RES_BOGUS;*/
2170 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2173 /*r->fParentRes = NULL;*/
2174 r
->fTopLevelData
= r
->fData
;
2180 * API: Counts members. For arrays and tables, returns number of resources.
2181 * For strings, returns 1.
2183 U_CAPI
int32_t U_EXPORT2
2184 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2185 const char* resourceKey
,
2188 UResourceBundle resData
;
2189 ures_initStackObject(&resData
);
2190 if (status
==NULL
|| U_FAILURE(*status
)) {
2193 if(resourceBundle
== NULL
) {
2194 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2197 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2199 if(resData
.fResData
.data
!= NULL
) {
2200 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2201 ures_close(&resData
);
2204 *status
= U_MISSING_RESOURCE_ERROR
;
2205 ures_close(&resData
);
2211 * Internal function.
2212 * Return the version number associated with this ResourceBundle as a string.
2214 * @param resourceBundle The resource bundle for which the version is checked.
2215 * @return A version number string as specified in the resource bundle or its parent.
2216 * The caller does not own this string.
2217 * @see ures_getVersion
2220 U_INTERNAL
const char* U_EXPORT2
2221 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2223 if (!resourceBundle
) return NULL
;
2225 if(resourceBundle
->fVersion
== NULL
) {
2227 /* If the version ID has not been built yet, then do so. Retrieve */
2228 /* the minor version from the file. */
2229 UErrorCode status
= U_ZERO_ERROR
;
2230 int32_t minor_len
= 0;
2233 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2235 /* Determine the length of of the final version string. This is */
2236 /* the length of the major part + the length of the separator */
2237 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2240 len
= (minor_len
> 0) ? minor_len
: 1;
2242 /* Allocate the string, and build it up. */
2243 /* + 1 for zero byte */
2246 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2247 /* Check for null pointer. */
2248 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2253 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2254 resourceBundle
->fVersion
[len
] = '\0';
2257 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2261 return resourceBundle
->fVersion
;
2264 U_CAPI
const char* U_EXPORT2
2265 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2267 return ures_getVersionNumberInternal(resourceBundle
);
2270 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2273 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2276 /** Tree support functions *******************************/
2277 #define INDEX_LOCALE_NAME "res_index"
2278 #define INDEX_TAG "InstalledLocales"
2279 #define DEFAULT_TAG "default"
2281 #if defined(URES_TREE_DEBUG)
2285 typedef struct ULocalesContext
{
2286 UResourceBundle installed
;
2287 UResourceBundle curr
;
2290 static void U_CALLCONV
2291 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2292 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2293 ures_close(&ctx
->curr
);
2294 ures_close(&ctx
->installed
);
2296 uprv_free(enumerator
);
2299 static int32_t U_CALLCONV
2300 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2301 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2302 return ures_getSize(&ctx
->installed
);
2305 static const char* U_CALLCONV
2306 ures_loc_nextLocale(UEnumeration
* en
,
2307 int32_t* resultLength
,
2308 UErrorCode
* status
) {
2309 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2310 UResourceBundle
*res
= &(ctx
->installed
);
2311 UResourceBundle
*k
= NULL
;
2312 const char *result
= NULL
;
2314 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2315 result
= ures_getKey(k
);
2316 len
= (int32_t)uprv_strlen(result
);
2319 *resultLength
= len
;
2324 static void U_CALLCONV
2325 ures_loc_resetLocales(UEnumeration
* en
,
2326 UErrorCode
* /*status*/) {
2327 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2328 ures_resetIterator(res
);
2332 static const UEnumeration gLocalesEnum
= {
2335 ures_loc_closeLocales
,
2336 ures_loc_countLocales
,
2338 ures_loc_nextLocale
,
2339 ures_loc_resetLocales
2343 U_CAPI UEnumeration
* U_EXPORT2
2344 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2346 UResourceBundle
*idx
= NULL
;
2347 UEnumeration
*en
= NULL
;
2348 ULocalesContext
*myContext
= NULL
;
2350 if(U_FAILURE(*status
)) {
2353 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2354 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2355 if(!en
|| !myContext
) {
2356 *status
= U_MEMORY_ALLOCATION_ERROR
;
2358 uprv_free(myContext
);
2361 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2363 ures_initStackObject(&myContext
->installed
);
2364 ures_initStackObject(&myContext
->curr
);
2365 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2366 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2367 if(U_SUCCESS(*status
)) {
2368 #if defined(URES_TREE_DEBUG)
2369 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2370 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2372 en
->context
= myContext
;
2374 #if defined(URES_TREE_DEBUG)
2375 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2377 ures_close(&myContext
->installed
);
2378 uprv_free(myContext
);
2388 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2390 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2391 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2398 U_CAPI
int32_t U_EXPORT2
2399 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2400 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2401 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2403 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2404 char defVal
[1024] = ""; /* default value for given locale */
2405 char defLoc
[1024] = ""; /* default value for given locale */
2406 char base
[1024] = ""; /* base locale */
2409 char full
[1024] = "";
2410 UResourceBundle bund1
, bund2
;
2411 UResourceBundle
*res
= NULL
;
2412 UErrorCode subStatus
= U_ZERO_ERROR
;
2414 if(U_FAILURE(*status
)) return 0;
2415 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2416 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2419 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2420 #if defined(URES_TREE_DEBUG)
2421 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2422 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2424 ures_initStackObject(&bund1
);
2425 ures_initStackObject(&bund2
);
2428 uprv_strcpy(parent
, base
);
2429 uprv_strcpy(found
, base
);
2432 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2433 *isAvailable
= TRUE
;
2434 if (U_SUCCESS(subStatus
)) {
2435 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2437 uenum_close(locEnum
);
2440 if(U_FAILURE(subStatus
)) {
2441 *status
= subStatus
;
2446 subStatus
= U_ZERO_ERROR
;
2447 res
= ures_open(path
, parent
, &subStatus
);
2448 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2449 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2451 *isAvailable
= FALSE
;
2453 isAvailable
= NULL
; /* only want to set this the first time around */
2455 #if defined(URES_TREE_DEBUG)
2456 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2458 if(U_FAILURE(subStatus
)) {
2459 *status
= subStatus
;
2460 } else if(subStatus
== U_ZERO_ERROR
) {
2461 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2462 if(subStatus
== U_ZERO_ERROR
) {
2463 const UChar
*defUstr
;
2465 /* look for default item */
2466 #if defined(URES_TREE_DEBUG)
2467 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2468 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2470 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2471 if(U_SUCCESS(subStatus
) && defLen
) {
2472 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2473 #if defined(URES_TREE_DEBUG)
2474 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2475 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2477 uprv_strcpy(defLoc
, parent
);
2479 uprv_strcpy(kwVal
, defVal
);
2480 #if defined(URES_TREE_DEBUG)
2481 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2482 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2489 subStatus
= U_ZERO_ERROR
;
2492 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2495 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2497 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2499 /* Now, see if we can find the kwVal collator.. start the search over.. */
2500 uprv_strcpy(parent
, base
);
2501 uprv_strcpy(found
, base
);
2504 subStatus
= U_ZERO_ERROR
;
2505 res
= ures_open(path
, parent
, &subStatus
);
2506 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2507 *isAvailable
= FALSE
;
2509 isAvailable
= NULL
; /* only want to set this the first time around */
2511 #if defined(URES_TREE_DEBUG)
2512 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2513 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2515 if(U_FAILURE(subStatus
)) {
2516 *status
= subStatus
;
2517 } else if(subStatus
== U_ZERO_ERROR
) {
2518 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2519 #if defined(URES_TREE_DEBUG)
2520 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2522 if(subStatus
== U_ZERO_ERROR
) {
2523 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2524 #if defined(URES_TREE_DEBUG)
2525 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2527 if(subStatus
== U_ZERO_ERROR
) {
2528 #if defined(URES_TREE_DEBUG)
2529 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2530 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2532 uprv_strcpy(full
, parent
);
2534 uprv_strcpy(full
, "root");
2536 /* now, recalculate default kw if need be */
2537 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2538 const UChar
*defUstr
;
2540 /* look for default item */
2541 #if defined(URES_TREE_DEBUG)
2542 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2543 path
?path
:"ICUDATA", full
);
2545 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2546 if(U_SUCCESS(subStatus
) && defLen
) {
2547 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2548 #if defined(URES_TREE_DEBUG)
2549 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2550 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2552 uprv_strcpy(defLoc
, full
);
2554 } /* end of recalculate default KW */
2555 #if defined(URES_TREE_DEBUG)
2557 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2561 #if defined(URES_TREE_DEBUG)
2562 fprintf(stderr
, "err=%s in %s looking for %s\n",
2563 u_errorName(subStatus
), parent
, kwVal
);
2569 subStatus
= U_ZERO_ERROR
;
2571 uprv_strcpy(found
, parent
);
2572 uloc_getParent(found
,parent
,1023,&subStatus
);
2574 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2576 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2577 #if defined(URES_TREE_DEBUG)
2578 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2580 uprv_strcpy(kwVal
, defVal
);
2581 uprv_strcpy(parent
, base
);
2582 uprv_strcpy(found
, base
);
2584 do { /* search for 'default' named item */
2585 subStatus
= U_ZERO_ERROR
;
2586 res
= ures_open(path
, parent
, &subStatus
);
2587 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2588 *isAvailable
= FALSE
;
2590 isAvailable
= NULL
; /* only want to set this the first time around */
2592 #if defined(URES_TREE_DEBUG)
2593 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2594 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2596 if(U_FAILURE(subStatus
)) {
2597 *status
= subStatus
;
2598 } else if(subStatus
== U_ZERO_ERROR
) {
2599 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2600 if(subStatus
== U_ZERO_ERROR
) {
2601 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2602 if(subStatus
== U_ZERO_ERROR
) {
2603 #if defined(URES_TREE_DEBUG)
2604 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2605 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2607 uprv_strcpy(full
, parent
);
2609 uprv_strcpy(full
, "root");
2612 /* now, recalculate default kw if need be */
2613 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2614 const UChar
*defUstr
;
2616 /* look for default item */
2617 #if defined(URES_TREE_DEBUG)
2618 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2619 path
?path
:"ICUDATA", full
);
2621 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2622 if(U_SUCCESS(subStatus
) && defLen
) {
2623 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2624 #if defined(URES_TREE_DEBUG)
2625 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2626 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2628 uprv_strcpy(defLoc
, full
);
2630 } /* end of recalculate default KW */
2631 #if defined(URES_TREE_DEBUG)
2633 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2639 subStatus
= U_ZERO_ERROR
;
2641 uprv_strcpy(found
, parent
);
2642 uloc_getParent(found
,parent
,1023,&subStatus
);
2644 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2647 if(U_SUCCESS(*status
)) {
2649 #if defined(URES_TREE_DEBUG)
2650 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2652 *status
= U_MISSING_RESOURCE_ERROR
;
2653 } else if(omitDefault
) {
2654 #if defined(URES_TREE_DEBUG)
2655 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2657 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2658 /* found the keyword in a *child* of where the default tag was present. */
2659 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2660 /* and the default is in or in an ancestor of the current locale */
2661 #if defined(URES_TREE_DEBUG)
2662 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2668 uprv_strcpy(found
, full
);
2670 uprv_strcat(found
, "@");
2671 uprv_strcat(found
, keyword
);
2672 uprv_strcat(found
, "=");
2673 uprv_strcat(found
, kwVal
);
2674 } else if(!omitDefault
) {
2675 uprv_strcat(found
, "@");
2676 uprv_strcat(found
, keyword
);
2677 uprv_strcat(found
, "=");
2678 uprv_strcat(found
, defVal
);
2681 /* we found the default locale - no need to repeat it.*/
2686 length
= (int32_t)uprv_strlen(found
);
2688 if(U_SUCCESS(*status
)) {
2689 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2691 uprv_strncpy(result
, found
, copyLength
);
2694 *status
= U_MISSING_RESOURCE_ERROR
;
2700 return u_terminateChars(result
, resultCapacity
, length
, status
);
2703 U_CAPI UEnumeration
* U_EXPORT2
2704 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2706 #define VALUES_BUF_SIZE 2048
2707 #define VALUES_LIST_SIZE 512
2709 char valuesBuf
[VALUES_BUF_SIZE
];
2710 int32_t valuesIndex
= 0;
2711 const char *valuesList
[VALUES_LIST_SIZE
];
2712 int32_t valuesCount
= 0;
2717 UEnumeration
*locs
= NULL
;
2719 UResourceBundle item
;
2720 UResourceBundle subItem
;
2722 ures_initStackObject(&item
);
2723 ures_initStackObject(&subItem
);
2724 locs
= ures_openAvailableLocales(path
, status
);
2726 if(U_FAILURE(*status
)) {
2728 ures_close(&subItem
);
2735 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2736 UResourceBundle
*bund
= NULL
;
2737 UResourceBundle
*subPtr
= NULL
;
2738 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2739 bund
= ures_openDirect(path
, locale
, &subStatus
);
2741 #if defined(URES_TREE_DEBUG)
2742 if(!bund
|| U_FAILURE(subStatus
)) {
2743 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2744 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2748 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2750 if(!bund
|| U_FAILURE(subStatus
)) {
2751 #if defined(URES_TREE_DEBUG)
2752 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2753 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2760 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2761 && U_SUCCESS(subStatus
)) {
2764 k
= ures_getKey(subPtr
);
2766 #if defined(URES_TREE_DEBUG)
2767 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2769 for(i
=0;k
&&i
<valuesCount
;i
++) {
2770 if(!uprv_strcmp(valuesList
[i
],k
)) {
2771 k
= NULL
; /* found duplicate */
2775 int32_t kLen
= (int32_t)uprv_strlen(k
);
2776 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2777 continue; /* don't need 'default'. */
2779 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2780 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2781 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2783 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2784 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2785 valuesIndex
+= kLen
;
2786 #if defined(URES_TREE_DEBUG)
2787 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2788 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2790 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2796 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2799 ures_close(&subItem
);
2801 #if defined(URES_TREE_DEBUG)
2802 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2803 valuesIndex
, valuesCount
);
2805 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2808 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2809 U_INTERNAL UBool U_EXPORT2
2810 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2811 if(res1
==NULL
|| res2
==NULL
){
2812 return res1
==res2
; /* pointer comparision */
2814 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2815 return (res1
->fKey
==res2
->fKey
);
2817 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2821 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2824 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2825 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2827 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2831 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2834 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2837 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2840 if(res1
->fRes
!= res2
->fRes
){
2845 U_INTERNAL UResourceBundle
* U_EXPORT2
2846 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2847 UResourceBundle
* bundle
= NULL
;
2848 UResourceBundle
* ret
= NULL
;
2849 if(U_FAILURE(*status
) || res
== NULL
){
2852 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2853 if(res
->fResPath
!=NULL
){
2854 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2861 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2862 ures_getParentBundle(const UResourceBundle
* res
){
2866 return res
->fParentRes
;
2870 U_INTERNAL
void U_EXPORT2
2871 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
2874 str
= ures_getStringByKey(res
, key
, &len
, status
);
2875 if(U_SUCCESS(*status
)) {
2876 u_versionFromUString(ver
, str
);