2 ******************************************************************************
3 * Copyright (C) 1997-2008, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 ******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 04/01/97 aliu Creation.
13 * 06/14/99 stephen Removed functions taking a filename suffix.
14 * 07/20/99 stephen Changed for UResourceBundle typedef'd to void*
15 * 11/09/99 weiv Added ures_getLocale()
16 * March 2000 weiv Total overhaul - using data in DLLs
17 * 06/20/2000 helena OS/400 port changes; mostly typecast.
18 * 06/24/02 weiv Added support for resource sharing
19 ******************************************************************************
22 #include "unicode/ustring.h"
23 #include "unicode/ucnv.h"
31 #include "unicode/uenum.h"
39 Static cache for already opened resource bundles - mostly for keeping fallback info
40 TODO: This cache should probably be removed when the deprecated code is
43 static UHashtable
*cache
= NULL
;
45 static UMTX resbMutex
= NULL
;
47 /* INTERNAL: hashes an entry */
48 static int32_t U_CALLCONV
hashEntry(const UHashTok parm
) {
49 UResourceDataEntry
*b
= (UResourceDataEntry
*)parm
.pointer
;
50 UHashTok namekey
, pathkey
;
51 namekey
.pointer
= b
->fName
;
52 pathkey
.pointer
= b
->fPath
;
53 return uhash_hashChars(namekey
)+37*uhash_hashChars(pathkey
);
56 /* INTERNAL: compares two entries */
57 static UBool U_CALLCONV
compareEntries(const UHashTok p1
, const UHashTok p2
) {
58 UResourceDataEntry
*b1
= (UResourceDataEntry
*)p1
.pointer
;
59 UResourceDataEntry
*b2
= (UResourceDataEntry
*)p2
.pointer
;
60 UHashTok name1
, name2
, path1
, path2
;
61 name1
.pointer
= b1
->fName
;
62 name2
.pointer
= b2
->fName
;
63 path1
.pointer
= b1
->fPath
;
64 path2
.pointer
= b2
->fPath
;
65 return (UBool
)(uhash_compareChars(name1
, name2
) &
66 uhash_compareChars(path1
, path2
));
71 * Internal function, gets parts of locale name according
72 * to the position of '_' character
74 static UBool
chopLocale(char *name
) {
75 char *i
= uprv_strrchr(name
, '_');
88 static void entryIncrease(UResourceDataEntry
*entry
) {
89 umtx_lock(&resbMutex
);
90 entry
->fCountExisting
++;
91 while(entry
->fParent
!= NULL
) {
92 entry
= entry
->fParent
;
93 entry
->fCountExisting
++;
95 umtx_unlock(&resbMutex
);
99 * Internal function. Tries to find a resource in given Resource
100 * Bundle, as well as in its parents
102 static const ResourceData
*getFallbackData(const UResourceBundle
* resBundle
, const char* * resTag
, UResourceDataEntry
* *realData
, Resource
*res
, UErrorCode
*status
) {
103 UResourceDataEntry
*resB
= resBundle
->fData
;
108 if(resB
->fBogus
== U_ZERO_ERROR
) { /* if this resource is real, */
109 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
); /* try to get data from there */
112 if(resBundle
->fHasFallback
== TRUE
) {
113 while(*res
== RES_BOGUS
&& resB
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
114 resB
= resB
->fParent
;
115 if(resB
->fBogus
== U_ZERO_ERROR
) {
117 *res
= res_getTableItemByKey(&(resB
->fData
), resB
->fData
.rootRes
, &indexR
, resTag
);
122 if(*res
!= RES_BOGUS
) { /* If the resource is found in parents, we need to adjust the error */
124 if(uprv_strcmp(resB
->fName
, uloc_getDefault())==0 || uprv_strcmp(resB
->fName
, kRootLocaleName
)==0) {
125 *status
= U_USING_DEFAULT_WARNING
;
127 *status
= U_USING_FALLBACK_WARNING
;
131 return (&(resB
->fData
));
132 } else { /* If resource is not found, we need to give an error */
133 *status
= U_MISSING_RESOURCE_ERROR
;
137 *status
= U_MISSING_RESOURCE_ERROR
;
143 free_entry(UResourceDataEntry
*entry
) {
144 if(entry
->fBogus
== U_ZERO_ERROR
) {
145 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
);
156 /* Works just like ucnv_flushCache() */
157 /* TODO: figure out why fCountExisting may not go to zero. Do not make this function public yet. */
158 static int32_t ures_flushCache()
160 UResourceDataEntry
*resB
= NULL
;
162 int32_t rbDeletedNum
= 0;
163 const UHashElement
*e
;
165 /*if shared data hasn't even been lazy evaluated yet
168 umtx_lock(&resbMutex
);
170 umtx_unlock(&resbMutex
);
174 /*creates an enumeration to iterate through every element in the table */
175 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
177 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
178 /* Deletes only if reference counter == 0
179 * Don't worry about the children of this node.
180 * Those will eventually get deleted too, if not already.
181 * Don't worry about the parents of this node.
182 * Those will eventually get deleted too, if not already.
184 /* DONE: figure out why fCountExisting may not go to zero. Do not make this function public yet. */
185 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
186 /* some resource bundles are still open somewhere. */
188 /*U_ASSERT(resB->fCountExisting == 0);*/
189 if (resB
->fCountExisting
== 0) {
191 uhash_removeElement(cache
, e
);
195 umtx_unlock(&resbMutex
);
200 static UBool U_CALLCONV
ures_cleanup(void)
204 if (cache
!= NULL
&& uhash_count(cache
) == 0) {
209 if (cache
== NULL
&& resbMutex
!= NULL
) {
210 umtx_destroy(&resbMutex
);
212 return (cache
== NULL
);
215 /** INTERNAL: Initializes the cache for resources */
216 static void initCache(UErrorCode
*status
) {
217 UBool makeCache
= FALSE
;
218 UMTX_CHECK(&resbMutex
, (cache
== NULL
), makeCache
);
220 UHashtable
*newCache
= uhash_open(hashEntry
, compareEntries
, NULL
, status
);
221 if (U_FAILURE(*status
)) {
224 umtx_lock(&resbMutex
);
228 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
230 umtx_unlock(&resbMutex
);
231 if(newCache
!= NULL
) {
232 uhash_close(newCache
);
237 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
239 static void setEntryName(UResourceDataEntry
*res
, char *name
, UErrorCode
*status
) {
240 int32_t len
= uprv_strlen(name
);
241 if(res
->fName
!= NULL
&& res
->fName
!= res
->fNameBuffer
) {
242 uprv_free(res
->fName
);
244 if (len
< (int32_t)sizeof(res
->fNameBuffer
)) {
245 res
->fName
= res
->fNameBuffer
;
248 res
->fName
= (char *)uprv_malloc(len
+1);
250 if(res
->fName
== NULL
) {
251 *status
= U_MEMORY_ALLOCATION_ERROR
;
253 uprv_strcpy(res
->fName
, name
);
258 * INTERNAL: Inits and opens an entry from a data DLL.
259 * CAUTION: resbMutex must be locked when calling this function.
261 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
262 UResourceDataEntry
*r
= NULL
;
263 UResourceDataEntry find
;
264 /*int32_t hashValue;*/
266 const char *myPath
= NULL
;
267 char aliasName
[100] = { 0 };
268 int32_t aliasLen
= 0;
269 /*UBool isAlias = FALSE;*/
272 if(U_FAILURE(*status
)) {
276 /* here we try to deduce the right locale name */
277 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
278 uprv_strcpy(name
, uloc_getDefault());
279 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
280 uprv_strcpy(name
, kRootLocaleName
);
281 } else { /* otherwise, we'll open what we're given */
282 uprv_strcpy(name
, localeID
);
285 if(path
!= NULL
) { /* if we actually have path, we'll use it */
290 find
.fPath
= (char *)myPath
;
292 /* calculate the hash value of the entry */
293 hashkey
.pointer
= (void *)&find
;
294 /*hashValue = hashEntry(hashkey);*/
296 /* check to see if we already have this entry */
297 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
299 if(r
!= NULL
) { /* if the entry is already in the hash table */
300 r
->fCountExisting
++; /* we just increase it's reference count */
301 /* if the resource has a warning */
302 /* we don't want to overwrite a status with no error */
303 if(r
->fBogus
!= U_ZERO_ERROR
) {
304 *status
= r
->fBogus
; /* set the returning status */
306 } else { /* otherwise, we'll try to construct a new entry */
307 UBool result
= FALSE
;
309 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
311 *status
= U_MEMORY_ALLOCATION_ERROR
;
315 uprv_memset(r
, 0, sizeof(UResourceDataEntry
));
316 r
->fCountExisting
= 1;
317 /*r->fHashKey = hashValue;*/
319 setEntryName(r
, name
, status
);
320 if (U_FAILURE(*status
)) {
326 r
->fPath
= (char *)uprv_strdup(myPath
);
327 if(r
->fPath
== NULL
) {
328 *status
= U_MEMORY_ALLOCATION_ERROR
;
334 /* this is the actual loading - returns bool true/false */
335 result
= res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
337 if (result
== FALSE
|| U_FAILURE(*status
)) {
338 /* we have no such entry in dll, so it will always use fallback */
339 *status
= U_USING_FALLBACK_WARNING
;
340 r
->fBogus
= U_USING_FALLBACK_WARNING
;
341 } else { /* if we have a regular entry */
342 /* We might be able to do this a wee bit more efficiently (we could check whether the aliased data) */
343 /* is already in the cache), but it's good the way it is */
344 /* handle the alias by trying to get out the %%Alias tag.*/
345 /* We'll try to get alias string from the bundle */
346 Resource aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
347 if (aliasres
!= RES_BOGUS
) {
348 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
349 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
350 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
352 res_unload(&(r
->fData
));
353 result
= res_load(&(r
->fData
), r
->fPath
, aliasName
, status
);
354 if (result
== FALSE
|| U_FAILURE(*status
)) {
355 /* we couldn't load aliased data - so we have no data */
356 *status
= U_USING_FALLBACK_WARNING
;
357 r
->fBogus
= U_USING_FALLBACK_WARNING
;
359 setEntryName(r
, aliasName
, status
);
365 UResourceDataEntry
*oldR
= NULL
;
366 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
367 /* just insert it in the cache */
368 uhash_put(cache
, (void *)r
, r
, status
);
369 if (U_FAILURE(*status
)) {
374 /* somebody have already inserted it while we were working, discard newly opened data */
375 /* Also, we could get here IF we opened an alias */
387 /* CAUTION: resbMutex must be locked when calling this function! */
388 static UResourceDataEntry
*findFirstExisting(const char* path
, char* name
, UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
389 UResourceDataEntry
*r
= NULL
;
390 UBool hasRealData
= FALSE
;
391 const char *defaultLoc
= uloc_getDefault();
392 *hasChopped
= TRUE
; /* we're starting with a fresh name */
394 while(*hasChopped
&& !hasRealData
) {
395 r
= init_entry(name
, path
, status
);
396 /* Null pointer test */
397 if (U_FAILURE(*status
)) {
400 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
401 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
403 /* this entry is not real. We will discard it. */
404 /* However, the parent line for this entry is */
405 /* not to be used - as there might be parent */
406 /* lines in cache from previous openings that */
407 /* are not updated yet. */
409 /*entryCloseInt(r);*/
411 *status
= U_USING_FALLBACK_WARNING
;
413 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
416 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
418 /*Fallback data stuff*/
419 *hasChopped
= chopLocale(name
);
424 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
429 resB
->fMagic1
= MAGIC1
;
430 resB
->fMagic2
= MAGIC2
;
434 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
435 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
439 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
440 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
441 ures_setIsStackObject(resB
, TRUE
);
444 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
, UErrorCode
* status
) {
445 UErrorCode intStatus
= U_ZERO_ERROR
;
446 UErrorCode parentStatus
= U_ZERO_ERROR
;
447 UResourceDataEntry
*r
= NULL
;
448 UResourceDataEntry
*t1
= NULL
;
449 UResourceDataEntry
*t2
= NULL
;
450 UBool isDefault
= FALSE
;
451 UBool isRoot
= FALSE
;
452 UBool hasRealData
= FALSE
;
453 UBool hasChopped
= TRUE
;
458 if(U_FAILURE(*status
)) {
462 uprv_strcpy(name
, localeID
);
464 umtx_lock(&resbMutex
);
466 /* We're going to skip all the locales that do not have any data */
467 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
469 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
472 while (hasChopped
&& !isRoot
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
) {
473 /* insert regular parents */
474 t2
= init_entry(name
, r
->fPath
, &parentStatus
);
475 /* Check for null pointer. */
477 *status
= U_MEMORY_ALLOCATION_ERROR
;
482 hasChopped
= chopLocale(name
);
486 /* we could have reached this point without having any real data */
487 /* if that is the case, we need to chain in the default locale */
488 if(r
==NULL
&& !isDefault
&& !isRoot
/*&& t1->fParent == NULL*/) {
489 /* insert default locale */
490 uprv_strcpy(name
, uloc_getDefault());
491 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
492 intStatus
= U_USING_DEFAULT_WARNING
;
493 if(r
!= NULL
) { /* the default locale exists */
497 while (hasChopped
&& t1
->fParent
== NULL
) {
498 /* insert chopped defaults */
499 t2
= init_entry(name
, r
->fPath
, &parentStatus
);
500 /* Check for null pointer. */
502 *status
= U_MEMORY_ALLOCATION_ERROR
;
507 hasChopped
= chopLocale(name
);
512 /* we could still have r == NULL at this point - maybe even default locale is not */
515 uprv_strcpy(name
, kRootLocaleName
);
516 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
519 intStatus
= U_USING_DEFAULT_WARNING
;
521 } else { /* we don't even have the root locale */
522 *status
= U_MISSING_RESOURCE_ERROR
;
525 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
526 /* insert root locale */
527 t2
= init_entry(kRootLocaleName
, r
->fPath
, &parentStatus
);
528 /* Check for null pointer. */
530 *status
= U_MEMORY_ALLOCATION_ERROR
;
534 r
->fBogus
= U_USING_DEFAULT_WARNING
;
536 hasRealData
= (UBool
)((t2
->fBogus
== U_ZERO_ERROR
) | hasRealData
);
541 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
542 t1
->fParent
->fCountExisting
++;
544 hasRealData
= (UBool
)((t1
->fBogus
== U_ZERO_ERROR
) | hasRealData
);
548 umtx_unlock(&resbMutex
);
550 if(U_SUCCESS(*status
)) {
551 if(U_SUCCESS(parentStatus
)) {
552 if(intStatus
!= U_ZERO_ERROR
) {
557 *status
= parentStatus
;
567 * Functions to create and destroy resource bundles.
568 * CAUTION: resbMutex must be locked when calling this function.
571 static void entryCloseInt(UResourceDataEntry
*resB
) {
572 UResourceDataEntry
*p
= resB
;
574 while(resB
!= NULL
) {
576 resB
->fCountExisting
--;
578 /* Entries are left in the cache. TODO: add ures_cacheFlush() to force a flush
581 if(resB->fCountExisting <= 0) {
582 uhash_remove(cache, resB);
583 if(resB->fBogus == U_ZERO_ERROR) {
584 res_unload(&(resB->fData));
586 if(resB->fName != NULL) {
587 uprv_free(resB->fName);
589 if(resB->fPath != NULL) {
590 uprv_free(resB->fPath);
601 * API: closes a resource bundle and cleans up.
604 static void entryClose(UResourceDataEntry
*resB
) {
605 umtx_lock(&resbMutex
);
607 umtx_unlock(&resbMutex
);
611 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
612 if(resB->fResPath == NULL) {
613 resB->fResPath = resB->fResBuf;
614 *(resB->fResPath) = 0;
616 resB->fResPathLen = uprv_strlen(toAdd);
617 if(RES_BUFSIZE <= resB->fResPathLen+1) {
618 if(resB->fResPath == resB->fResBuf) {
619 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
621 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
624 uprv_strcpy(resB->fResPath, toAdd);
627 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
, UErrorCode
*status
) {
628 int32_t resPathLenOrig
= resB
->fResPathLen
;
629 if(resB
->fResPath
== NULL
) {
630 resB
->fResPath
= resB
->fResBuf
;
631 *(resB
->fResPath
) = 0;
632 resB
->fResPathLen
= 0;
634 resB
->fResPathLen
+= lenToAdd
;
635 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
636 if(resB
->fResPath
== resB
->fResBuf
) {
637 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
638 /* Check that memory was allocated correctly. */
639 if (resB
->fResPath
== NULL
) {
640 *status
= U_MEMORY_ALLOCATION_ERROR
;
643 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
645 char *temp
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
646 /* Check that memory was reallocated correctly. */
648 *status
= U_MEMORY_ALLOCATION_ERROR
;
651 resB
->fResPath
= temp
;
654 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
657 static void ures_freeResPath(UResourceBundle
*resB
) {
658 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
659 uprv_free(resB
->fResPath
);
661 resB
->fResPath
= NULL
;
662 resB
->fResPathLen
= 0;
666 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
669 if(resB
->fData
!= NULL
) {
670 entryClose(resB
->fData
);
672 if(resB
->fVersion
!= NULL
) {
673 uprv_free(resB
->fVersion
);
675 ures_freeResPath(resB
);
677 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
682 /* poison the data */
683 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
689 U_CAPI
void U_EXPORT2
690 ures_close(UResourceBundle
* resB
)
692 ures_closeBundle(resB
, TRUE
);
695 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
696 const char *key
, int32_t index
, UResourceDataEntry
*realData
,
697 const UResourceBundle
*parent
, int32_t noAlias
,
698 UResourceBundle
*resB
, UErrorCode
*status
)
700 if(status
== NULL
|| U_FAILURE(*status
)) {
703 if (parent
== NULL
) {
704 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
707 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
708 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
710 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
712 /* we have an alias, now let's cut it up */
713 char stackAlias
[200];
714 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
718 * Allocate enough space for both the char * version
719 * of the alias and parent->fResPath.
721 * We do this so that res_findResource() can modify the path,
722 * which allows us to remove redundant _res_findResource() variants
724 * res_findResource() now NUL-terminates each segment so that table keys
725 * can always be compared with strcmp() instead of strncmp().
726 * Saves code there and simplifies testing and code coverage.
730 ++len
; /* count the terminating NUL */
731 if(parent
->fResPath
!= NULL
) {
732 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
739 if(capacity
<= sizeof(stackAlias
)) {
740 capacity
= sizeof(stackAlias
);
741 chAlias
= stackAlias
;
743 chAlias
= (char *)uprv_malloc(capacity
);
745 if(chAlias
== NULL
) {
746 *status
= U_MEMORY_ALLOCATION_ERROR
;
750 u_UCharsToChars(alias
, chAlias
, len
);
752 if(*chAlias
== RES_PATH_SEPARATOR
) {
753 /* there is a path included */
754 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
756 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
762 if(uprv_strcmp(path
, "LOCALE") == 0) {
763 /* this is an XPath alias, starting with "/LOCALE/" */
764 /* it contains the path to a resource which should be looked up */
765 /* starting in the requested locale */
767 locale
= parent
->fTopLevelData
->fName
; /* this is the requested locale's name */
768 path
= realData
->fPath
; /* we will be looking in the same package */
770 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
773 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
780 /* no path, start with a locale */
782 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
787 path
= realData
->fPath
;
792 /* got almost everything, let's try to open */
793 /* first, open the bundle with real data */
794 UResourceBundle
*result
= resB
;
795 const char* temp
= NULL
;
796 UErrorCode intStatus
= U_ZERO_ERROR
;
797 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
798 if(U_SUCCESS(intStatus
)) {
799 if(keyPath
== NULL
) {
800 /* no key path. This means that we are going to
801 * to use the corresponding resource from
804 /* first, we are going to get a corresponding parent
805 * resource to the one we are searching.
807 char *aKey
= parent
->fResPath
;
809 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
811 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
816 /* we need to make keyPath from parent's fResPath and
817 * current key, if there is a key associated
819 len
= (int32_t)(uprv_strlen(key
) + 1);
822 if(chAlias
== stackAlias
) {
823 chAlias
= (char *)uprv_malloc(capacity
);
825 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
827 if(chAlias
== NULL
) {
829 *status
= U_MEMORY_ALLOCATION_ERROR
;
833 uprv_memcpy(chAlias
, key
, len
);
835 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
836 } else if(index
!= -1) {
837 /* if there is no key, but there is an index, try to get by the index */
838 /* here we have either a table or an array, so get the element */
839 if(RES_GET_TYPE(r
) == URES_TABLE
|| RES_GET_TYPE(r
) == URES_TABLE32
) {
840 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, index
, (const char **)&aKey
);
842 r
= res_getArrayItem(&(mainRes
->fResData
), r
, index
);
846 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
848 *status
= U_MISSING_RESOURCE_ERROR
;
852 /* this one is a bit trickier.
853 * we start finding keys, but after we resolve one alias, the path might continue.
855 * aliastest:alias { "testtypes/anotheralias/Sequence" }
856 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
857 * aliastest resource should finally have the sequence, not collation elements.
859 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
860 char stackPath
[URES_MAX_BUFFER_SIZE
];
861 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
862 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
863 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
864 if(pathBuf
== NULL
) {
865 *status
= U_MEMORY_ALLOCATION_ERROR
;
869 uprv_strcpy(pathBuf
, keyPath
);
871 /* now we have fallback following here */
873 r
= dataEntry
->fData
.rootRes
;
874 /* this loop handles 'found' resources over several levels */
875 while(*myPath
&& U_SUCCESS(*status
)) {
876 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
877 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
878 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
881 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
882 dataEntry
= result
->fData
;
884 } else { /* no resource found, we don't really want to look anymore on this level */
888 dataEntry
= dataEntry
->fParent
;
889 uprv_strcpy(pathBuf
, keyPath
);
891 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
893 *status
= U_MISSING_RESOURCE_ERROR
;
896 if(pathBuf
!= stackPath
) {
900 } else { /* we failed to open the resource we're aliasing to */
903 if(chAlias
!= stackAlias
) {
906 if(mainRes
!= result
) {
912 /* bad alias, should be an error */
913 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
917 *status
= U_TOO_MANY_ALIASES_ERROR
;
922 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
925 *status
= U_MEMORY_ALLOCATION_ERROR
;
928 ures_setIsStackObject(resB
, FALSE
);
929 resB
->fResPath
= NULL
;
930 resB
->fResPathLen
= 0;
932 if(resB
->fData
!= NULL
) {
933 entryClose(resB
->fData
);
935 if(resB
->fVersion
!= NULL
) {
936 uprv_free(resB
->fVersion
);
939 weiv: if stack object was passed in, it doesn't really need to be reinited,
940 since the purpose of initing is to remove stack junk. However, at this point
941 we would not do anything to an allocated object, so stack object should be
945 if(ures_isStackObject(resB) != FALSE) {
946 ures_initStackObject(resB);
950 ures_freeResPath(resB
);
953 resB
->fData
= realData
;
954 entryIncrease(resB
->fData
);
955 resB
->fHasFallback
= FALSE
;
956 resB
->fIsTopLevel
= FALSE
;
959 /*resB->fParentRes = parent;*/
960 resB
->fTopLevelData
= parent
->fTopLevelData
;
961 if(parent
->fResPath
&& parent
!= resB
) {
962 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
, status
);
965 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
), status
);
966 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
967 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
969 } else if(index
>= 0) {
971 int32_t len
= T_CString_integerToString(buf
, index
, 10);
972 ures_appendResPath(resB
, buf
, len
, status
);
973 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
974 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1, status
);
977 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
979 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
980 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
983 resB
->fVersion
= NULL
;
985 /*resB->fParent = parent->fRes;*/
986 uprv_memmove(&resB
->fResData
, rdata
, sizeof(ResourceData
));
987 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
991 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
993 if(U_FAILURE(*status
) || r
== original
) {
996 if(original
!= NULL
) {
998 isStackObject
= FALSE
;
999 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1002 *status
= U_MEMORY_ALLOCATION_ERROR
;
1006 isStackObject
= ures_isStackObject(r
);
1007 ures_closeBundle(r
, FALSE
);
1009 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
1012 if(original
->fResPath
) {
1013 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
, status
);
1015 ures_setIsStackObject(r
, isStackObject
);
1016 if(r
->fData
!= NULL
) {
1017 entryIncrease(r
->fData
);
1024 * Functions to retrieve data from resource bundles.
1027 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
1029 if (status
==NULL
|| U_FAILURE(*status
)) {
1033 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1037 switch(RES_GET_TYPE(resB
->fRes
)) {
1039 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1041 case URES_INT_VECTOR
:
1047 *status
= U_RESOURCE_TYPE_MISMATCH
;
1054 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1055 char *dest
, int32_t *pLength
,
1057 UErrorCode
*status
) {
1060 if (U_FAILURE(*status
)) {
1063 if (pLength
!= NULL
) {
1064 capacity
= *pLength
;
1068 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1069 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1073 if (length16
== 0) {
1074 /* empty string, return as read-only pointer */
1075 if (pLength
!= NULL
) {
1079 u_terminateChars(dest
, capacity
, 0, status
);
1085 /* We need to transform the string to the destination buffer. */
1086 if (capacity
< length16
) {
1087 /* No chance for the string to fit. Pure preflighting. */
1088 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1090 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1092 * We know the string will fit into dest because each UChar turns
1093 * into at most three UTF-8 bytes. Fill the latter part of dest
1094 * so that callers do not expect to use dest as a string pointer,
1095 * hopefully leading to more robust code for when resource bundles
1096 * may store UTF-8 natively.
1097 * (In which case dest would not be used at all.)
1099 * We do not do this if forceCopy=TRUE because then the caller
1100 * expects the string to start exactly at dest.
1102 * The test above for <= 0x2aaaaaaa prevents overflows.
1103 * The +1 is for the NUL terminator.
1105 int32_t maxLength
= 3 * length16
+ 1;
1106 if (capacity
> maxLength
) {
1107 dest
+= capacity
- maxLength
;
1108 capacity
= maxLength
;
1111 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1115 U_CAPI
const char * U_EXPORT2
1116 ures_getUTF8String(const UResourceBundle
*resB
,
1117 char *dest
, int32_t *pLength
,
1119 UErrorCode
*status
) {
1121 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1122 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1125 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1126 UErrorCode
* status
) {
1127 if (status
==NULL
|| U_FAILURE(*status
)) {
1131 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1134 switch(RES_GET_TYPE(resB
->fRes
)) {
1136 return res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1139 case URES_INT_VECTOR
:
1144 *status
= U_RESOURCE_TYPE_MISMATCH
;
1150 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1151 UErrorCode
* status
) {
1152 if (status
==NULL
|| U_FAILURE(*status
)) {
1156 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1159 switch(RES_GET_TYPE(resB
->fRes
)) {
1160 case URES_INT_VECTOR
:
1161 return res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1169 *status
= U_RESOURCE_TYPE_MISMATCH
;
1175 /* this function returns a signed integer */
1176 /* it performs sign extension */
1177 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1178 if (status
==NULL
|| U_FAILURE(*status
)) {
1182 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1185 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1186 *status
= U_RESOURCE_TYPE_MISMATCH
;
1189 return RES_GET_INT(resB
->fRes
);
1192 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1193 if (status
==NULL
|| U_FAILURE(*status
)) {
1197 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1200 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1201 *status
= U_RESOURCE_TYPE_MISMATCH
;
1204 return RES_GET_UINT(resB
->fRes
);
1208 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1214 type
= (UResType
) RES_GET_TYPE(resB
->fRes
);
1215 return type
== URES_TABLE32
? URES_TABLE
: type
;
1218 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1226 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1234 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1235 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1236 const UChar
* result
= 0;
1237 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1238 result
= ures_getString(tempRes
, len
, status
);
1239 ures_close(tempRes
);
1242 return res_getString(&(resB
->fResData
), r
, len
);
1246 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1253 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1257 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1260 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1261 Resource r
= RES_BOGUS
;
1263 if (status
==NULL
|| U_FAILURE(*status
)) {
1267 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1271 if(resB
->fIndex
== resB
->fSize
-1) {
1272 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1275 switch(RES_GET_TYPE(resB
->fRes
)) {
1279 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1282 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1283 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1284 /* TODO: do the fallback */
1286 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1288 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1289 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1290 /* TODO: do the fallback */
1292 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1294 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1295 case URES_INT_VECTOR
:
1304 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1305 const char *key
= NULL
;
1306 Resource r
= RES_BOGUS
;
1308 if (status
==NULL
|| U_FAILURE(*status
)) {
1313 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1318 if(resB
->fIndex
== resB
->fSize
-1) {
1319 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1323 switch(RES_GET_TYPE(resB
->fRes
)) {
1327 return ures_copyResb(fillIn
, resB
, status
);
1330 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1331 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1332 /* TODO: do the fallback */
1334 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1336 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1337 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1338 /* TODO: do the fallback */
1340 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1341 case URES_INT_VECTOR
:
1351 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1352 const char* key
= NULL
;
1353 Resource r
= RES_BOGUS
;
1355 if (status
==NULL
|| U_FAILURE(*status
)) {
1360 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1365 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1366 switch(RES_GET_TYPE(resB
->fRes
)) {
1370 return ures_copyResb(fillIn
, resB
, status
);
1373 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1374 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1375 /* TODO: do the fallback */
1377 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1379 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1380 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1381 /* TODO: do the fallback */
1383 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1384 case URES_INT_VECTOR
:
1390 *status
= U_MISSING_RESOURCE_ERROR
;
1396 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1397 const char* key
= NULL
;
1398 Resource r
= RES_BOGUS
;
1400 if (status
==NULL
|| U_FAILURE(*status
)) {
1404 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1408 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1409 switch(RES_GET_TYPE(resB
->fRes
)) {
1413 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1416 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1417 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1418 /* TODO: do the fallback */
1420 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1422 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1423 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1424 /* TODO: do the fallback */
1426 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1428 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1430 /*case URES_INT_VECTOR:*/
1432 /* must not occur */
1433 *status
= U_INTERNAL_PROGRAM_ERROR
;
1437 *status
= U_MISSING_RESOURCE_ERROR
;
1442 U_CAPI
const char * U_EXPORT2
1443 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1445 char *dest
, int32_t *pLength
,
1447 UErrorCode
*status
) {
1449 const UChar
*s16
= ures_getStringByIndex(resB
, index
, &length16
, status
);
1450 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1453 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1454 return resB->fResPath;
1457 U_CAPI UResourceBundle
* U_EXPORT2
1458 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1460 UResourceBundle
*first
= NULL
;
1461 UResourceBundle
*result
= fillIn
;
1462 char *packageName
= NULL
;
1463 char *pathToResource
= NULL
, *save
= NULL
;
1464 char *locale
= NULL
, *localeEnd
= NULL
;
1467 if(status
== NULL
|| U_FAILURE(*status
)) {
1471 length
= (int32_t)(uprv_strlen(path
)+1);
1472 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1474 if(pathToResource
== NULL
) {
1475 *status
= U_MEMORY_ALLOCATION_ERROR
;
1478 uprv_memcpy(pathToResource
, path
, length
);
1480 locale
= pathToResource
;
1481 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1483 packageName
= pathToResource
;
1484 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1485 if(pathToResource
== NULL
) {
1486 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1488 *pathToResource
= 0;
1489 locale
= pathToResource
+1;
1493 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1494 if(localeEnd
!= NULL
) {
1498 first
= ures_open(packageName
, locale
, status
);
1500 if(U_SUCCESS(*status
)) {
1502 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1504 result
= ures_copyResb(fillIn
, first
, status
);
1512 U_CAPI UResourceBundle
* U_EXPORT2
1513 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1515 Resource res
= RES_BOGUS
;
1516 UResourceBundle
*result
= fillIn
;
1519 if(status
== NULL
|| U_FAILURE(*status
)) {
1523 /* here we do looping and circular alias checking */
1524 /* this loop is here because aliasing is resolved on this level, not on res level */
1525 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1527 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1528 if(res
!= RES_BOGUS
) {
1529 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1532 *status
= U_MISSING_RESOURCE_ERROR
;
1535 } while(*path
); /* there is more stuff in the path */
1539 U_INTERNAL
const UChar
* U_EXPORT2
1540 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1543 UErrorCode
*status
) {
1545 UResourceBundle stack
;
1546 const UChar
* retVal
= NULL
;
1547 ures_initStackObject(&stack
);
1548 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1549 retVal
= ures_getString(&stack
, len
, status
);
1554 U_CAPI UResourceBundle
* U_EXPORT2
1555 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1557 UResourceBundle
*fillIn
,
1558 UErrorCode
*status
) {
1559 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1560 /*UResourceDataEntry *realData = NULL;*/
1561 const char *key
= inKey
;
1562 UResourceBundle
*helper
= NULL
;
1564 if (status
==NULL
|| U_FAILURE(*status
)) {
1568 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1572 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1574 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1575 if(res
== RES_BOGUS
) {
1576 UResourceDataEntry
*dataEntry
= resB
->fData
;
1578 char* myPath
= path
;
1579 const char* resPath
= resB
->fResPath
;
1580 int32_t len
= resB
->fResPathLen
;
1582 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1583 dataEntry
= dataEntry
->fParent
;
1584 rootRes
= dataEntry
->fData
.rootRes
;
1585 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1586 uprv_strncpy(path
, resPath
, len
);
1587 uprv_strcpy(path
+len
, inKey
);
1591 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1592 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1593 /* We hit an alias, but we didn't finish following the path. */
1594 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1595 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1597 dataEntry
= helper
->fData
;
1598 rootRes
= helper
->fRes
;
1599 resPath
= helper
->fResPath
;
1600 len
= helper
->fResPathLen
;
1606 } while(*myPath
); /* Continue until the whole path is consumed */
1609 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1610 if(res
!= RES_BOGUS
) {
1611 /* check if resB->fResPath gives the right name here */
1612 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1613 *status
= U_USING_DEFAULT_WARNING
;
1615 *status
= U_USING_FALLBACK_WARNING
;
1618 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1620 *status
= U_MISSING_RESOURCE_ERROR
;
1623 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1627 *status
= U_RESOURCE_TYPE_MISMATCH
;
1634 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1635 Resource res
= RES_BOGUS
;
1636 UResourceDataEntry
*realData
= NULL
;
1637 const char *key
= inKey
;
1639 if (status
==NULL
|| U_FAILURE(*status
)) {
1643 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1647 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1649 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1650 if(res
== RES_BOGUS
) {
1652 if(resB
->fHasFallback
== TRUE
) {
1653 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1654 if(U_SUCCESS(*status
)) {
1655 /* check if resB->fResPath gives the right name here */
1656 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1658 *status
= U_MISSING_RESOURCE_ERROR
;
1661 *status
= U_MISSING_RESOURCE_ERROR
;
1664 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1668 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1670 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1671 /* here should go a first attempt to locate the key using index table */
1672 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1673 if(U_SUCCESS(*status
)) {
1674 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1676 *status
= U_MISSING_RESOURCE_ERROR
;
1681 *status
= U_RESOURCE_TYPE_MISMATCH
;
1686 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1687 Resource res
= RES_BOGUS
;
1688 UResourceDataEntry
*realData
= NULL
;
1689 const char* key
= inKey
;
1691 if (status
==NULL
|| U_FAILURE(*status
)) {
1695 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1699 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1702 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1704 if(res
== RES_BOGUS
) {
1706 if(resB
->fHasFallback
== TRUE
) {
1707 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1708 if(U_SUCCESS(*status
)) {
1709 switch (RES_GET_TYPE(res
)) {
1714 return res_getString(rd
, res
, len
);
1717 const UChar
* result
= 0;
1718 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1719 result
= ures_getString(tempRes
, len
, status
);
1720 ures_close(tempRes
);
1724 *status
= U_RESOURCE_TYPE_MISMATCH
;
1727 *status
= U_MISSING_RESOURCE_ERROR
;
1730 *status
= U_MISSING_RESOURCE_ERROR
;
1733 switch (RES_GET_TYPE(res
)) {
1738 return res_getString(&(resB
->fResData
), res
, len
);
1741 const UChar
* result
= 0;
1742 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1743 result
= ures_getString(tempRes
, len
, status
);
1744 ures_close(tempRes
);
1748 *status
= U_RESOURCE_TYPE_MISMATCH
;
1753 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1755 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1756 /* here should go a first attempt to locate the key using index table */
1757 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1758 if(U_SUCCESS(*status
)) {
1759 return res_getString(rd
, res
, len
);
1761 *status
= U_MISSING_RESOURCE_ERROR
;
1766 *status
= U_RESOURCE_TYPE_MISMATCH
;
1771 U_CAPI
const char * U_EXPORT2
1772 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
1774 char *dest
, int32_t *pLength
,
1776 UErrorCode
*status
) {
1778 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
1779 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1782 /* TODO: clean from here down */
1785 * INTERNAL: Get the name of the first real locale (not placeholder)
1786 * that has resource bundle data.
1788 U_CAPI
const char* U_EXPORT2
1789 ures_getLocale(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1791 if (status
==NULL
|| U_FAILURE(*status
)) {
1794 if (!resourceBundle
) {
1795 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1798 return resourceBundle
->fData
->fName
;
1802 U_CAPI
const char* U_EXPORT2
1803 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1804 ULocDataLocaleType type
,
1805 UErrorCode
* status
) {
1806 if (status
==NULL
|| U_FAILURE(*status
)) {
1809 if (!resourceBundle
) {
1810 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1814 case ULOC_ACTUAL_LOCALE
:
1815 return resourceBundle
->fData
->fName
;
1816 case ULOC_VALID_LOCALE
:
1817 return resourceBundle
->fTopLevelData
->fName
;
1818 case ULOC_REQUESTED_LOCALE
:
1821 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1827 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
1832 return resB
->fData
->fName
;
1836 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
1841 return resB
->fData
->fPath
;
1845 /* OLD API implementation */
1848 * API: This function is used to open a resource bundle
1849 * proper fallback chaining is executed while initialization.
1850 * The result is stored in cache for later fallback search.
1852 U_CAPI
void U_EXPORT2
1853 ures_openFillIn(UResourceBundle
*r
, const char* path
,
1854 const char* localeID
, UErrorCode
* status
) {
1856 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1858 UResourceDataEntry
*firstData
;
1859 UBool isStackObject
= ures_isStackObject(r
);
1861 ures_closeBundle(r
, FALSE
);
1862 uprv_memset(r
, 0, sizeof(UResourceBundle
));
1863 ures_setIsStackObject(r
, isStackObject
);
1864 r
->fHasFallback
= TRUE
;
1865 r
->fIsTopLevel
= TRUE
;
1867 r
->fData
= entryOpen(path
, localeID
, status
);
1868 if(U_FAILURE(*status
)) {
1871 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
1872 firstData
= r
->fData
;
1873 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
1874 firstData
= firstData
->fParent
;
1876 uprv_memcpy(&r
->fResData
, &firstData
->fData
, sizeof(ResourceData
));
1877 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
1878 r
->fRes
= r
->fResData
.rootRes
;
1879 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1880 r
->fTopLevelData
= r
->fData
;
1884 U_CAPI UResourceBundle
* U_EXPORT2
1885 ures_open(const char* path
,
1886 const char* localeID
,
1889 char canonLocaleID
[100];
1890 UResourceDataEntry
*hasData
= NULL
;
1893 if(status
== NULL
|| U_FAILURE(*status
)) {
1897 /* first "canonicalize" the locale ID */
1898 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
1899 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
1900 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1904 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1906 *status
= U_MEMORY_ALLOCATION_ERROR
;
1910 uprv_memset(r
, 0, sizeof(UResourceBundle
));
1911 r
->fHasFallback
= TRUE
;
1912 r
->fIsTopLevel
= TRUE
;
1913 ures_setIsStackObject(r
, FALSE
);
1915 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
1916 if(U_FAILURE(*status
)) {
1920 r
->fTopLevelData
= r
->fData
;
1923 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
1924 hasData
= hasData
->fParent
;
1925 if(hasData
== NULL
) {
1926 /* This can happen only if fallback chain gets broken by an act of God */
1927 /* TODO: this unlikely to happen, consider removing it */
1928 entryClose(r
->fData
);
1930 *status
= U_MISSING_RESOURCE_ERROR
;
1935 uprv_memcpy(&r
->fResData
, &hasData
->fData
, sizeof(ResourceData
));
1936 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
1937 r
->fRes
= r
->fResData
.rootRes
;
1938 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1940 if(r->fData->fPath != NULL) {
1941 ures_setResPath(r, r->fData->fPath);
1942 ures_appendResPath(r, RES_PATH_PACKAGE_S);
1943 ures_appendResPath(r, r->fData->fName);
1945 ures_setResPath(r, r->fData->fName);
1954 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
1955 * or sought. However, alias substitution will happen!
1957 U_CAPI UResourceBundle
* U_EXPORT2
1958 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
1960 UErrorCode subStatus
= U_ZERO_ERROR
;
1962 if(status
== NULL
|| U_FAILURE(*status
)) {
1966 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1968 *status
= U_MEMORY_ALLOCATION_ERROR
;
1972 r
->fHasFallback
= FALSE
;
1973 r
->fIsTopLevel
= TRUE
;
1974 ures_setIsStackObject(r
, FALSE
);
1976 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
1977 if(U_FAILURE(subStatus
)) {
1978 *status
= subStatus
;
1982 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
1983 /* we didn't find one we were looking for - so openDirect */
1985 entryClose(r
->fData
);
1987 *status
= U_MISSING_RESOURCE_ERROR
;
1993 uprv_memcpy(&r
->fResData
, &r
->fData
->fData
, sizeof(ResourceData
));
1994 /* r->fHasFallback remains FALSE here in ures_openDirect() */
1995 r
->fRes
= r
->fResData
.rootRes
;
1996 /*r->fParent = RES_BOGUS;*/
1997 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
2000 /*r->fParentRes = NULL;*/
2001 r
->fTopLevelData
= r
->fData
;
2007 * API: Counts members. For arrays and tables, returns number of resources.
2008 * For strings, returns 1.
2010 U_CAPI
int32_t U_EXPORT2
2011 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
2012 const char* resourceKey
,
2015 UResourceBundle resData
;
2016 ures_initStackObject(&resData
);
2017 if (status
==NULL
|| U_FAILURE(*status
)) {
2020 if(resourceBundle
== NULL
) {
2021 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
2024 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
2026 if(resData
.fResData
.data
!= NULL
) {
2027 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
2028 ures_close(&resData
);
2031 *status
= U_MISSING_RESOURCE_ERROR
;
2032 ures_close(&resData
);
2037 U_CAPI
const char* U_EXPORT2
2038 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
2040 if (!resourceBundle
) return NULL
;
2042 if(resourceBundle
->fVersion
== NULL
) {
2044 /* If the version ID has not been built yet, then do so. Retrieve */
2045 /* the minor version from the file. */
2046 UErrorCode status
= U_ZERO_ERROR
;
2047 int32_t minor_len
= 0;
2050 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2052 /* Determine the length of of the final version string. This is */
2053 /* the length of the major part + the length of the separator */
2054 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2057 len
= (minor_len
> 0) ? minor_len
: 1;
2059 /* Allocate the string, and build it up. */
2060 /* + 1 for zero byte */
2063 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2064 /* Check for null pointer. */
2065 if (((UResourceBundle
*)resourceBundle
)->fVersion
== NULL
) {
2070 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2071 resourceBundle
->fVersion
[len
] = '\0';
2074 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2078 return resourceBundle
->fVersion
;
2081 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2084 u_versionFromString(versionInfo
, ures_getVersionNumber(resB
));
2087 /** Tree support functions *******************************/
2088 #define INDEX_LOCALE_NAME "res_index"
2089 #define INDEX_TAG "InstalledLocales"
2090 #define DEFAULT_TAG "default"
2092 #if defined(URES_TREE_DEBUG)
2096 typedef struct ULocalesContext
{
2097 UResourceBundle installed
;
2098 UResourceBundle curr
;
2101 static void U_CALLCONV
2102 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2103 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2104 ures_close(&ctx
->curr
);
2105 ures_close(&ctx
->installed
);
2107 uprv_free(enumerator
);
2110 static int32_t U_CALLCONV
2111 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
*status
) {
2112 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2113 return ures_getSize(&ctx
->installed
);
2116 static const char* U_CALLCONV
2117 ures_loc_nextLocale(UEnumeration
* en
,
2118 int32_t* resultLength
,
2119 UErrorCode
* status
) {
2120 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2121 UResourceBundle
*res
= &(ctx
->installed
);
2122 UResourceBundle
*k
= NULL
;
2123 const char *result
= NULL
;
2125 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2126 result
= ures_getKey(k
);
2127 len
= (int32_t)uprv_strlen(result
);
2130 *resultLength
= len
;
2135 static void U_CALLCONV
2136 ures_loc_resetLocales(UEnumeration
* en
,
2137 UErrorCode
* status
) {
2138 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2139 ures_resetIterator(res
);
2143 static const UEnumeration gLocalesEnum
= {
2146 ures_loc_closeLocales
,
2147 ures_loc_countLocales
,
2149 ures_loc_nextLocale
,
2150 ures_loc_resetLocales
2154 U_CAPI UEnumeration
* U_EXPORT2
2155 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2157 UResourceBundle
*index
= NULL
;
2158 UEnumeration
*en
= NULL
;
2159 ULocalesContext
*myContext
= NULL
;
2161 if(U_FAILURE(*status
)) {
2164 myContext
= uprv_malloc(sizeof(ULocalesContext
));
2165 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2166 if(!en
|| !myContext
) {
2167 *status
= U_MEMORY_ALLOCATION_ERROR
;
2169 uprv_free(myContext
);
2172 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2174 ures_initStackObject(&myContext
->installed
);
2175 ures_initStackObject(&myContext
->curr
);
2176 index
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2177 ures_getByKey(index
, INDEX_TAG
, &myContext
->installed
, status
);
2178 if(U_SUCCESS(*status
)) {
2179 #if defined(URES_TREE_DEBUG)
2180 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2181 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2183 en
->context
= myContext
;
2185 #if defined(URES_TREE_DEBUG)
2186 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2188 ures_close(&myContext
->installed
);
2189 uprv_free(myContext
);
2199 static UBool
isLocaleInList(UEnumeration
*locEnum
, const char *locToSearch
, UErrorCode
*status
) {
2201 while ((loc
= uenum_next(locEnum
, NULL
, status
)) != NULL
) {
2202 if (uprv_strcmp(loc
, locToSearch
) == 0) {
2209 U_CAPI
int32_t U_EXPORT2
2210 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2211 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2212 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2214 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2215 char defVal
[1024] = ""; /* default value for given locale */
2216 char defLoc
[1024] = ""; /* default value for given locale */
2217 char base
[1024] = ""; /* base locale */
2220 char full
[1024] = "";
2221 UResourceBundle bund1
, bund2
;
2222 UResourceBundle
*res
= NULL
;
2223 UErrorCode subStatus
= U_ZERO_ERROR
;
2225 if(U_FAILURE(*status
)) return 0;
2226 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2227 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2230 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2231 #if defined(URES_TREE_DEBUG)
2232 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2233 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2235 ures_initStackObject(&bund1
);
2236 ures_initStackObject(&bund2
);
2239 uprv_strcpy(parent
, base
);
2240 uprv_strcpy(found
, base
);
2243 UEnumeration
*locEnum
= ures_openAvailableLocales(path
, &subStatus
);
2244 *isAvailable
= TRUE
;
2245 if (U_SUCCESS(subStatus
)) {
2246 *isAvailable
= isLocaleInList(locEnum
, parent
, &subStatus
);
2248 uenum_close(locEnum
);
2251 if(U_FAILURE(subStatus
)) {
2252 *status
= subStatus
;
2257 subStatus
= U_ZERO_ERROR
;
2258 res
= ures_open(path
, parent
, &subStatus
);
2259 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2260 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
)
2262 *isAvailable
= FALSE
;
2264 isAvailable
= NULL
; /* only want to set this the first time around */
2266 #if defined(URES_TREE_DEBUG)
2267 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2269 if(U_FAILURE(subStatus
)) {
2270 *status
= subStatus
;
2271 } else if(subStatus
== U_ZERO_ERROR
) {
2272 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2273 if(subStatus
== U_ZERO_ERROR
) {
2274 const UChar
*defUstr
;
2276 /* look for default item */
2277 #if defined(URES_TREE_DEBUG)
2278 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2279 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2281 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2282 if(U_SUCCESS(subStatus
) && defLen
) {
2283 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2284 #if defined(URES_TREE_DEBUG)
2285 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2286 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2288 uprv_strcpy(defLoc
, parent
);
2290 uprv_strcpy(kwVal
, defVal
);
2291 #if defined(URES_TREE_DEBUG)
2292 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2293 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2300 subStatus
= U_ZERO_ERROR
;
2303 uprv_strcpy(found
, ures_getLocaleByType(res
, ULOC_VALID_LOCALE
, &subStatus
));
2306 uloc_getParent(found
,parent
,sizeof(parent
),&subStatus
);
2308 } while(!defVal
[0] && *found
&& uprv_strcmp(found
, "root") != 0 && U_SUCCESS(*status
));
2310 /* Now, see if we can find the kwVal collator.. start the search over.. */
2311 uprv_strcpy(parent
, base
);
2312 uprv_strcpy(found
, base
);
2315 subStatus
= U_ZERO_ERROR
;
2316 res
= ures_open(path
, parent
, &subStatus
);
2317 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2318 *isAvailable
= FALSE
;
2320 isAvailable
= NULL
; /* only want to set this the first time around */
2322 #if defined(URES_TREE_DEBUG)
2323 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2324 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2326 if(U_FAILURE(subStatus
)) {
2327 *status
= subStatus
;
2328 } else if(subStatus
== U_ZERO_ERROR
) {
2329 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2330 #if defined(URES_TREE_DEBUG)
2331 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2333 if(subStatus
== U_ZERO_ERROR
) {
2334 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2335 #if defined(URES_TREE_DEBUG)
2336 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2338 if(subStatus
== U_ZERO_ERROR
) {
2339 #if defined(URES_TREE_DEBUG)
2340 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2341 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2343 uprv_strcpy(full
, parent
);
2345 uprv_strcpy(full
, "root");
2347 /* now, recalculate default kw if need be */
2348 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2349 const UChar
*defUstr
;
2351 /* look for default item */
2352 #if defined(URES_TREE_DEBUG)
2353 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2354 path
?path
:"ICUDATA", full
);
2356 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2357 if(U_SUCCESS(subStatus
) && defLen
) {
2358 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2359 #if defined(URES_TREE_DEBUG)
2360 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2361 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2363 uprv_strcpy(defLoc
, full
);
2365 } /* end of recalculate default KW */
2366 #if defined(URES_TREE_DEBUG)
2368 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2372 #if defined(URES_TREE_DEBUG)
2373 fprintf(stderr
, "err=%s in %s looking for %s\n",
2374 u_errorName(subStatus
), parent
, kwVal
);
2380 subStatus
= U_ZERO_ERROR
;
2382 uprv_strcpy(found
, parent
);
2383 uloc_getParent(found
,parent
,1023,&subStatus
);
2385 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2387 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2388 #if defined(URES_TREE_DEBUG)
2389 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2391 uprv_strcpy(kwVal
, defVal
);
2392 uprv_strcpy(parent
, base
);
2393 uprv_strcpy(found
, base
);
2395 do { /* search for 'default' named item */
2396 subStatus
= U_ZERO_ERROR
;
2397 res
= ures_open(path
, parent
, &subStatus
);
2398 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2399 *isAvailable
= FALSE
;
2401 isAvailable
= NULL
; /* only want to set this the first time around */
2403 #if defined(URES_TREE_DEBUG)
2404 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2405 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2407 if(U_FAILURE(subStatus
)) {
2408 *status
= subStatus
;
2409 } else if(subStatus
== U_ZERO_ERROR
) {
2410 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2411 if(subStatus
== U_ZERO_ERROR
) {
2412 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2413 if(subStatus
== U_ZERO_ERROR
) {
2414 #if defined(URES_TREE_DEBUG)
2415 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2416 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2418 uprv_strcpy(full
, parent
);
2420 uprv_strcpy(full
, "root");
2423 /* now, recalculate default kw if need be */
2424 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2425 const UChar
*defUstr
;
2427 /* look for default item */
2428 #if defined(URES_TREE_DEBUG)
2429 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2430 path
?path
:"ICUDATA", full
);
2432 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2433 if(U_SUCCESS(subStatus
) && defLen
) {
2434 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2435 #if defined(URES_TREE_DEBUG)
2436 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2437 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2439 uprv_strcpy(defLoc
, full
);
2441 } /* end of recalculate default KW */
2442 #if defined(URES_TREE_DEBUG)
2444 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2450 subStatus
= U_ZERO_ERROR
;
2452 uprv_strcpy(found
, parent
);
2453 uloc_getParent(found
,parent
,1023,&subStatus
);
2455 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2458 if(U_SUCCESS(*status
)) {
2460 #if defined(URES_TREE_DEBUG)
2461 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2463 *status
= U_MISSING_RESOURCE_ERROR
;
2464 } else if(omitDefault
) {
2465 #if defined(URES_TREE_DEBUG)
2466 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2468 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2469 /* found the keyword in a *child* of where the default tag was present. */
2470 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2471 /* and the default is in or in an ancestor of the current locale */
2472 #if defined(URES_TREE_DEBUG)
2473 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2479 uprv_strcpy(found
, full
);
2481 uprv_strcat(found
, "@");
2482 uprv_strcat(found
, keyword
);
2483 uprv_strcat(found
, "=");
2484 uprv_strcat(found
, kwVal
);
2485 } else if(!omitDefault
) {
2486 uprv_strcat(found
, "@");
2487 uprv_strcat(found
, keyword
);
2488 uprv_strcat(found
, "=");
2489 uprv_strcat(found
, defVal
);
2492 /* we found the default locale - no need to repeat it.*/
2497 length
= (int32_t)uprv_strlen(found
);
2499 if(U_SUCCESS(*status
)) {
2500 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2502 uprv_strncpy(result
, found
, copyLength
);
2505 *status
= U_MISSING_RESOURCE_ERROR
;
2511 return u_terminateChars(result
, resultCapacity
, length
, status
);
2514 U_CAPI UEnumeration
* U_EXPORT2
2515 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2517 #define VALUES_BUF_SIZE 2048
2518 #define VALUES_LIST_SIZE 512
2520 char valuesBuf
[VALUES_BUF_SIZE
];
2521 int32_t valuesIndex
= 0;
2522 const char *valuesList
[VALUES_LIST_SIZE
];
2523 int32_t valuesCount
= 0;
2528 UEnumeration
*locs
= NULL
;
2530 UResourceBundle item
;
2531 UResourceBundle subItem
;
2533 ures_initStackObject(&item
);
2534 ures_initStackObject(&subItem
);
2535 locs
= ures_openAvailableLocales(path
, status
);
2537 if(U_FAILURE(*status
)) {
2539 ures_close(&subItem
);
2546 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2547 UResourceBundle
*bund
= NULL
;
2548 UResourceBundle
*subPtr
= NULL
;
2549 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2550 bund
= ures_openDirect(path
, locale
, &subStatus
);
2552 #if defined(URES_TREE_DEBUG)
2553 if(!bund
|| U_FAILURE(subStatus
)) {
2554 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2555 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2559 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2561 if(!bund
|| U_FAILURE(subStatus
)) {
2562 #if defined(URES_TREE_DEBUG)
2563 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2564 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2571 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2572 && U_SUCCESS(subStatus
)) {
2575 k
= ures_getKey(subPtr
);
2577 #if defined(URES_TREE_DEBUG)
2578 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2580 for(i
=0;k
&&i
<valuesCount
;i
++) {
2581 if(!uprv_strcmp(valuesList
[i
],k
)) {
2582 k
= NULL
; /* found duplicate */
2586 int32_t kLen
= (int32_t)uprv_strlen(k
);
2587 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2588 continue; /* don't need 'default'. */
2590 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2591 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2592 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2594 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2595 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2596 valuesIndex
+= kLen
;
2597 #if defined(URES_TREE_DEBUG)
2598 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2599 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2601 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2607 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2610 ures_close(&subItem
);
2612 #if defined(URES_TREE_DEBUG)
2613 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2614 valuesIndex
, valuesCount
);
2616 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2619 /* This code isn't needed, and given the documentation warnings the implementation is suspect */
2620 U_INTERNAL UBool U_EXPORT2
2621 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2622 if(res1
==NULL
|| res2
==NULL
){
2623 return res1
==res2
; /* pointer comparision */
2625 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2626 return (res1
->fKey
==res2
->fKey
);
2628 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2632 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2635 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2636 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2638 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2642 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2645 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2648 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2651 if(res1
->fRes
!= res2
->fRes
){
2656 U_INTERNAL UResourceBundle
* U_EXPORT2
2657 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2658 UResourceBundle
* bundle
= NULL
;
2659 UResourceBundle
* ret
= NULL
;
2660 if(U_FAILURE(*status
) || res
== NULL
){
2663 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2664 if(res
->fResPath
!=NULL
){
2665 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2672 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2673 ures_getParentBundle(const UResourceBundle
* res
){
2677 return res
->fParentRes
;