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"
40 Static cache for already opened resource bundles - mostly for keeping fallback info
41 TODO: This cache should probably be removed when the deprecated code is
44 static UHashtable
*cache
= NULL
;
46 static UMutex resbMutex
= U_MUTEX_INITIALIZER
;
48 /* INTERNAL: hashes an entry */
49 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
50 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
51 UHashTok namekey
, pathkey
;
52 namekey
.pointer
= b
->fName
;
53 pathkey
.pointer
= b
->fPath
;
54 return uhash_hashChars(namekey
)+37*uhash_hashChars(pathkey
);
57 /* INTERNAL: compares two entries */
58 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
59 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
60 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
61 UHashTok name1
, name2
, path1
, path2
;
62 name1
.pointer
= b1
->fName
;
63 name2
.pointer
= b2
->fName
;
64 path1
.pointer
= b1
->fPath
;
65 path2
.pointer
= b2
->fPath
;
66 return (UBool
)(uhash_compareChars(name1
, name2
) &&
67 uhash_compareChars(path1
, path2
));
72 * Internal function, gets parts of locale name according
73 * to the position of '_' character
75 static UBool
chopLocale(char *name
) {
76 char *i
= uprv_strrchr(name
, '_');
89 static void entryIncrease(UResourceDataEntry
*entry
) {
90 umtx_lock(&resbMutex
);
91 entry
->fCountExisting
++;
92 while(entry
->fParent
!= NULL
) {
93 entry
= entry
->fParent
;
94 entry
->fCountExisting
++;
96 umtx_unlock(&resbMutex
);
100 * Internal function. Tries to find a resource in given Resource
101 * Bundle, as well as in its parents
103 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
104 UResourceDataEntry
*resB
= resBundle
->fData
;
109 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
110 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
113 if(resBundle
->fHasFallback
== TRUE
) {
114 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
115 resB
= resB
->fParent
;
116 if(resB
->fBogus
== U_ZERO_ERROR
) {
118 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
123 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
125 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
126 *status
= U_USING_DEFAULT_WARNING
;
128 *status
= U_USING_FALLBACK_WARNING
;
132 return (&(resB
->fData
));
133 } else { /* If resource is not found, we need to give an error */
134 *status
= U_MISSING_RESOURCE_ERROR
;
138 *status
= U_MISSING_RESOURCE_ERROR
;
144 free_entry(UResourceDataEntry
*entry
) {
145 UResourceDataEntry
*alias
;
146 res_unload(&(entry
->fData
));
147 if(entry
->fName
!= NULL
&& entry
->fName
!= entry
->fNameBuffer
) {
148 uprv_free(entry
->fName
);
150 if(entry
->fPath
!= NULL
) {
151 uprv_free(entry
->fPath
);
153 if(entry
->fPool
!= NULL
) {
154 --entry
->fPool
->fCountExisting
;
156 alias
= entry
->fAlias
;
158 while(alias
->fAlias
!= NULL
) {
159 alias
= alias
->fAlias
;
161 --alias
->fCountExisting
;
166 /* Works just like ucnv_flushCache() */
167 static int32_t ures_flushCache()
169 UResourceDataEntry
*resB
;
171 int32_t rbDeletedNum
= 0;
172 const UHashElement
*e
;
175 /*if shared data hasn't even been lazy evaluated yet
178 umtx_lock(&resbMutex
);
180 umtx_unlock(&resbMutex
);
186 /*creates an enumeration to iterate through every element in the table */
188 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
190 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
191 /* Deletes only if reference counter == 0
192 * Don't worry about the children of this node.
193 * Those will eventually get deleted too, if not already.
194 * Don't worry about the parents of this node.
195 * Those will eventually get deleted too, if not already.
197 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
198 /* some resource bundles are still open somewhere. */
200 if (resB
->fCountExisting
== 0) {
203 uhash_removeElement(cache
, e
);
208 * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
209 * got decremented by free_entry().
211 } while(deletedMore
);
212 umtx_unlock(&resbMutex
);
220 U_CAPI UBool U_EXPORT2
ures_dumpCacheContents(void) {
221 UBool cacheNotEmpty
= FALSE
;
223 const UHashElement
*e
;
224 UResourceDataEntry
*resB
;
226 umtx_lock(&resbMutex
);
228 umtx_unlock(&resbMutex
);
229 fprintf(stderr
,"%s:%d: RB Cache is NULL.\n", __FILE__
, __LINE__
);
233 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
) {
235 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
236 fprintf(stderr
,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s. Pool 0x%p, alias 0x%p, parent 0x%p\n",
238 (void*)resB
, resB
->fCountExisting
,
239 resB
->fName
?resB
->fName
:"NULL",
240 resB
->fPath
?resB
->fPath
:"NULL",
243 (void*)resB
->fParent
);
246 fprintf(stderr
,"%s:%d: RB Cache still contains %d items.\n", __FILE__
, __LINE__
, uhash_count(cache
));
248 umtx_unlock(&resbMutex
);
250 return cacheNotEmpty
;
255 static UBool U_CALLCONV
ures_cleanup(void)
259 if (cache
!= NULL
&& uhash_count(cache
) == 0) {
264 return (cache
== NULL
);
267 /** INTERNAL: Initializes the cache for resources */
268 static void initCache(UErrorCode
*status
) {
269 UBool makeCache
= FALSE
;
270 UMTX_CHECK(&resbMutex
, (cache
== NULL
), makeCache
);
272 UHashtable
*newCache
= uhash_open(hashEntry
, compareEntries
, NULL
, status
);
273 if (U_FAILURE(*status
)) {
276 umtx_lock(&resbMutex
);
280 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
282 umtx_unlock(&resbMutex
);
283 if(newCache
!= NULL
) {
284 uhash_close(newCache
);
289 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
291 static void setEntryName(UResourceDataEntry
*res
, const char *name
, UErrorCode
*status
) {
292 int32_t len
= (int32_t)uprv_strlen(name
);
293 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
294 uprv_free(res
->fName
);
296 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
297 res
->fName
= res
->fNameBuffer
;
300 res
->fName
= (char *)uprv_malloc(len
+1);
302 if(res
->fName
== NULL
) {
303 *status
= U_MEMORY_ALLOCATION_ERROR
;
305 uprv_strcpy(res
->fName
, name
);
309 static UResourceDataEntry
*
310 getPoolEntry(const char *path
, UErrorCode
*status
);
313 * INTERNAL: Inits and opens an entry from a data DLL.
314 * CAUTION: resbMutex must be locked when calling this function.
316 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
317 UResourceDataEntry
*r
= NULL
;
318 UResourceDataEntry find
;
319 /*int32_t hashValue;*/
321 char aliasName
[100] = { 0 };
322 int32_t aliasLen
= 0;
323 /*UBool isAlias = FALSE;*/
324 /*UHashTok hashkey; */
326 if(U_FAILURE(*status
)) {
330 /* here we try to deduce the right locale name */
331 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
332 name
= uloc_getDefault();
333 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
334 name
= kRootLocaleName
;
335 } else { /* otherwise, we'll open what we're given */
339 find
.fName
= (char *)name
;
340 find
.fPath
= (char *)path
;
342 /* calculate the hash value of the entry */
343 /*hashkey.pointer = (void *)&find;*/
344 /*hashValue = hashEntry(hashkey);*/
346 /* check to see if we already have this entry */
347 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
349 /* if the entry is not yet in the hash table, we'll try to construct a new one */
350 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
352 *status
= U_MEMORY_ALLOCATION_ERROR
;
356 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
357 /*r->fHashKey = hashValue;*/
359 setEntryName(r
, name
, status
);
360 if (U_FAILURE(*status
)) {
366 r
->fPath
= (char *)uprv_strdup(path
);
367 if(r
->fPath
== NULL
) {
368 *status
= U_MEMORY_ALLOCATION_ERROR
;
374 /* this is the actual loading */
375 res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
377 if (U_FAILURE(*status
)) {
378 /* we have no such entry in dll, so it will always use fallback */
379 *status
= U_USING_FALLBACK_WARNING
;
380 r
->fBogus
= U_USING_FALLBACK_WARNING
;
381 } else { /* if we have a regular entry */
383 if (r
->fData
.usesPoolBundle
) {
384 r
->fPool
= getPoolEntry(r
->fPath
, status
);
385 if (U_SUCCESS(*status
)) {
386 const int32_t *poolIndexes
= r
->fPool
->fData
.pRoot
+ 1;
387 if(r
->fData
.pRoot
[1 + URES_INDEX_POOL_CHECKSUM
] == poolIndexes
[URES_INDEX_POOL_CHECKSUM
]) {
388 r
->fData
.poolBundleKeys
= (const char *)(poolIndexes
+ (poolIndexes
[URES_INDEX_LENGTH
] & 0xff));
390 r
->fBogus
= *status
= U_INVALID_FORMAT_ERROR
;
396 if (U_SUCCESS(*status
)) {
397 /* handle the alias by trying to get out the %%Alias tag.*/
398 /* We'll try to get alias string from the bundle */
399 aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
400 if (aliasres
!= RES_BOGUS
) {
401 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
402 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
403 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
404 r
->fAlias
= init_entry(aliasName
, path
, status
);
411 UResourceDataEntry
*oldR
= NULL
;
412 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
413 /* just insert it in the cache */
414 UErrorCode cacheStatus
= U_ZERO_ERROR
;
415 uhash_put(cache
, (void *)r
, r
, &cacheStatus
);
416 if (U_FAILURE(cacheStatus
)) {
417 *status
= cacheStatus
;
422 /* somebody have already inserted it while we were working, discard newly opened data */
423 /* Also, we could get here IF we opened an alias */
431 /* return the real bundle */
432 while(r
->fAlias
!= NULL
) {
435 r
->fCountExisting
++; /* we increase its reference count */
436 /* if the resource has a warning */
437 /* we don't want to overwrite a status with no error */
438 if(r
->fBogus
!= U_ZERO_ERROR
&& U_SUCCESS(*status
)) {
439 *status
= r
->fBogus
; /* set the returning status */
445 static UResourceDataEntry
*
446 getPoolEntry(const char *path
, UErrorCode
*status
) {
447 UResourceDataEntry
*poolBundle
= init_entry(kPoolBundleName
, path
, status
);
448 if( U_SUCCESS(*status
) &&
449 (poolBundle
== NULL
|| poolBundle
->fBogus
!= U_ZERO_ERROR
|| !poolBundle
->fData
.isPoolBundle
)
451 *status
= U_INVALID_FORMAT_ERROR
;
457 /* CAUTION: resbMutex must be locked when calling this function! */
458 static UResourceDataEntry
*findFirstExisting(const char* path
, char* name
, UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
459 UResourceDataEntry
*r
= NULL
;
460 UBool hasRealData
= FALSE
;
461 const char *defaultLoc
= uloc_getDefault();
462 *hasChopped
= TRUE
; /* we're starting with a fresh name */
464 while(*hasChopped
&& !hasRealData
) {
465 r
= init_entry(name
, path
, status
);
466 /* Null pointer test */
467 if (U_FAILURE(*status
)) {
470 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
471 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
473 /* this entry is not real. We will discard it. */
474 /* However, the parent line for this entry is */
475 /* not to be used - as there might be parent */
476 /* lines in cache from previous openings that */
477 /* are not updated yet. */
479 /*entryCloseInt(r);*/
481 *status
= U_USING_FALLBACK_WARNING
;
483 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
486 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
488 /*Fallback data stuff*/
489 *hasChopped
= chopLocale(name
);
494 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
499 resB
->fMagic1
= MAGIC1
;
500 resB
->fMagic2
= MAGIC2
;
504 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
505 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
509 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
510 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
511 ures_setIsStackObject(resB
, TRUE
);
514 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
, UErrorCode
* status
) {
515 UErrorCode intStatus
= U_ZERO_ERROR
;
516 UErrorCode parentStatus
= U_ZERO_ERROR
;
517 UErrorCode usrStatus
= U_ZERO_ERROR
;
518 UResourceDataEntry
*r
= NULL
;
519 UResourceDataEntry
*t1
= NULL
;
520 UResourceDataEntry
*t2
= NULL
;
521 UResourceDataEntry
*u1
= NULL
;
522 UResourceDataEntry
*u2
= NULL
;
523 UBool isDefault
= FALSE
;
524 UBool isRoot
= FALSE
;
525 UBool hasRealData
= FALSE
;
526 UBool hasChopped
= TRUE
;
527 UBool usingUSRData
= U_USE_USRDATA
&& ( path
== NULL
|| uprv_strncmp(path
,U_ICUDATA_NAME
,8) == 0);
529 char name
[ULOC_FULLNAME_CAPACITY
];
530 char usrDataPath
[96];
534 if(U_FAILURE(*status
)) {
538 uprv_strncpy(name
, localeID
, sizeof(name
) - 1);
539 name
[sizeof(name
) - 1] = 0;
541 if ( usingUSRData
) {
542 if ( path
== NULL
) {
543 uprv_strcpy(usrDataPath
, U_USRDATA_NAME
);
545 uprv_strncpy(usrDataPath
, path
, sizeof(usrDataPath
) - 1);
546 usrDataPath
[0] = 'u';
547 usrDataPath
[1] = 's';
548 usrDataPath
[2] = 'r';
549 usrDataPath
[sizeof(usrDataPath
) - 1] = 0;
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 if ( res_getResource(&t1
->fData
,"%%Parent") != RES_BOGUS
) { /* An explicit parent was found */
575 int32_t parentLocaleLen
= 0;
576 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), res_getResource(&t1
->fData
,"%%Parent") , &parentLocaleLen
);
577 if(parentLocaleName
!= NULL
&& parentLocaleLen
> 0) {
578 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+1);
579 if ( !uprv_strcmp(name
,"root") ) { /* If parent is root, we just terminate the loop */
585 /* insert regular parents */
586 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
587 if ( usingUSRData
) { /* This code inserts user override data into the inheritance chain */
588 usrStatus
= U_ZERO_ERROR
;
589 u2
= init_entry(name
, usrDataPath
, &usrStatus
);
591 /* Check for null pointer. */
592 if (t2
== NULL
|| ( usingUSRData
&& u2
== NULL
)) {
593 *status
= U_MEMORY_ALLOCATION_ERROR
;
597 if ( usingUSRData
&& u2
->fBogus
== U_ZERO_ERROR
) {
603 /* the USR override data wasn't found, set it to be deleted */
604 u2
->fCountExisting
= 0;
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 if ( res_getResource(&t1
->fData
,"%%Parent") != RES_BOGUS
) { /* An explicit parent was found */
625 int32_t parentLocaleLen
= 0;
626 const UChar
*parentLocaleName
= res_getString(&(t1
->fData
), res_getResource(&t1
->fData
,"%%Parent") , &parentLocaleLen
);
627 if(parentLocaleName
!= NULL
&& parentLocaleLen
> 0) {
628 u_UCharsToChars(parentLocaleName
, name
, parentLocaleLen
+1);
629 if ( !uprv_strcmp(name
,"root") ) { /* If parent is root, we just terminate the loop */
635 /* insert chopped defaults */
636 t2
= init_entry(name
, t1
->fPath
, &parentStatus
);
637 /* Check for null pointer. */
639 *status
= U_MEMORY_ALLOCATION_ERROR
;
643 if ( res_getResource(&t1
->fData
,"%%ParentIsRoot") == RES_BOGUS
) {
647 hasChopped
= chopLocale(name
);
652 /* we could still have r == NULL at this point - maybe even default locale is not */
655 uprv_strcpy(name
, kRootLocaleName
);
656 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
659 intStatus
= U_USING_DEFAULT_WARNING
;
661 } else { /* we don't even have the root locale */
662 *status
= U_MISSING_RESOURCE_ERROR
;
665 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
666 /* insert root locale */
667 t2
= init_entry(kRootLocaleName
, t1
->fPath
, &parentStatus
);
668 /* Check for null pointer. */
670 *status
= U_MEMORY_ALLOCATION_ERROR
;
674 r
->fBogus
= U_USING_DEFAULT_WARNING
;
676 hasRealData
= (UBool
)((t2
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
681 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
682 t1
->fParent
->fCountExisting
++;
684 hasRealData
= (UBool
)((t1
->fBogus
== U_ZERO_ERROR
) || hasRealData
);
688 umtx_unlock(&resbMutex
);
690 if(U_SUCCESS(*status
)) {
691 if(U_SUCCESS(parentStatus
)) {
692 if(intStatus
!= U_ZERO_ERROR
) {
697 *status
= parentStatus
;
707 * Functions to create and destroy resource bundles.
708 * CAUTION: resbMutex must be locked when calling this function.
711 static void entryCloseInt(UResourceDataEntry
*resB
) {
712 UResourceDataEntry
*p
= resB
;
714 while(resB
!= NULL
) {
716 resB
->fCountExisting
--;
718 /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
721 if(resB->fCountExisting <= 0) {
722 uhash_remove(cache, resB);
723 if(resB->fBogus == U_ZERO_ERROR) {
724 res_unload(&(resB->fData));
726 if(resB->fName != NULL) {
727 uprv_free(resB->fName);
729 if(resB->fPath != NULL) {
730 uprv_free(resB->fPath);
741 * API: closes a resource bundle and cleans up.
744 static void entryClose(UResourceDataEntry
*resB
) {
745 umtx_lock(&resbMutex
);
747 umtx_unlock(&resbMutex
);
751 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
752 if(resB->fResPath == NULL) {
753 resB->fResPath = resB->fResBuf;
754 *(resB->fResPath) = 0;
756 resB->fResPathLen = uprv_strlen(toAdd);
757 if(RES_BUFSIZE <= resB->fResPathLen+1) {
758 if(resB->fResPath == resB->fResBuf) {
759 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
761 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
764 uprv_strcpy(resB->fResPath, toAdd);
767 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
768 int32_t resPathLenOrig
= resB
->fResPathLen
;
769 if(resB
->fResPath
== NULL
) {
770 resB
->fResPath
= resB
->fResBuf
;
771 *(resB
->fResPath
) = 0;
772 resB
->fResPathLen
= 0;
774 resB
->fResPathLen
+= lenToAdd
;
775 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
776 if(resB
->fResPath
== resB
->fResBuf
) {
777 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
778 /* Check that memory was allocated correctly. */
779 if (resB
->fResPath
== NULL
) {
780 *status
= U_MEMORY_ALLOCATION_ERROR
;
783 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
785 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
786 /* Check that memory was reallocated correctly. */
788 *status
= U_MEMORY_ALLOCATION_ERROR
;
791 resB
->fResPath
= temp
;
794 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
797 static void ures_freeResPath(UResourceBundle
*resB
) {
798 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
799 uprv_free(resB
->fResPath
);
801 resB
->fResPath
= NULL
;
802 resB
->fResPathLen
= 0;
806 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
809 if(resB
->fData
!= NULL
) {
810 entryClose(resB
->fData
);
812 if(resB
->fVersion
!= NULL
) {
813 uprv_free(resB
->fVersion
);
815 ures_freeResPath(resB
);
817 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
822 /* poison the data */
823 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
829 U_CAPI
void U_EXPORT2
830 ures_close(UResourceBundle
* resB
)
832 ures_closeBundle(resB
, TRUE
);
835 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
836 const char *key
, int32_t idx
, UResourceDataEntry
*realData
,
837 const UResourceBundle
*parent
, int32_t noAlias
,
838 UResourceBundle
*resB
, UErrorCode
*status
)
840 if(status
== NULL
|| U_FAILURE(*status
)) {
843 if (parent
== NULL
) {
844 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
847 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
848 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
850 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
852 /* we have an alias, now let's cut it up */
853 char stackAlias
[200];
854 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
858 * Allocate enough space for both the char * version
859 * of the alias and parent->fResPath.
861 * We do this so that res_findResource() can modify the path,
862 * which allows us to remove redundant _res_findResource() variants
864 * res_findResource() now NUL-terminates each segment so that table keys
865 * can always be compared with strcmp() instead of strncmp().
866 * Saves code there and simplifies testing and code coverage.
870 ++len
; /* count the terminating NUL */
871 if(parent
->fResPath
!= NULL
) {
872 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
879 if(capacity
<= (int32_t)sizeof(stackAlias
)) {
880 capacity
= (int32_t)sizeof(stackAlias
);
881 chAlias
= stackAlias
;
883 chAlias
= (char *)uprv_malloc(capacity
);
885 if(chAlias
== NULL
) {
886 *status
= U_MEMORY_ALLOCATION_ERROR
;
890 u_UCharsToChars(alias
, chAlias
, len
);
892 if(*chAlias
== RES_PATH_SEPARATOR
) {
893 /* there is a path included */
894 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
896 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
902 if(uprv_strcmp(path
, "LOCALE") == 0) {
903 /* this is an XPath alias, starting with "/LOCALE/" */
904 /* it contains the path to a resource which should be looked up */
905 /* starting in the requested locale */
907 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
908 path
= realData
->fPath
; /* we will be looking in the same package */
910 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
913 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
920 /* no path, start with a locale */
922 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
927 path
= realData
->fPath
;
932 /* got almost everything, let's try to open */
933 /* first, open the bundle with real data */
934 UResourceBundle
*result
= resB
;
935 const char* temp
= NULL
;
936 UErrorCode intStatus
= U_ZERO_ERROR
;
937 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
938 if(U_SUCCESS(intStatus
)) {
939 if(keyPath
== NULL
) {
940 /* no key path. This means that we are going to
941 * to use the corresponding resource from
944 /* first, we are going to get a corresponding parent
945 * resource to the one we are searching.
947 char *aKey
= parent
->fResPath
;
949 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
951 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
956 /* we need to make keyPath from parent's fResPath and
957 * current key, if there is a key associated
959 len
= (int32_t)(uprv_strlen(key
) + 1);
962 if(chAlias
== stackAlias
) {
963 chAlias
= (char *)uprv_malloc(capacity
);
965 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
967 if(chAlias
== NULL
) {
969 *status
= U_MEMORY_ALLOCATION_ERROR
;
973 uprv_memcpy(chAlias
, key
, len
);
975 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
976 } else if(idx
!= -1) {
977 /* if there is no key, but there is an index, try to get by the index */
978 /* here we have either a table or an array, so get the element */
979 int32_t type
= RES_GET_TYPE(r
);
980 if(URES_IS_TABLE(type
)) {
981 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, idx
, (const char **)&aKey
);
983 r
= res_getArrayItem(&(mainRes
->fResData
), r
, idx
);
987 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
989 *status
= U_MISSING_RESOURCE_ERROR
;
993 /* this one is a bit trickier.
994 * we start finding keys, but after we resolve one alias, the path might continue.
996 * aliastest:alias { "testtypes/anotheralias/Sequence" }
997 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
998 * aliastest resource should finally have the sequence, not collation elements.
1000 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
1001 char stackPath
[URES_MAX_BUFFER_SIZE
];
1002 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
1003 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
1004 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
1005 if(pathBuf
== NULL
) {
1006 *status
= U_MEMORY_ALLOCATION_ERROR
;
1010 uprv_strcpy(pathBuf
, keyPath
);
1012 /* now we have fallback following here */
1014 r
= dataEntry
->fData
.rootRes
;
1015 /* this loop handles 'found' resources over several levels */
1016 while(*myPath
&& U_SUCCESS(*status
)) {
1017 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
1018 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
1019 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
1022 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
1023 dataEntry
= result
->fData
;
1025 } else { /* no resource found, we don't really want to look anymore on this level */
1029 dataEntry
= dataEntry
->fParent
;
1030 uprv_strcpy(pathBuf
, keyPath
);
1032 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
1033 if(r
== RES_BOGUS
) {
1034 *status
= U_MISSING_RESOURCE_ERROR
;
1037 if(pathBuf
!= stackPath
) {
1041 } else { /* we failed to open the resource we're aliasing to */
1042 *status
= intStatus
;
1044 if(chAlias
!= stackAlias
) {
1047 if(mainRes
!= result
) {
1048 ures_close(mainRes
);
1053 /* bad alias, should be an error */
1054 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1058 *status
= U_TOO_MANY_ALIASES_ERROR
;
1063 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1066 *status
= U_MEMORY_ALLOCATION_ERROR
;
1069 ures_setIsStackObject(resB
, FALSE
);
1070 resB
->fResPath
= NULL
;
1071 resB
->fResPathLen
= 0;
1073 if(resB
->fData
!= NULL
) {
1074 entryClose(resB
->fData
);
1076 if(resB
->fVersion
!= NULL
) {
1077 uprv_free(resB
->fVersion
);
1080 weiv: if stack object was passed in, it doesn't really need to be reinited,
1081 since the purpose of initing is to remove stack junk. However, at this point
1082 we would not do anything to an allocated object, so stack object should be
1086 if(ures_isStackObject(resB) != FALSE) {
1087 ures_initStackObject(resB);
1090 if(parent
!= resB
) {
1091 ures_freeResPath(resB
);
1094 resB
->fData
= realData
;
1095 entryIncrease(resB
->fData
);
1096 resB
->fHasFallback
= FALSE
;
1097 resB
->fIsTopLevel
= FALSE
;
1100 /*resB->fParentRes = parent;*/
1101 resB
->fTopLevelData
= parent
->fTopLevelData
;
1102 if(parent
->fResPath
&& parent
!= resB
) {
1103 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
1106 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
1107 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1108 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1110 } else if(idx
>= 0) {
1112 int32_t len
= T_CString_integerToString(buf
, idx
, 10);
1113 ures_appendResPath(resB
, buf
, len
, status
);
1114 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
1115 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
1118 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1120 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
1121 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
1124 resB
->fVersion
= NULL
;
1126 /*resB->fParent = parent->fRes;*/
1127 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
1128 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
1132 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
1133 UBool isStackObject
;
1134 if(U_FAILURE(*status
) || r
== original
) {
1137 if(original
!= NULL
) {
1139 isStackObject
= FALSE
;
1140 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1143 *status
= U_MEMORY_ALLOCATION_ERROR
;
1147 isStackObject
= ures_isStackObject(r
);
1148 ures_closeBundle(r
, FALSE
);
1150 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1153 if(original
->fResPath
) {
1154 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1156 ures_setIsStackObject(r
, isStackObject
);
1157 if(r
->fData
!= NULL
) {
1158 entryIncrease(r
->fData
);
1165 * Functions to retrieve data from resource bundles.
1168 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1170 if (status
==NULL
|| U_FAILURE(*status
)) {
1174 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1177 s
= res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1179 *status
= U_RESOURCE_TYPE_MISMATCH
;
1185 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1186 char *dest
, int32_t *pLength
,
1188 UErrorCode
*status
) {
1191 if (U_FAILURE(*status
)) {
1194 if (pLength
!= NULL
) {
1195 capacity
= *pLength
;
1199 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1200 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1204 if (length16
== 0) {
1205 /* empty string, return as read-only pointer */
1206 if (pLength
!= NULL
) {
1210 u_terminateChars(dest
, capacity
, 0, status
);
1216 /* We need to transform the string to the destination buffer. */
1217 if (capacity
< length16
) {
1218 /* No chance for the string to fit. Pure preflighting. */
1219 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1221 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1223 * We know the string will fit into dest because each UChar turns
1224 * into at most three UTF-8 bytes. Fill the latter part of dest
1225 * so that callers do not expect to use dest as a string pointer,
1226 * hopefully leading to more robust code for when resource bundles
1227 * may store UTF-8 natively.
1228 * (In which case dest would not be used at all.)
1230 * We do not do this if forceCopy=TRUE because then the caller
1231 * expects the string to start exactly at dest.
1233 * The test above for <= 0x2aaaaaaa prevents overflows.
1234 * The +1 is for the NUL terminator.
1236 int32_t maxLength
= 3 * length16
+ 1;
1237 if (capacity
> maxLength
) {
1238 dest
+= capacity
- maxLength
;
1239 capacity
= maxLength
;
1242 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1246 U_CAPI
const char * U_EXPORT2
1247 ures_getUTF8String(const UResourceBundle
*resB
,
1248 char *dest
, int32_t *pLength
,
1250 UErrorCode
*status
) {
1252 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1253 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1256 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1257 UErrorCode
* status
) {
1259 if (status
==NULL
|| U_FAILURE(*status
)) {
1263 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1266 p
= res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1268 *status
= U_RESOURCE_TYPE_MISMATCH
;
1273 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1274 UErrorCode
* status
) {
1276 if (status
==NULL
|| U_FAILURE(*status
)) {
1280 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1283 p
= res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1285 *status
= U_RESOURCE_TYPE_MISMATCH
;
1290 /* this function returns a signed integer */
1291 /* it performs sign extension */
1292 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1293 if (status
==NULL
|| U_FAILURE(*status
)) {
1297 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1300 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1301 *status
= U_RESOURCE_TYPE_MISMATCH
;
1304 return RES_GET_INT(resB
->fRes
);
1307 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1308 if (status
==NULL
|| U_FAILURE(*status
)) {
1312 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1315 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1316 *status
= U_RESOURCE_TYPE_MISMATCH
;
1319 return RES_GET_UINT(resB
->fRes
);
1322 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1326 return res_getPublicType(resB
->fRes
);
1329 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1337 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1345 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1346 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1347 const UChar
* result
= 0;
1348 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1349 result
= ures_getString(tempRes
, len
, status
);
1350 ures_close(tempRes
);
1353 return res_getString(&(resB
->fResData
), r
, len
);
1357 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1364 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1368 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1371 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1372 Resource r
= RES_BOGUS
;
1374 if (status
==NULL
|| U_FAILURE(*status
)) {
1378 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1382 if(resB
->fIndex
== resB
->fSize
-1) {
1383 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1386 switch(RES_GET_TYPE(resB
->fRes
)) {
1388 case URES_STRING_V2
:
1389 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1393 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1394 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1395 /* TODO: do the fallback */
1397 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1400 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1401 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1402 /* TODO: do the fallback */
1404 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1406 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1409 case URES_INT_VECTOR
:
1410 *status
= U_RESOURCE_TYPE_MISMATCH
;
1411 default: /*fall through*/
1419 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1420 const char *key
= NULL
;
1421 Resource r
= RES_BOGUS
;
1423 if (status
==NULL
|| U_FAILURE(*status
)) {
1428 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1433 if(resB
->fIndex
== resB
->fSize
-1) {
1434 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1438 switch(RES_GET_TYPE(resB
->fRes
)) {
1442 case URES_STRING_V2
:
1443 case URES_INT_VECTOR
:
1444 return ures_copyResb(fillIn
, resB
, status
);
1448 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1449 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1450 /* TODO: do the fallback */
1452 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1455 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1456 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1457 /* TODO: do the fallback */
1459 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1469 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1470 const char* key
= NULL
;
1471 Resource r
= RES_BOGUS
;
1473 if (status
==NULL
|| U_FAILURE(*status
)) {
1478 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1483 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1484 switch(RES_GET_TYPE(resB
->fRes
)) {
1488 case URES_STRING_V2
:
1489 case URES_INT_VECTOR
:
1490 return ures_copyResb(fillIn
, resB
, status
);
1494 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1495 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1496 /* TODO: do the fallback */
1498 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1501 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1502 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1503 /* TODO: do the fallback */
1505 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1511 *status
= U_MISSING_RESOURCE_ERROR
;
1517 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1518 const char* key
= NULL
;
1519 Resource r
= RES_BOGUS
;
1521 if (status
==NULL
|| U_FAILURE(*status
)) {
1525 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1529 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1530 switch(RES_GET_TYPE(resB
->fRes
)) {
1532 case URES_STRING_V2
:
1533 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1537 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1538 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1539 /* TODO: do the fallback */
1541 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1544 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1545 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1546 /* TODO: do the fallback */
1548 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1550 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1553 case URES_INT_VECTOR
:
1554 *status
= U_RESOURCE_TYPE_MISMATCH
;
1557 /* must not occur */
1558 *status
= U_INTERNAL_PROGRAM_ERROR
;
1562 *status
= U_MISSING_RESOURCE_ERROR
;
1567 U_CAPI
const char * U_EXPORT2
1568 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1570 char *dest
, int32_t *pLength
,
1572 UErrorCode
*status
) {
1574 const UChar
*s16
= ures_getStringByIndex(resB
, idx
, &length16
, status
);
1575 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1578 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1579 return resB->fResPath;
1582 U_CAPI UResourceBundle
* U_EXPORT2
1583 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1585 UResourceBundle
*first
= NULL
;
1586 UResourceBundle
*result
= fillIn
;
1587 char *packageName
= NULL
;
1588 char *pathToResource
= NULL
, *save
= NULL
;
1589 char *locale
= NULL
, *localeEnd
= NULL
;
1592 if(status
== NULL
|| U_FAILURE(*status
)) {
1596 length
= (int32_t)(uprv_strlen(path
)+1);
1597 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1599 if(pathToResource
== NULL
) {
1600 *status
= U_MEMORY_ALLOCATION_ERROR
;
1603 uprv_memcpy(pathToResource
, path
, length
);
1605 locale
= pathToResource
;
1606 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1608 packageName
= pathToResource
;
1609 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1610 if(pathToResource
== NULL
) {
1611 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1613 *pathToResource
= 0;
1614 locale
= pathToResource
+1;
1618 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1619 if(localeEnd
!= NULL
) {
1623 first
= ures_open(packageName
, locale
, status
);
1625 if(U_SUCCESS(*status
)) {
1627 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1629 result
= ures_copyResb(fillIn
, first
, status
);
1637 U_CAPI UResourceBundle
* U_EXPORT2
1638 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1640 Resource res
= RES_BOGUS
;
1641 UResourceBundle
*result
= fillIn
;
1644 if(status
== NULL
|| U_FAILURE(*status
)) {
1648 /* here we do looping and circular alias checking */
1649 /* this loop is here because aliasing is resolved on this level, not on res level */
1650 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1652 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1653 if(res
!= RES_BOGUS
) {
1654 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1657 *status
= U_MISSING_RESOURCE_ERROR
;
1660 } while(*path
); /* there is more stuff in the path */
1664 U_INTERNAL
const UChar
* U_EXPORT2
1665 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1668 UErrorCode
*status
) {
1670 UResourceBundle stack
;
1671 const UChar
* retVal
= NULL
;
1672 ures_initStackObject(&stack
);
1673 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1674 retVal
= ures_getString(&stack
, len
, status
);
1676 if ( retVal
!= NULL
&& u_strlen(retVal
) == 3 && retVal
[0] == EMPTY_SET
&& retVal
[1] == EMPTY_SET
&& retVal
[2] == EMPTY_SET
) {
1679 *status
= U_MISSING_RESOURCE_ERROR
;
1685 Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1687 static Resource
getTableItemByKeyPath(const ResourceData
*pResData
, Resource table
, const char *key
) {
1688 Resource resource
= table
; /* The current resource */
1689 icu::CharString path
;
1690 UErrorCode errorCode
= U_ZERO_ERROR
;
1691 path
.append(key
, errorCode
);
1692 if (U_FAILURE(errorCode
)) { return RES_BOGUS
; }
1693 char *pathPart
= path
.data(); /* Path from current resource to desired resource */
1694 UResType type
= (UResType
)RES_GET_TYPE(resource
); /* the current resource type */
1695 while (*pathPart
&& resource
!= RES_BOGUS
&& URES_IS_CONTAINER(type
)) {
1696 char *nextPathPart
= uprv_strchr(pathPart
, RES_PATH_SEPARATOR
);
1697 if (nextPathPart
!= NULL
) {
1698 *nextPathPart
= 0; /* Terminating null for this part of path. */
1701 nextPathPart
= uprv_strchr(pathPart
, 0);
1704 const char *pathP
= pathPart
;
1705 resource
= res_getTableItemByKey(pResData
, resource
, &t
, &pathP
);
1706 type
= (UResType
)RES_GET_TYPE(resource
);
1707 pathPart
= nextPathPart
;
1715 U_CAPI UResourceBundle
* U_EXPORT2
1716 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1718 UResourceBundle
*fillIn
,
1719 UErrorCode
*status
) {
1720 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1721 /*UResourceDataEntry *realData = NULL;*/
1722 UResourceBundle
*helper
= NULL
;
1724 if (status
==NULL
|| U_FAILURE(*status
)) {
1728 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1732 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1733 if(URES_IS_TABLE(type
)) {
1734 res
= getTableItemByKeyPath(&(resB
->fResData
), resB
->fRes
, inKey
);
1735 const char* key
= inKey
;
1736 if(res
== RES_BOGUS
) {
1737 UResourceDataEntry
*dataEntry
= resB
->fData
;
1739 char* myPath
= path
;
1740 const char* resPath
= resB
->fResPath
;
1741 int32_t len
= resB
->fResPathLen
;
1742 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1743 dataEntry
= dataEntry
->fParent
;
1744 rootRes
= dataEntry
->fData
.rootRes
;
1746 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1748 uprv_memcpy(path
, resPath
, len
);
1750 uprv_strcpy(path
+len
, inKey
);
1754 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1755 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1756 /* We hit an alias, but we didn't finish following the path. */
1757 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1758 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1760 dataEntry
= helper
->fData
;
1761 rootRes
= helper
->fRes
;
1762 resPath
= helper
->fResPath
;
1763 len
= helper
->fResPathLen
;
1769 } while(*myPath
); /* Continue until the whole path is consumed */
1772 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1773 if(res
!= RES_BOGUS
) {
1774 /* check if resB->fResPath gives the right name here */
1775 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1776 *status
= U_USING_DEFAULT_WARNING
;
1778 *status
= U_USING_FALLBACK_WARNING
;
1781 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1783 *status
= U_MISSING_RESOURCE_ERROR
;
1786 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1790 *status
= U_RESOURCE_TYPE_MISMATCH
;
1797 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1798 Resource res
= RES_BOGUS
;
1799 UResourceDataEntry
*realData
= NULL
;
1800 const char *key
= inKey
;
1802 if (status
==NULL
|| U_FAILURE(*status
)) {
1806 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1810 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1811 if(URES_IS_TABLE(type
)) {
1813 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1814 if(res
== RES_BOGUS
) {
1816 if(resB
->fHasFallback
== TRUE
) {
1817 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1818 if(U_SUCCESS(*status
)) {
1819 /* check if resB->fResPath gives the right name here */
1820 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1822 *status
= U_MISSING_RESOURCE_ERROR
;
1825 *status
= U_MISSING_RESOURCE_ERROR
;
1828 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1832 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1834 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1835 /* here should go a first attempt to locate the key using index table */
1836 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1837 if(U_SUCCESS(*status
)) {
1838 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1840 *status
= U_MISSING_RESOURCE_ERROR
;
1845 *status
= U_RESOURCE_TYPE_MISMATCH
;
1850 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1851 Resource res
= RES_BOGUS
;
1852 UResourceDataEntry
*realData
= NULL
;
1853 const char* key
= inKey
;
1855 if (status
==NULL
|| U_FAILURE(*status
)) {
1859 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1863 int32_t type
= RES_GET_TYPE(resB
->fRes
);
1864 if(URES_IS_TABLE(type
)) {
1867 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1869 if(res
== RES_BOGUS
) {
1871 if(resB
->fHasFallback
== TRUE
) {
1872 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1873 if(U_SUCCESS(*status
)) {
1874 switch (RES_GET_TYPE(res
)) {
1876 case URES_STRING_V2
:
1877 return res_getString(rd
, res
, len
);
1880 const UChar
* result
= 0;
1881 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1882 result
= ures_getString(tempRes
, len
, status
);
1883 ures_close(tempRes
);
1887 *status
= U_RESOURCE_TYPE_MISMATCH
;
1890 *status
= U_MISSING_RESOURCE_ERROR
;
1893 *status
= U_MISSING_RESOURCE_ERROR
;
1896 switch (RES_GET_TYPE(res
)) {
1898 case URES_STRING_V2
:
1899 return res_getString(&(resB
->fResData
), res
, len
);
1902 const UChar
* result
= 0;
1903 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1904 result
= ures_getString(tempRes
, len
, status
);
1905 ures_close(tempRes
);
1909 *status
= U_RESOURCE_TYPE_MISMATCH
;
1914 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1916 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1917 /* here should go a first attempt to locate the key using index table */
1918 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1919 if(U_SUCCESS(*status
)) {
1920 return res_getString(rd
, res
, len
);
1922 *status
= U_MISSING_RESOURCE_ERROR
;
1927 *status
= U_RESOURCE_TYPE_MISMATCH
;
1932 U_CAPI
const char * U_EXPORT2
1933 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
1935 char *dest
, int32_t *pLength
,
1937 UErrorCode
*status
) {
1939 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
1940 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1943 /* TODO: clean from here down */
1946 * INTERNAL: Get the name of the first real locale (not placeholder)
1947 * that has resource bundle data.
1949 U_INTERNAL
const char* U_EXPORT2
1950 ures_getLocaleInternal(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1952 if (status
==NULL
|| U_FAILURE(*status
)) {
1955 if (!resourceBundle
) {
1956 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1959 return resourceBundle
->fData
->fName
;
1963 U_CAPI
const char* U_EXPORT2
1964 ures_getLocale(const UResourceBundle
* resourceBundle
,
1967 return ures_getLocaleInternal(resourceBundle
, status
);
1971 U_CAPI
const char* U_EXPORT2
1972 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1973 ULocDataLocaleType type
,
1974 UErrorCode
* status
) {
1975 if (status
==NULL
|| U_FAILURE(*status
)) {
1978 if (!resourceBundle
) {
1979 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1983 case ULOC_ACTUAL_LOCALE
:
1984 return resourceBundle
->fData
->fName
;
1985 case ULOC_VALID_LOCALE
:
1986 return resourceBundle
->fTopLevelData
->fName
;
1987 case ULOC_REQUESTED_LOCALE
:
1990 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1996 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
2001 return resB
->fData
->fName
;
2005 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
2010 return resB
->fData
->fPath
;
2014 /* OLD API implementation */
2017 * API: This function is used to open a resource bundle
2018 * proper fallback chaining is executed while initialization.
2019 * The result is stored in cache for later fallback search.
2021 U_CAPI
void U_EXPORT2
2022 ures_openFillIn(UResourceBundle
*r
, const char* path
,
2023 const char* localeID
, UErrorCode
* status
) {
2025 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2027 UResourceDataEntry
*firstData
;
2028 UBool isStackObject
= ures_isStackObject(r
);
2029 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2031 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2032 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2033 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2037 ures_closeBundle(r
, FALSE
);
2038 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2039 ures_setIsStackObject(r
, isStackObject
);
2040 r
->fHasFallback
= TRUE
;
2041 r
->fIsTopLevel
= TRUE
;
2043 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2044 if(U_FAILURE(*status
)) {
2047 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
2048 firstData
= r
->fData
;
2049 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
2050 firstData
= firstData
->fParent
;
2052 uprv_memcpy(&r
->fResData
, &firstData
->fData
, sizeof(ResourceData
));
2053 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2054 r
->fRes
= r
->fResData
.rootRes
;
2055 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2056 r
->fTopLevelData
= r
->fData
;
2060 U_CAPI UResourceBundle
* U_EXPORT2
2061 ures_open(const char* path
,
2062 const char* localeID
,
2065 char canonLocaleID
[ULOC_FULLNAME_CAPACITY
];
2066 UResourceDataEntry
*hasData
= NULL
;
2069 if(status
== NULL
|| U_FAILURE(*status
)) {
2073 /* first "canonicalize" the locale ID */
2074 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
2075 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
2076 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2080 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2082 *status
= U_MEMORY_ALLOCATION_ERROR
;
2086 uprv_memset(r
, 0, sizeof(UResourceBundle
));
2087 r
->fHasFallback
= TRUE
;
2088 r
->fIsTopLevel
= TRUE
;
2089 ures_setIsStackObject(r
, FALSE
);
2091 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
2092 if(U_FAILURE(*status
)) {
2096 r
->fTopLevelData
= r
->fData
;
2099 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
2100 hasData
= hasData
->fParent
;
2101 if(hasData
== NULL
) {
2102 /* This can happen only if fallback chain gets broken by an act of God */
2103 /* TODO: this unlikely to happen, consider removing it */
2104 entryClose(r
->fData
);
2106 *status
= U_MISSING_RESOURCE_ERROR
;
2111 uprv_memcpy(&r
->fResData
, &hasData
->fData
, sizeof(ResourceData
));
2112 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
2113 r
->fRes
= r
->fResData
.rootRes
;
2114 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2116 if(r->fData->fPath != NULL) {
2117 ures_setResPath(r, r->fData->fPath);
2118 ures_appendResPath(r, RES_PATH_PACKAGE_S);
2119 ures_appendResPath(r, r->fData->fName);
2121 ures_setResPath(r, r->fData->fName);
2130 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
2131 * or sought. However, alias substitution will happen!
2133 U_CAPI UResourceBundle
* U_EXPORT2
2134 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
2136 UErrorCode subStatus
= U_ZERO_ERROR
;
2138 if(status
== NULL
|| U_FAILURE(*status
)) {
2142 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
2144 *status
= U_MEMORY_ALLOCATION_ERROR
;
2148 r
->fHasFallback
= FALSE
;
2149 r
->fIsTopLevel
= TRUE
;
2150 ures_setIsStackObject(r
, FALSE
);
2152 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
2153 if(U_FAILURE(subStatus
)) {
2154 *status
= subStatus
;
2158 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
2159 /* we didn't find one we were looking for - so openDirect */
2161 entryClose(r
->fData
);
2163 *status
= U_MISSING_RESOURCE_ERROR
;
2169 uprv_memcpy(&r
->fResData
, &r
->fData
->fData
, sizeof(ResourceData
));
2170 /* r->fHasFallback remains FALSE here in ures_openDirect() */
2171 r
->fRes
= r
->fResData
.rootRes
;
2172 /*r->fParent = RES_BOGUS;*/
2173 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2176 /*r->fParentRes = NULL;*/
2177 r
->fTopLevelData
= r
->fData
;
2183 * API: Counts members. For arrays and tables, returns number of resources.
2184 * For strings, returns 1.
2186 U_CAPI
int32_t U_EXPORT2
2187 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2188 const char* resourceKey
,
2191 UResourceBundle resData
;
2192 ures_initStackObject(&resData
);
2193 if (status
==NULL
|| U_FAILURE(*status
)) {
2196 if(resourceBundle
== NULL
) {
2197 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2200 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2202 if(resData
.fResData
.data
!= NULL
) {
2203 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2204 ures_close(&resData
);
2207 *status
= U_MISSING_RESOURCE_ERROR
;
2208 ures_close(&resData
);
2214 * Internal function.
2215 * Return the version number associated with this ResourceBundle as a string.
2217 * @param resourceBundle The resource bundle for which the version is checked.
2218 * @return A version number string as specified in the resource bundle or its parent.
2219 * The caller does not own this string.
2220 * @see ures_getVersion
2223 U_INTERNAL
const char* U_EXPORT2
2224 ures_getVersionNumberInternal(const UResourceBundle
*resourceBundle
)
2226 if (!resourceBundle
) return NULL
;
2228 if(resourceBundle
->fVersion
== NULL
) {
2230 /* If the version ID has not been built yet, then do so. Retrieve */
2231 /* the minor version from the file. */
2232 UErrorCode status
= U_ZERO_ERROR
;
2233 int32_t minor_len
= 0;
2236 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2238 /* Determine the length of of the final version string. This is */
2239 /* the length of the major part + the length of the separator */
2240 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2243 len
= (minor_len
> 0) ? minor_len
: 1;
2245 /* Allocate the string, and build it up. */
2246 /* + 1 for zero byte */
2249 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2250 /* Check for null pointer. */
2251 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2256 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2257 resourceBundle
->fVersion
[len
] = '\0';
2260 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2264 return resourceBundle
->fVersion
;
2267 U_CAPI
const char* U_EXPORT2
2268 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2270 return ures_getVersionNumberInternal(resourceBundle
);
2273 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2276 u_versionFromString(versionInfo
, ures_getVersionNumberInternal(resB
));
2279 /** Tree support functions *******************************/
2280 #define INDEX_LOCALE_NAME "res_index"
2281 #define INDEX_TAG "InstalledLocales"
2282 #define DEFAULT_TAG "default"
2284 #if defined(URES_TREE_DEBUG)
2288 typedef struct ULocalesContext
{
2289 UResourceBundle installed
;
2290 UResourceBundle curr
;
2293 static void U_CALLCONV
2294 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2295 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2296 ures_close(&ctx
->curr
);
2297 ures_close(&ctx
->installed
);
2299 uprv_free(enumerator
);
2302 static int32_t U_CALLCONV
2303 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
* /*status*/) {
2304 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2305 return ures_getSize(&ctx
->installed
);
2308 static const char* U_CALLCONV
2309 ures_loc_nextLocale(UEnumeration
* en
,
2310 int32_t* resultLength
,
2311 UErrorCode
* status
) {
2312 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2313 UResourceBundle
*res
= &(ctx
->installed
);
2314 UResourceBundle
*k
= NULL
;
2315 const char *result
= NULL
;
2317 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2318 result
= ures_getKey(k
);
2319 len
= (int32_t)uprv_strlen(result
);
2322 *resultLength
= len
;
2327 static void U_CALLCONV
2328 ures_loc_resetLocales(UEnumeration
* en
,
2329 UErrorCode
* /*status*/) {
2330 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2331 ures_resetIterator(res
);
2335 static const UEnumeration gLocalesEnum
= {
2338 ures_loc_closeLocales
,
2339 ures_loc_countLocales
,
2341 ures_loc_nextLocale
,
2342 ures_loc_resetLocales
2346 U_CAPI UEnumeration
* U_EXPORT2
2347 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2349 UResourceBundle
*idx
= NULL
;
2350 UEnumeration
*en
= NULL
;
2351 ULocalesContext
*myContext
= NULL
;
2353 if(U_FAILURE(*status
)) {
2356 myContext
= static_cast<ULocalesContext
*>(uprv_malloc(sizeof(ULocalesContext
)));
2357 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2358 if(!en
|| !myContext
) {
2359 *status
= U_MEMORY_ALLOCATION_ERROR
;
2361 uprv_free(myContext
);
2364 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2366 ures_initStackObject(&myContext
->installed
);
2367 ures_initStackObject(&myContext
->curr
);
2368 idx
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2369 ures_getByKey(idx
, INDEX_TAG
, &myContext
->installed
, status
);
2370 if(U_SUCCESS(*status
)) {
2371 #if defined(URES_TREE_DEBUG)
2372 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2373 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2375 en
->context
= myContext
;
2377 #if defined(URES_TREE_DEBUG)
2378 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2380 ures_close(&myContext
->installed
);
2381 uprv_free(myContext
);
2391 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2393 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2394 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2401 U_CAPI
int32_t U_EXPORT2
2402 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2403 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2404 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2406 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2407 char defVal
[1024] = ""; /* default value for given locale */
2408 char defLoc
[1024] = ""; /* default value for given locale */
2409 char base
[1024] = ""; /* base locale */
2412 char full
[1024] = "";
2413 UResourceBundle bund1
, bund2
;
2414 UResourceBundle
*res
= NULL
;
2415 UErrorCode subStatus
= U_ZERO_ERROR
;
2417 if(U_FAILURE(*status
)) return 0;
2418 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2419 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2422 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2423 #if defined(URES_TREE_DEBUG)
2424 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2425 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2427 ures_initStackObject(&bund1
);
2428 ures_initStackObject(&bund2
);
2431 uprv_strcpy(parent
, base
);
2432 uprv_strcpy(found
, base
);
2435 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2436 *isAvailable
= TRUE
;
2437 if (U_SUCCESS(subStatus
)) {
2438 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2440 uenum_close(locEnum
);
2443 if(U_FAILURE(subStatus
)) {
2444 *status
= subStatus
;
2449 subStatus
= U_ZERO_ERROR
;
2450 res
= ures_open(path
, parent
, &subStatus
);
2451 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2452 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2454 *isAvailable
= FALSE
;
2456 isAvailable
= NULL
; /* only want to set this the first time around */
2458 #if defined(URES_TREE_DEBUG)
2459 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2461 if(U_FAILURE(subStatus
)) {
2462 *status
= subStatus
;
2463 } else if(subStatus
== U_ZERO_ERROR
) {
2464 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2465 if(subStatus
== U_ZERO_ERROR
) {
2466 const UChar
*defUstr
;
2468 /* look for default item */
2469 #if defined(URES_TREE_DEBUG)
2470 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2471 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2473 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2474 if(U_SUCCESS(subStatus
) && defLen
) {
2475 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2476 #if defined(URES_TREE_DEBUG)
2477 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2478 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2480 uprv_strcpy(defLoc
, parent
);
2482 uprv_strcpy(kwVal
, defVal
);
2483 #if defined(URES_TREE_DEBUG)
2484 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2485 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2492 subStatus
= U_ZERO_ERROR
;
2495 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2498 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2500 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2502 /* Now, see if we can find the kwVal collator.. start the search over.. */
2503 uprv_strcpy(parent
, base
);
2504 uprv_strcpy(found
, base
);
2507 subStatus
= U_ZERO_ERROR
;
2508 res
= ures_open(path
, parent
, &subStatus
);
2509 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2510 *isAvailable
= FALSE
;
2512 isAvailable
= NULL
; /* only want to set this the first time around */
2514 #if defined(URES_TREE_DEBUG)
2515 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2516 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2518 if(U_FAILURE(subStatus
)) {
2519 *status
= subStatus
;
2520 } else if(subStatus
== U_ZERO_ERROR
) {
2521 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2522 #if defined(URES_TREE_DEBUG)
2523 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2525 if(subStatus
== U_ZERO_ERROR
) {
2526 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2527 #if defined(URES_TREE_DEBUG)
2528 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2530 if(subStatus
== U_ZERO_ERROR
) {
2531 #if defined(URES_TREE_DEBUG)
2532 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2533 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2535 uprv_strcpy(full
, parent
);
2537 uprv_strcpy(full
, "root");
2539 /* now, recalculate default kw if need be */
2540 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2541 const UChar
*defUstr
;
2543 /* look for default item */
2544 #if defined(URES_TREE_DEBUG)
2545 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2546 path
?path
:"ICUDATA", full
);
2548 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2549 if(U_SUCCESS(subStatus
) && defLen
) {
2550 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2551 #if defined(URES_TREE_DEBUG)
2552 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2553 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2555 uprv_strcpy(defLoc
, full
);
2557 } /* end of recalculate default KW */
2558 #if defined(URES_TREE_DEBUG)
2560 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2564 #if defined(URES_TREE_DEBUG)
2565 fprintf(stderr
, "err=%s in %s looking for %s\n",
2566 u_errorName(subStatus
), parent
, kwVal
);
2572 subStatus
= U_ZERO_ERROR
;
2574 uprv_strcpy(found
, parent
);
2575 uloc_getParent(found
,parent
,1023,&subStatus
);
2577 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2579 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2580 #if defined(URES_TREE_DEBUG)
2581 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2583 uprv_strcpy(kwVal
, defVal
);
2584 uprv_strcpy(parent
, base
);
2585 uprv_strcpy(found
, base
);
2587 do { /* search for 'default' named item */
2588 subStatus
= U_ZERO_ERROR
;
2589 res
= ures_open(path
, parent
, &subStatus
);
2590 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2591 *isAvailable
= FALSE
;
2593 isAvailable
= NULL
; /* only want to set this the first time around */
2595 #if defined(URES_TREE_DEBUG)
2596 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2597 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2599 if(U_FAILURE(subStatus
)) {
2600 *status
= subStatus
;
2601 } else if(subStatus
== U_ZERO_ERROR
) {
2602 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2603 if(subStatus
== U_ZERO_ERROR
) {
2604 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2605 if(subStatus
== U_ZERO_ERROR
) {
2606 #if defined(URES_TREE_DEBUG)
2607 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2608 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2610 uprv_strcpy(full
, parent
);
2612 uprv_strcpy(full
, "root");
2615 /* now, recalculate default kw if need be */
2616 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2617 const UChar
*defUstr
;
2619 /* look for default item */
2620 #if defined(URES_TREE_DEBUG)
2621 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2622 path
?path
:"ICUDATA", full
);
2624 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2625 if(U_SUCCESS(subStatus
) && defLen
) {
2626 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2627 #if defined(URES_TREE_DEBUG)
2628 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2629 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2631 uprv_strcpy(defLoc
, full
);
2633 } /* end of recalculate default KW */
2634 #if defined(URES_TREE_DEBUG)
2636 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2642 subStatus
= U_ZERO_ERROR
;
2644 uprv_strcpy(found
, parent
);
2645 uloc_getParent(found
,parent
,1023,&subStatus
);
2647 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2650 if(U_SUCCESS(*status
)) {
2652 #if defined(URES_TREE_DEBUG)
2653 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2655 *status
= U_MISSING_RESOURCE_ERROR
;
2656 } else if(omitDefault
) {
2657 #if defined(URES_TREE_DEBUG)
2658 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2660 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2661 /* found the keyword in a *child* of where the default tag was present. */
2662 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2663 /* and the default is in or in an ancestor of the current locale */
2664 #if defined(URES_TREE_DEBUG)
2665 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2671 uprv_strcpy(found
, full
);
2673 uprv_strcat(found
, "@");
2674 uprv_strcat(found
, keyword
);
2675 uprv_strcat(found
, "=");
2676 uprv_strcat(found
, kwVal
);
2677 } else if(!omitDefault
) {
2678 uprv_strcat(found
, "@");
2679 uprv_strcat(found
, keyword
);
2680 uprv_strcat(found
, "=");
2681 uprv_strcat(found
, defVal
);
2684 /* we found the default locale - no need to repeat it.*/
2689 length
= (int32_t)uprv_strlen(found
);
2691 if(U_SUCCESS(*status
)) {
2692 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2694 uprv_strncpy(result
, found
, copyLength
);
2697 *status
= U_MISSING_RESOURCE_ERROR
;
2703 return u_terminateChars(result
, resultCapacity
, length
, status
);
2706 U_CAPI UEnumeration
* U_EXPORT2
2707 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2709 #define VALUES_BUF_SIZE 2048
2710 #define VALUES_LIST_SIZE 512
2712 char valuesBuf
[VALUES_BUF_SIZE
];
2713 int32_t valuesIndex
= 0;
2714 const char *valuesList
[VALUES_LIST_SIZE
];
2715 int32_t valuesCount
= 0;
2720 UEnumeration
*locs
= NULL
;
2722 UResourceBundle item
;
2723 UResourceBundle subItem
;
2725 ures_initStackObject(&item
);
2726 ures_initStackObject(&subItem
);
2727 locs
= ures_openAvailableLocales(path
, status
);
2729 if(U_FAILURE(*status
)) {
2731 ures_close(&subItem
);
2738 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2739 UResourceBundle
*bund
= NULL
;
2740 UResourceBundle
*subPtr
= NULL
;
2741 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2742 bund
= ures_openDirect(path
, locale
, &subStatus
);
2744 #if defined(URES_TREE_DEBUG)
2745 if(!bund
|| U_FAILURE(subStatus
)) {
2746 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2747 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2751 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2753 if(!bund
|| U_FAILURE(subStatus
)) {
2754 #if defined(URES_TREE_DEBUG)
2755 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2756 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2763 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2764 && U_SUCCESS(subStatus
)) {
2767 k
= ures_getKey(subPtr
);
2769 #if defined(URES_TREE_DEBUG)
2770 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2772 for(i
=0;k
&&i
<valuesCount
;i
++) {
2773 if(!uprv_strcmp(valuesList
[i
],k
)) {
2774 k
= NULL
; /* found duplicate */
2778 int32_t kLen
= (int32_t)uprv_strlen(k
);
2779 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2780 continue; /* don't need 'default'. */
2782 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2783 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2784 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2786 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2787 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2788 valuesIndex
+= kLen
;
2789 #if defined(URES_TREE_DEBUG)
2790 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2791 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2793 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2799 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2802 ures_close(&subItem
);
2804 #if defined(URES_TREE_DEBUG)
2805 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2806 valuesIndex
, valuesCount
);
2808 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2811 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2812 U_INTERNAL UBool U_EXPORT2
2813 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2814 if(res1
==NULL
|| res2
==NULL
){
2815 return res1
==res2
; /* pointer comparision */
2817 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2818 return (res1
->fKey
==res2
->fKey
);
2820 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2824 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2827 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2828 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2830 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2834 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2837 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2840 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2843 if(res1
->fRes
!= res2
->fRes
){
2848 U_INTERNAL UResourceBundle
* U_EXPORT2
2849 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2850 UResourceBundle
* bundle
= NULL
;
2851 UResourceBundle
* ret
= NULL
;
2852 if(U_FAILURE(*status
) || res
== NULL
){
2855 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2856 if(res
->fResPath
!=NULL
){
2857 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2864 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2865 ures_getParentBundle(const UResourceBundle
* res
){
2869 return res
->fParentRes
;
2873 U_INTERNAL
void U_EXPORT2
2874 ures_getVersionByKey(const UResourceBundle
* res
, const char *key
, UVersionInfo ver
, UErrorCode
*status
) {
2877 str
= ures_getStringByKey(res
, key
, &len
, status
);
2878 if(U_SUCCESS(*status
)) {
2879 u_versionFromUString(ver
, str
);