2 ******************************************************************************
3 * Copyright (C) 1997-2006, 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
;
142 /* Works just like ucnv_flushCache() */
143 /* TODO: figure out why fCountExisting may not go to zero. Do not make this function public yet. */
144 static int32_t ures_flushCache()
146 UResourceDataEntry
*resB
= NULL
;
148 int32_t rbDeletedNum
= 0;
149 const UHashElement
*e
;
151 /*if shared data hasn't even been lazy evaluated yet
154 umtx_lock(&resbMutex
);
156 umtx_unlock(&resbMutex
);
160 /*creates an enumeration to iterate through every element in the table */
161 while ((e
= uhash_nextElement(cache
, &pos
)) != NULL
)
163 resB
= (UResourceDataEntry
*) e
->value
.pointer
;
164 /* Deletes only if reference counter == 0
165 * Don't worry about the children of this node.
166 * Those will eventually get deleted too, if not already.
167 * Don't worry about the parents of this node.
168 * Those will eventually get deleted too, if not already.
170 /* DONE: figure out why fCountExisting may not go to zero. Do not make this function public yet. */
171 /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that */
172 /* some resource bundles are still open somewhere. */
174 /*U_ASSERT(resB->fCountExisting == 0);*/
175 if (resB
->fCountExisting
== 0) {
177 uhash_removeElement(cache
, e
);
178 if(resB
->fBogus
== U_ZERO_ERROR
) {
179 res_unload(&(resB
->fData
));
181 if(resB
->fName
!= NULL
) {
182 uprv_free(resB
->fName
);
184 if(resB
->fPath
!= NULL
) {
185 uprv_free(resB
->fPath
);
190 umtx_unlock(&resbMutex
);
195 static UBool U_CALLCONV
ures_cleanup(void)
199 if (cache
!= NULL
&& uhash_count(cache
) == 0) {
204 if (cache
== NULL
&& resbMutex
!= NULL
) {
205 umtx_destroy(&resbMutex
);
207 return (cache
== NULL
);
210 /** INTERNAL: Initializes the cache for resources */
211 static void initCache(UErrorCode
*status
) {
212 UBool makeCache
= FALSE
;
213 umtx_lock(&resbMutex
);
214 makeCache
= (cache
== NULL
);
215 umtx_unlock(&resbMutex
);
217 UHashtable
*newCache
= uhash_open(hashEntry
, compareEntries
, NULL
, status
);
218 if (U_FAILURE(*status
)) {
221 umtx_lock(&resbMutex
);
225 ucln_common_registerCleanup(UCLN_COMMON_URES
, ures_cleanup
);
227 umtx_unlock(&resbMutex
);
228 if(newCache
!= NULL
) {
229 uhash_close(newCache
);
234 /** INTERNAL: sets the name (locale) of the resource bundle to given name */
236 static void setEntryName(UResourceDataEntry
*res
, char *name
, UErrorCode
*status
) {
237 if(res
->fName
!= NULL
) {
238 uprv_free(res
->fName
);
240 res
->fName
= (char *)uprv_malloc(sizeof(char)*uprv_strlen(name
)+1);
241 if(res
->fName
== NULL
) {
242 *status
= U_MEMORY_ALLOCATION_ERROR
;
244 uprv_strcpy(res
->fName
, name
);
249 * INTERNAL: Inits and opens an entry from a data DLL.
250 * CAUTION: resbMutex must be locked when calling this function.
252 static UResourceDataEntry
*init_entry(const char *localeID
, const char *path
, UErrorCode
*status
) {
253 UResourceDataEntry
*r
= NULL
;
254 UResourceDataEntry find
;
257 const char *myPath
= NULL
;
258 char aliasName
[100] = { 0 };
259 int32_t aliasLen
= 0;
260 /*UBool isAlias = FALSE;*/
263 if(U_FAILURE(*status
)) {
267 /* here we try to deduce the right locale name */
268 if(localeID
== NULL
) { /* if localeID is NULL, we're trying to open default locale */
269 uprv_strcpy(name
, uloc_getDefault());
270 } else if(*localeID
== 0) { /* if localeID is "" then we try to open root locale */
271 uprv_strcpy(name
, kRootLocaleName
);
272 } else { /* otherwise, we'll open what we're given */
273 uprv_strcpy(name
, localeID
);
276 if(path
!= NULL
) { /* if we actually have path, we'll use it */
281 find
.fPath
= (char *)myPath
;
283 /* calculate the hash value of the entry */
284 hashkey
.pointer
= (void *)&find
;
285 hashValue
= hashEntry(hashkey
);
287 /* check to see if we already have this entry */
288 r
= (UResourceDataEntry
*)uhash_get(cache
, &find
);
290 if(r
!= NULL
) { /* if the entry is already in the hash table */
291 r
->fCountExisting
++; /* we just increase it's reference count */
292 /* if the resource has a warning */
293 /* we don't want to overwrite a status with no error */
294 if(r
->fBogus
!= U_ZERO_ERROR
) {
295 *status
= r
->fBogus
; /* set the returning status */
297 } else { /* otherwise, we'll try to construct a new entry */
298 UBool result
= FALSE
;
300 r
= (UResourceDataEntry
*) uprv_malloc(sizeof(UResourceDataEntry
));
303 *status
= U_MEMORY_ALLOCATION_ERROR
;
306 r
->fCountExisting
= 1;
309 setEntryName(r
, name
, status
);
312 if(myPath
!= NULL
&& !U_FAILURE(*status
)) {
313 r
->fPath
= (char *)uprv_malloc(sizeof(char)*uprv_strlen(myPath
)+1);
314 if(r
->fPath
== NULL
) {
315 *status
= U_MEMORY_ALLOCATION_ERROR
;
317 uprv_strcpy(r
->fPath
, myPath
);
321 r
->fHashKey
= hashValue
;
323 uprv_memset(&r
->fData
, 0, sizeof(ResourceData
));
324 r
->fBogus
= U_ZERO_ERROR
;
326 /* this is the actual loading - returns bool true/false */
327 result
= res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
329 if (result
== FALSE
|| U_FAILURE(*status
)) {
330 /* we have no such entry in dll, so it will always use fallback */
331 *status
= U_USING_FALLBACK_WARNING
;
332 r
->fBogus
= U_USING_FALLBACK_WARNING
;
333 } else { /* if we have a regular entry */
334 /* We might be able to do this a wee bit more efficiently (we could check whether the aliased data) */
335 /* is already in the cache), but it's good the way it is */
336 /* handle the alias by trying to get out the %%Alias tag.*/
337 /* We'll try to get alias string from the bundle */
338 Resource aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
339 if (aliasres
!= RES_BOGUS
) {
340 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
341 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
342 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
344 res_unload(&(r
->fData
));
345 result
= res_load(&(r
->fData
), r
->fPath
, aliasName
, status
);
346 if (result
== FALSE
|| U_FAILURE(*status
)) {
347 /* we couldn't load aliased data - so we have no data */
348 *status
= U_USING_FALLBACK_WARNING
;
349 r
->fBogus
= U_USING_FALLBACK_WARNING
;
351 setEntryName(r
, aliasName
, status
);
357 UResourceDataEntry
*oldR
= NULL
;
358 if((oldR
= (UResourceDataEntry
*)uhash_get(cache
, r
)) == NULL
) { /* if the data is not cached */
359 /* just insert it in the cache */
360 uhash_put(cache
, (void *)r
, r
, status
);
362 /* somebody have already inserted it while we were working, discard newly opened data */
363 /* Also, we could get here IF we opened an alias */
365 if(r
->fPath
!= NULL
) {
368 res_unload(&(r
->fData
));
380 /* CAUTION: resbMutex must be locked when calling this function! */
381 static UResourceDataEntry
*findFirstExisting(const char* path
, char* name
, UBool
*isRoot
, UBool
*hasChopped
, UBool
*isDefault
, UErrorCode
* status
) {
382 UResourceDataEntry
*r
= NULL
;
383 UBool hasRealData
= FALSE
;
384 const char *defaultLoc
= uloc_getDefault();
385 UErrorCode intStatus
= U_ZERO_ERROR
;
386 *hasChopped
= TRUE
; /* we're starting with a fresh name */
388 while(*hasChopped
&& !hasRealData
) {
389 r
= init_entry(name
, path
, &intStatus
);
390 *isDefault
= (UBool
)(uprv_strncmp(name
, defaultLoc
, uprv_strlen(name
)) == 0);
391 hasRealData
= (UBool
)(r
->fBogus
== U_ZERO_ERROR
);
393 /* this entry is not real. We will discard it. */
394 /* However, the parent line for this entry is */
395 /* not to be used - as there might be parent */
396 /* lines in cache from previous openings that */
397 /* are not updated yet. */
399 /*entryCloseInt(r);*/
401 *status
= U_USING_FALLBACK_WARNING
;
403 uprv_strcpy(name
, r
->fName
); /* this is needed for supporting aliases */
406 *isRoot
= (UBool
)(uprv_strcmp(name
, kRootLocaleName
) == 0);
408 /*Fallback data stuff*/
409 *hasChopped
= chopLocale(name
);
414 static void ures_setIsStackObject( UResourceBundle
* resB
, UBool state
) {
419 resB
->fMagic1
= MAGIC1
;
420 resB
->fMagic2
= MAGIC2
;
424 static UBool
ures_isStackObject(const UResourceBundle
* resB
) {
425 return((resB
->fMagic1
== MAGIC1
&& resB
->fMagic2
== MAGIC2
)?FALSE
:TRUE
);
429 U_CFUNC
void ures_initStackObject(UResourceBundle
* resB
) {
430 uprv_memset(resB
, 0, sizeof(UResourceBundle
));
431 ures_setIsStackObject(resB
, TRUE
);
434 static UResourceDataEntry
*entryOpen(const char* path
, const char* localeID
, UErrorCode
* status
) {
435 UErrorCode intStatus
= U_ZERO_ERROR
;
436 UErrorCode parentStatus
= U_ZERO_ERROR
;
437 UResourceDataEntry
*r
= NULL
;
438 UResourceDataEntry
*t1
= NULL
;
439 UResourceDataEntry
*t2
= NULL
;
440 UBool isDefault
= FALSE
;
441 UBool isRoot
= FALSE
;
442 UBool hasRealData
= FALSE
;
443 UBool hasChopped
= TRUE
;
446 if(U_FAILURE(*status
)) {
452 uprv_strcpy(name
, localeID
);
454 umtx_lock(&resbMutex
);
456 /* We're going to skip all the locales that do not have any data */
457 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
459 if(r
!= NULL
) { /* if there is one real locale, we can look for parents. */
462 while (hasChopped
&& !isRoot
&& t1
->fParent
== NULL
&& !t1
->fData
.noFallback
) {
463 /* insert regular parents */
464 t2
= init_entry(name
, r
->fPath
, &parentStatus
);
467 hasChopped
= chopLocale(name
);
471 /* we could have reached this point without having any real data */
472 /* if that is the case, we need to chain in the default locale */
473 if(r
==NULL
&& !isDefault
&& !isRoot
/*&& t1->fParent == NULL*/) {
474 /* insert default locale */
475 uprv_strcpy(name
, uloc_getDefault());
476 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
477 intStatus
= U_USING_DEFAULT_WARNING
;
478 if(r
!= NULL
) { /* the default locale exists */
482 while (hasChopped
&& t1
->fParent
== NULL
) {
483 /* insert chopped defaults */
484 t2
= init_entry(name
, r
->fPath
, &parentStatus
);
487 hasChopped
= chopLocale(name
);
492 /* we could still have r == NULL at this point - maybe even default locale is not */
495 uprv_strcpy(name
, kRootLocaleName
);
496 r
= findFirstExisting(path
, name
, &isRoot
, &hasChopped
, &isDefault
, &intStatus
);
499 intStatus
= U_USING_DEFAULT_WARNING
;
501 } else { /* we don't even have the root locale */
502 *status
= U_MISSING_RESOURCE_ERROR
;
504 } else if(!isRoot
&& uprv_strcmp(t1
->fName
, kRootLocaleName
) != 0 && t1
->fParent
== NULL
&& !r
->fData
.noFallback
) {
505 /* insert root locale */
506 t2
= init_entry(kRootLocaleName
, r
->fPath
, &parentStatus
);
508 r
->fBogus
= U_USING_DEFAULT_WARNING
;
510 hasRealData
= (UBool
)((t2
->fBogus
== U_ZERO_ERROR
) | hasRealData
);
515 while(r
!= NULL
&& !isRoot
&& t1
->fParent
!= NULL
) {
516 t1
->fParent
->fCountExisting
++;
518 hasRealData
= (UBool
)((t1
->fBogus
== U_ZERO_ERROR
) | hasRealData
);
521 umtx_unlock(&resbMutex
);
523 if(U_SUCCESS(*status
)) {
524 if(U_SUCCESS(parentStatus
)) {
525 if(intStatus
!= U_ZERO_ERROR
) {
530 *status
= parentStatus
;
540 * Functions to create and destroy resource bundles.
541 * CAUTION: resbMutex must be locked when calling this function.
544 static void entryCloseInt(UResourceDataEntry
*resB
) {
545 UResourceDataEntry
*p
= resB
;
547 while(resB
!= NULL
) {
549 resB
->fCountExisting
--;
551 /* Entries are left in the cache. TODO: add ures_cacheFlush() to force a flush
554 if(resB->fCountExisting <= 0) {
555 uhash_remove(cache, resB);
556 if(resB->fBogus == U_ZERO_ERROR) {
557 res_unload(&(resB->fData));
559 if(resB->fName != NULL) {
560 uprv_free(resB->fName);
562 if(resB->fPath != NULL) {
563 uprv_free(resB->fPath);
574 * API: closes a resource bundle and cleans up.
577 static void entryClose(UResourceDataEntry
*resB
) {
578 umtx_lock(&resbMutex
);
580 umtx_unlock(&resbMutex
);
584 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
585 if(resB->fResPath == NULL) {
586 resB->fResPath = resB->fResBuf;
587 *(resB->fResPath) = 0;
589 resB->fResPathLen = uprv_strlen(toAdd);
590 if(RES_BUFSIZE <= resB->fResPathLen+1) {
591 if(resB->fResPath == resB->fResBuf) {
592 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
594 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
597 uprv_strcpy(resB->fResPath, toAdd);
600 static void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
) {
601 int32_t resPathLenOrig
= resB
->fResPathLen
;
602 if(resB
->fResPath
== NULL
) {
603 resB
->fResPath
= resB
->fResBuf
;
604 *(resB
->fResPath
) = 0;
605 resB
->fResPathLen
= 0;
607 resB
->fResPathLen
+= lenToAdd
;
608 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
609 if(resB
->fResPath
== resB
->fResBuf
) {
610 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
611 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
613 resB
->fResPath
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
616 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
619 static void ures_freeResPath(UResourceBundle
*resB
) {
620 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
621 uprv_free(resB
->fResPath
);
623 resB
->fResPath
= NULL
;
624 resB
->fResPathLen
= 0;
628 ures_closeBundle(UResourceBundle
* resB
, UBool freeBundleObj
)
631 if(resB
->fData
!= NULL
) {
632 entryClose(resB
->fData
);
634 if(resB
->fVersion
!= NULL
) {
635 uprv_free(resB
->fVersion
);
637 ures_freeResPath(resB
);
639 if(ures_isStackObject(resB
) == FALSE
&& freeBundleObj
) {
644 /* poison the data */
645 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
651 U_CAPI
void U_EXPORT2
652 ures_close(UResourceBundle
* resB
)
654 ures_closeBundle(resB
, TRUE
);
657 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
658 const char *key
, int32_t index
, UResourceDataEntry
*realData
,
659 const UResourceBundle
*parent
, int32_t noAlias
,
660 UResourceBundle
*resB
, UErrorCode
*status
)
662 if(status
== NULL
|| U_FAILURE(*status
)) {
665 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
666 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
668 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
670 /* we have an alias, now let's cut it up */
671 char stackAlias
[200];
672 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
676 * Allocate enough space for both the char * version
677 * of the alias and parent->fResPath.
679 * We do this so that res_findResource() can modify the path,
680 * which allows us to remove redundant _res_findResource() variants
682 * res_findResource() now NUL-terminates each segment so that table keys
683 * can always be compared with strcmp() instead of strncmp().
684 * Saves code there and simplifies testing and code coverage.
688 ++len
; /* count the terminating NUL */
689 if(parent
!= NULL
&& parent
->fResPath
!= NULL
) {
690 capacity
= (int32_t)uprv_strlen(parent
->fResPath
) + 1;
697 if(capacity
<= sizeof(stackAlias
)) {
698 capacity
= sizeof(stackAlias
);
699 chAlias
= stackAlias
;
701 chAlias
= (char *)uprv_malloc(capacity
);
703 if(chAlias
== NULL
) {
704 *status
= U_MEMORY_ALLOCATION_ERROR
;
708 u_UCharsToChars(alias
, chAlias
, len
);
710 if(*chAlias
== RES_PATH_SEPARATOR
) {
711 /* there is a path included */
712 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
714 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
720 if(uprv_strcmp(path
, "LOCALE") == 0) {
721 /* this is an XPath alias, starting with "/LOCALE/" */
722 /* it contains the path to a resource which should be looked up */
723 /* starting in parent */
725 locale
= parent
->fData
->fName
; /* this is the parent's name */
726 path
= realData
->fPath
; /* we will be looking in the same package */
728 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
731 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
738 /* no path, start with a locale */
740 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
745 path
= realData
->fPath
;
750 /* got almost everything, let's try to open */
751 /* first, open the bundle with real data */
752 UResourceBundle
*result
= resB
;
753 const char* temp
= NULL
;
754 UErrorCode intStatus
= U_ZERO_ERROR
;
755 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
756 if(U_SUCCESS(intStatus
)) {
757 if(keyPath
== NULL
) {
758 /* no key path. This means that we are going to
759 * to use the corresponding resource from
762 /* first, we are going to get a corresponding parent
763 * resource to the one we are searching.
765 char *aKey
= parent
->fResPath
;
767 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
769 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
774 /* we need to make keyPath from parent's fResPath and
775 * current key, if there is a key associated
777 len
= (int32_t)(uprv_strlen(key
) + 1);
780 if(chAlias
== stackAlias
) {
781 chAlias
= (char *)uprv_malloc(capacity
);
783 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
785 if(chAlias
== NULL
) {
787 *status
= U_MEMORY_ALLOCATION_ERROR
;
791 uprv_memcpy(chAlias
, key
, len
);
793 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
794 } else if(index
!= -1) {
795 /* if there is no key, but there is an index, try to get by the index */
796 /* here we have either a table or an array, so get the element */
797 if(RES_GET_TYPE(r
) == URES_TABLE
|| RES_GET_TYPE(r
) == URES_TABLE32
) {
798 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, index
, (const char **)&aKey
);
800 r
= res_getArrayItem(&(mainRes
->fResData
), r
, index
);
804 result
= init_resb_result(&(mainRes
->fResData
), r
, temp
, -1, mainRes
->fData
, mainRes
, noAlias
+1, resB
, status
);
806 *status
= U_MISSING_RESOURCE_ERROR
;
810 /* this one is a bit trickier.
811 * we start finding keys, but after we resolve one alias, the path might continue.
813 * aliastest:alias { "testtypes/anotheralias/Sequence" }
814 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
815 * aliastest resource should finally have the sequence, not collation elements.
817 UResourceDataEntry
*dataEntry
= mainRes
->fData
;
818 char stackPath
[URES_MAX_BUFFER_SIZE
];
819 char *pathBuf
= stackPath
, *myPath
= pathBuf
;
820 if(uprv_strlen(keyPath
) > URES_MAX_BUFFER_SIZE
) {
821 pathBuf
= (char *)uprv_malloc((uprv_strlen(keyPath
)+1)*sizeof(char));
822 if(pathBuf
== NULL
) {
823 *status
= U_MEMORY_ALLOCATION_ERROR
;
827 uprv_strcpy(pathBuf
, keyPath
);
829 /* now we have fallback following here */
831 r
= dataEntry
->fData
.rootRes
;
832 /* this loop handles 'found' resources over several levels */
833 while(*myPath
&& U_SUCCESS(*status
)) {
834 r
= res_findResource(&(dataEntry
->fData
), r
, &myPath
, &temp
);
835 if(r
!= RES_BOGUS
) { /* found a resource, but it might be an indirection */
836 resB
= init_resb_result(&(dataEntry
->fData
), r
, temp
, -1, dataEntry
, result
, noAlias
+1, resB
, status
);
839 r
= result
->fRes
; /* switch to a new resource, possibly a new tree */
840 dataEntry
= result
->fData
;
842 } else { /* no resource found, we don't really want to look anymore on this level */
846 dataEntry
= dataEntry
->fParent
;
847 uprv_strcpy(pathBuf
, keyPath
);
849 } while(r
== RES_BOGUS
&& dataEntry
!= NULL
);
851 *status
= U_MISSING_RESOURCE_ERROR
;
854 if(pathBuf
!= stackPath
) {
858 } else { /* we failed to open the resource we're aliasing to */
861 if(chAlias
!= stackAlias
) {
864 if(mainRes
!= result
) {
870 /* bad alias, should be an error */
871 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
875 *status
= U_TOO_MANY_ALIASES_ERROR
;
880 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
883 *status
= U_MEMORY_ALLOCATION_ERROR
;
886 ures_setIsStackObject(resB
, FALSE
);
887 resB
->fResPath
= NULL
;
888 resB
->fResPathLen
= 0;
890 if(resB
->fData
!= NULL
) {
891 entryClose(resB
->fData
);
893 if(resB
->fVersion
!= NULL
) {
894 uprv_free(resB
->fVersion
);
897 weiv: if stack object was passed in, it doesn't really need to be reinited,
898 since the purpose of initing is to remove stack junk. However, at this point
899 we would not do anything to an allocated object, so stack object should be
903 if(ures_isStackObject(resB) != FALSE) {
904 ures_initStackObject(resB);
908 ures_freeResPath(resB
);
911 resB
->fData
= realData
;
912 entryIncrease(resB
->fData
);
913 resB
->fHasFallback
= FALSE
;
914 resB
->fIsTopLevel
= FALSE
;
917 resB
->fParentRes
= parent
;
918 resB
->fTopLevelData
= parent
->fTopLevelData
;
919 if(parent
->fResPath
&& parent
!= resB
) {
920 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
);
923 ures_appendResPath(resB
, key
, (int32_t)uprv_strlen(key
));
924 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
925 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1);
927 } else if(index
>= 0) {
929 int32_t len
= T_CString_integerToString(buf
, index
, 10);
930 ures_appendResPath(resB
, buf
, len
);
931 if(resB
->fResPath
[resB
->fResPathLen
-1] != RES_PATH_SEPARATOR
) {
932 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1);
935 /* Make sure that Purify doesn't complain about uninitialized memory copies. */
937 int32_t usedLen
= ((resB
->fResBuf
== resB
->fResPath
) ? resB
->fResPathLen
: 0);
938 uprv_memset(resB
->fResBuf
+ usedLen
, 0, sizeof(resB
->fResBuf
) - usedLen
);
941 resB
->fVersion
= NULL
;
943 /*resB->fParent = parent->fRes;*/
944 uprv_memcpy(&resB
->fResData
, rdata
, sizeof(ResourceData
));
945 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
949 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
951 if(U_FAILURE(*status
) || r
== original
) {
954 if(original
!= NULL
) {
956 isStackObject
= FALSE
;
957 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
960 *status
= U_MEMORY_ALLOCATION_ERROR
;
964 isStackObject
= ures_isStackObject(r
);
965 ures_closeBundle(r
, FALSE
);
967 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
970 if(original
->fResPath
) {
971 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
);
973 ures_setIsStackObject(r
, isStackObject
);
974 if(r
->fData
!= NULL
) {
975 entryIncrease(r
->fData
);
982 * Functions to retrieve data from resource bundles.
985 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
987 if (status
==NULL
|| U_FAILURE(*status
)) {
991 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
995 switch(RES_GET_TYPE(resB
->fRes
)) {
997 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
999 case URES_INT_VECTOR
:
1005 *status
= U_RESOURCE_TYPE_MISMATCH
;
1012 ures_toUTF8String(const UChar
*s16
, int32_t length16
,
1013 char *dest
, int32_t *pLength
,
1015 UErrorCode
*status
) {
1018 if (U_FAILURE(*status
)) {
1021 if (pLength
!= NULL
) {
1022 capacity
= *pLength
;
1026 if (capacity
< 0 || (capacity
> 0 && dest
== NULL
)) {
1027 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1031 if (length16
== 0) {
1032 /* empty string, return as read-only pointer */
1033 if (pLength
!= NULL
) {
1037 u_terminateChars(dest
, capacity
, 0, status
);
1043 /* We need to transform the string to the destination buffer. */
1044 if (capacity
< length16
) {
1045 /* No chance for the string to fit. Pure preflighting. */
1046 return u_strToUTF8(NULL
, 0, pLength
, s16
, length16
, status
);
1048 if (!forceCopy
&& (length16
<= 0x2aaaaaaa)) {
1050 * We know the string will fit into dest because each UChar turns
1051 * into at most three UTF-8 bytes. Fill the latter part of dest
1052 * so that callers do not expect to use dest as a string pointer,
1053 * hopefully leading to more robust code for when resource bundles
1054 * may store UTF-8 natively.
1055 * (In which case dest would not be used at all.)
1057 * We do not do this if forceCopy=TRUE because then the caller
1058 * expects the string to start exactly at dest.
1060 * The test above for <= 0x2aaaaaaa prevents overflows.
1061 * The +1 is for the NUL terminator.
1063 int32_t maxLength
= 3 * length16
+ 1;
1064 if (capacity
> maxLength
) {
1065 dest
+= capacity
- maxLength
;
1066 capacity
= maxLength
;
1069 return u_strToUTF8(dest
, capacity
, pLength
, s16
, length16
, status
);
1073 U_DRAFT
const char * U_EXPORT2
1074 ures_getUTF8String(const UResourceBundle
*resB
,
1075 char *dest
, int32_t *pLength
,
1077 UErrorCode
*status
) {
1079 const UChar
*s16
= ures_getString(resB
, &length16
, status
);
1080 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1083 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
1084 UErrorCode
* status
) {
1085 if (status
==NULL
|| U_FAILURE(*status
)) {
1089 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1092 switch(RES_GET_TYPE(resB
->fRes
)) {
1094 return res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
1097 case URES_INT_VECTOR
:
1102 *status
= U_RESOURCE_TYPE_MISMATCH
;
1108 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
1109 UErrorCode
* status
) {
1110 if (status
==NULL
|| U_FAILURE(*status
)) {
1114 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1117 switch(RES_GET_TYPE(resB
->fRes
)) {
1118 case URES_INT_VECTOR
:
1119 return res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
1127 *status
= U_RESOURCE_TYPE_MISMATCH
;
1133 /* this function returns a signed integer */
1134 /* it performs sign extension */
1135 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1136 if (status
==NULL
|| U_FAILURE(*status
)) {
1140 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1143 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1144 *status
= U_RESOURCE_TYPE_MISMATCH
;
1147 return RES_GET_INT(resB
->fRes
);
1150 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
1151 if (status
==NULL
|| U_FAILURE(*status
)) {
1155 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1158 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
1159 *status
= U_RESOURCE_TYPE_MISMATCH
;
1162 return RES_GET_UINT(resB
->fRes
);
1166 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
1172 type
= (UResType
) RES_GET_TYPE(resB
->fRes
);
1173 return type
== URES_TABLE32
? URES_TABLE
: type
;
1176 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
1184 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
1192 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1193 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1194 const UChar
* result
= 0;
1195 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1196 result
= ures_getString(tempRes
, len
, status
);
1197 ures_close(tempRes
);
1200 return res_getString(&(resB
->fResData
), r
, len
);
1204 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1211 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1215 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1218 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1219 Resource r
= RES_BOGUS
;
1221 if (status
==NULL
|| U_FAILURE(*status
)) {
1225 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1229 if(resB
->fIndex
== resB
->fSize
-1) {
1230 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1233 switch(RES_GET_TYPE(resB
->fRes
)) {
1237 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1240 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1241 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1242 /* TODO: do the fallback */
1244 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1246 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1247 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1248 /* TODO: do the fallback */
1250 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1252 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1253 case URES_INT_VECTOR
:
1262 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1263 const char *key
= NULL
;
1264 Resource r
= RES_BOGUS
;
1266 if (status
==NULL
|| U_FAILURE(*status
)) {
1271 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1276 if(resB
->fIndex
== resB
->fSize
-1) {
1277 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1281 switch(RES_GET_TYPE(resB
->fRes
)) {
1285 return ures_copyResb(fillIn
, resB
, status
);
1288 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1289 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1290 /* TODO: do the fallback */
1292 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1294 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1295 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1296 /* TODO: do the fallback */
1298 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1299 case URES_INT_VECTOR
:
1309 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1310 const char* key
= NULL
;
1311 Resource r
= RES_BOGUS
;
1313 if (status
==NULL
|| U_FAILURE(*status
)) {
1318 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1323 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1324 switch(RES_GET_TYPE(resB
->fRes
)) {
1328 return ures_copyResb(fillIn
, resB
, status
);
1331 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1332 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1333 /* TODO: do the fallback */
1335 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1337 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1338 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1339 /* TODO: do the fallback */
1341 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1342 case URES_INT_VECTOR
:
1348 *status
= U_MISSING_RESOURCE_ERROR
;
1354 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1355 const char* key
= NULL
;
1356 Resource r
= RES_BOGUS
;
1358 if (status
==NULL
|| U_FAILURE(*status
)) {
1362 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1366 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1367 switch(RES_GET_TYPE(resB
->fRes
)) {
1371 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1374 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1375 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1376 /* TODO: do the fallback */
1378 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1380 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1381 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1382 /* TODO: do the fallback */
1384 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1386 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1388 /*case URES_INT_VECTOR:*/
1393 *status
= U_MISSING_RESOURCE_ERROR
;
1398 U_DRAFT
const char * U_EXPORT2
1399 ures_getUTF8StringByIndex(const UResourceBundle
*resB
,
1401 char *dest
, int32_t *pLength
,
1403 UErrorCode
*status
) {
1405 const UChar
*s16
= ures_getStringByIndex(resB
, index
, &length16
, status
);
1406 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1409 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1410 return resB->fResPath;
1413 U_CAPI UResourceBundle
* U_EXPORT2
1414 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1416 UResourceBundle
*first
= NULL
;
1417 UResourceBundle
*result
= fillIn
;
1418 char *packageName
= NULL
;
1419 char *pathToResource
= NULL
, *save
= NULL
;
1420 char *locale
= NULL
, *localeEnd
= NULL
;
1423 if(status
== NULL
|| U_FAILURE(*status
)) {
1427 length
= (int32_t)(uprv_strlen(path
)+1);
1428 save
= pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1430 if(pathToResource
== NULL
) {
1431 *status
= U_MEMORY_ALLOCATION_ERROR
;
1434 uprv_memcpy(pathToResource
, path
, length
);
1436 locale
= pathToResource
;
1437 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1439 packageName
= pathToResource
;
1440 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1441 if(pathToResource
== NULL
) {
1442 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1444 *pathToResource
= 0;
1445 locale
= pathToResource
+1;
1449 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1450 if(localeEnd
!= NULL
) {
1454 first
= ures_open(packageName
, locale
, status
);
1456 if(U_SUCCESS(*status
)) {
1458 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1460 result
= ures_copyResb(fillIn
, first
, status
);
1468 U_CAPI UResourceBundle
* U_EXPORT2
1469 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1471 Resource res
= RES_BOGUS
;
1472 UResourceBundle
*result
= fillIn
;
1475 if(status
== NULL
|| U_FAILURE(*status
)) {
1479 /* here we do looping and circular alias checking */
1480 /* this loop is here because aliasing is resolved on this level, not on res level */
1481 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1483 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1484 if(res
!= RES_BOGUS
) {
1485 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1488 *status
= U_MISSING_RESOURCE_ERROR
;
1491 } while(*path
); /* there is more stuff in the path */
1495 U_INTERNAL
const UChar
* U_EXPORT2
1496 ures_getStringByKeyWithFallback(const UResourceBundle
*resB
,
1499 UErrorCode
*status
) {
1501 UResourceBundle stack
;
1502 const UChar
* retVal
= NULL
;
1503 ures_initStackObject(&stack
);
1504 ures_getByKeyWithFallback(resB
, inKey
, &stack
, status
);
1505 retVal
= ures_getString(&stack
, len
, status
);
1510 U_CAPI UResourceBundle
* U_EXPORT2
1511 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1513 UResourceBundle
*fillIn
,
1514 UErrorCode
*status
) {
1515 Resource res
= RES_BOGUS
, rootRes
= RES_BOGUS
;
1516 /*UResourceDataEntry *realData = NULL;*/
1517 const char *key
= inKey
;
1518 UResourceBundle
*helper
= NULL
;
1520 if (status
==NULL
|| U_FAILURE(*status
)) {
1524 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1528 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1530 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1531 if(res
== RES_BOGUS
) {
1532 UResourceDataEntry
*dataEntry
= resB
->fData
;
1534 char* myPath
= path
;
1535 const char* resPath
= resB
->fResPath
;
1536 int32_t len
= resB
->fResPathLen
;
1538 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1539 dataEntry
= dataEntry
->fParent
;
1540 rootRes
= dataEntry
->fData
.rootRes
;
1541 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1542 uprv_strncpy(path
, resPath
, len
);
1543 uprv_strcpy(path
+len
, inKey
);
1547 res
= res_findResource(&(dataEntry
->fData
), rootRes
, &myPath
, &key
);
1548 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1549 /* We hit an alias, but we didn't finish following the path. */
1550 helper
= init_resb_result(&(dataEntry
->fData
), res
, NULL
, -1, dataEntry
, resB
, 0, helper
, status
);
1551 /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1553 dataEntry
= helper
->fData
;
1554 rootRes
= helper
->fRes
;
1555 resPath
= helper
->fResPath
;
1556 len
= helper
->fResPathLen
;
1562 } while(*myPath
); /* Continue until the whole path is consumed */
1565 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1566 if(res
!= RES_BOGUS
) {
1567 /* check if resB->fResPath gives the right name here */
1568 if(uprv_strcmp(dataEntry
->fName
, uloc_getDefault())==0 || uprv_strcmp(dataEntry
->fName
, kRootLocaleName
)==0) {
1569 *status
= U_USING_DEFAULT_WARNING
;
1571 *status
= U_USING_FALLBACK_WARNING
;
1574 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1576 *status
= U_MISSING_RESOURCE_ERROR
;
1579 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1583 *status
= U_RESOURCE_TYPE_MISMATCH
;
1590 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1591 Resource res
= RES_BOGUS
;
1592 UResourceDataEntry
*realData
= NULL
;
1593 const char *key
= inKey
;
1595 if (status
==NULL
|| U_FAILURE(*status
)) {
1599 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1603 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1605 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1606 if(res
== RES_BOGUS
) {
1608 if(resB
->fHasFallback
== TRUE
) {
1609 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1610 if(U_SUCCESS(*status
)) {
1611 /* check if resB->fResPath gives the right name here */
1612 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1614 *status
= U_MISSING_RESOURCE_ERROR
;
1617 *status
= U_MISSING_RESOURCE_ERROR
;
1620 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1624 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1626 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1627 /* here should go a first attempt to locate the key using index table */
1628 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1629 if(U_SUCCESS(*status
)) {
1630 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1632 *status
= U_MISSING_RESOURCE_ERROR
;
1637 *status
= U_RESOURCE_TYPE_MISMATCH
;
1642 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1643 Resource res
= RES_BOGUS
;
1644 UResourceDataEntry
*realData
= NULL
;
1645 const char* key
= inKey
;
1647 if (status
==NULL
|| U_FAILURE(*status
)) {
1651 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1655 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1658 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1660 if(res
== RES_BOGUS
) {
1662 if(resB
->fHasFallback
== TRUE
) {
1663 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1664 if(U_SUCCESS(*status
)) {
1665 switch (RES_GET_TYPE(res
)) {
1670 return res_getString(rd
, res
, len
);
1673 const UChar
* result
= 0;
1674 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1675 result
= ures_getString(tempRes
, len
, status
);
1676 ures_close(tempRes
);
1680 *status
= U_RESOURCE_TYPE_MISMATCH
;
1683 *status
= U_MISSING_RESOURCE_ERROR
;
1686 *status
= U_MISSING_RESOURCE_ERROR
;
1689 switch (RES_GET_TYPE(res
)) {
1694 return res_getString(&(resB
->fResData
), res
, len
);
1697 const UChar
* result
= 0;
1698 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1699 result
= ures_getString(tempRes
, len
, status
);
1700 ures_close(tempRes
);
1704 *status
= U_RESOURCE_TYPE_MISMATCH
;
1709 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1711 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1712 /* here should go a first attempt to locate the key using index table */
1713 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1714 if(U_SUCCESS(*status
)) {
1715 return res_getString(rd
, res
, len
);
1717 *status
= U_MISSING_RESOURCE_ERROR
;
1722 *status
= U_RESOURCE_TYPE_MISMATCH
;
1727 U_DRAFT
const char * U_EXPORT2
1728 ures_getUTF8StringByKey(const UResourceBundle
*resB
,
1730 char *dest
, int32_t *pLength
,
1732 UErrorCode
*status
) {
1734 const UChar
*s16
= ures_getStringByKey(resB
, key
, &length16
, status
);
1735 return ures_toUTF8String(s16
, length16
, dest
, pLength
, forceCopy
, status
);
1738 /* TODO: clean from here down */
1741 * INTERNAL: Get the name of the first real locale (not placeholder)
1742 * that has resource bundle data.
1744 U_CAPI
const char* U_EXPORT2
1745 ures_getLocale(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1747 if (status
==NULL
|| U_FAILURE(*status
)) {
1750 if (!resourceBundle
) {
1751 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1754 return resourceBundle
->fData
->fName
;
1758 U_CAPI
const char* U_EXPORT2
1759 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1760 ULocDataLocaleType type
,
1761 UErrorCode
* status
) {
1762 if (status
==NULL
|| U_FAILURE(*status
)) {
1765 if (!resourceBundle
) {
1766 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1770 case ULOC_ACTUAL_LOCALE
:
1771 return resourceBundle
->fData
->fName
;
1772 case ULOC_VALID_LOCALE
:
1773 return resourceBundle
->fTopLevelData
->fName
;
1774 case ULOC_REQUESTED_LOCALE
:
1777 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1783 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
1788 return resB
->fData
->fName
;
1791 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
1796 return resB
->fData
->fPath
;
1799 /* OLD API implementation */
1802 * API: This function is used to open a resource bundle
1803 * proper fallback chaining is executed while initialization.
1804 * The result is stored in cache for later fallback search.
1806 U_CAPI
void U_EXPORT2
1807 ures_openFillIn(UResourceBundle
*r
, const char* path
,
1808 const char* localeID
, UErrorCode
* status
) {
1810 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1812 UResourceDataEntry
*firstData
;
1813 UBool isStackObject
= ures_isStackObject(r
);
1815 ures_closeBundle(r
, FALSE
);
1816 uprv_memset(r
, 0, sizeof(UResourceBundle
));
1817 ures_setIsStackObject(r
, isStackObject
);
1818 r
->fHasFallback
= TRUE
;
1819 r
->fIsTopLevel
= TRUE
;
1821 r
->fData
= entryOpen(path
, localeID
, status
);
1822 if(U_FAILURE(*status
)) {
1825 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
1826 firstData
= r
->fData
;
1827 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
1828 firstData
= firstData
->fParent
;
1830 uprv_memcpy(&r
->fResData
, &firstData
->fData
, sizeof(ResourceData
));
1831 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
1832 r
->fRes
= r
->fResData
.rootRes
;
1833 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1834 r
->fTopLevelData
= r
->fData
;
1838 U_CAPI UResourceBundle
* U_EXPORT2
1839 ures_open(const char* path
,
1840 const char* localeID
,
1843 char canonLocaleID
[100];
1844 UResourceDataEntry
*hasData
= NULL
;
1847 if(status
== NULL
|| U_FAILURE(*status
)) {
1851 /* first "canonicalize" the locale ID */
1852 uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
1853 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
1854 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1858 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1860 *status
= U_MEMORY_ALLOCATION_ERROR
;
1864 uprv_memset(r
, 0, sizeof(UResourceBundle
));
1865 r
->fHasFallback
= TRUE
;
1866 r
->fIsTopLevel
= TRUE
;
1867 ures_setIsStackObject(r
, FALSE
);
1869 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
1870 if(U_FAILURE(*status
)) {
1874 r
->fTopLevelData
= r
->fData
;
1877 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
1878 hasData
= hasData
->fParent
;
1879 if(hasData
== NULL
) {
1880 /* This can happen only if fallback chain gets broken by an act of God */
1881 /* TODO: this unlikely to happen, consider removing it */
1882 entryClose(r
->fData
);
1884 *status
= U_MISSING_RESOURCE_ERROR
;
1889 uprv_memcpy(&r
->fResData
, &hasData
->fData
, sizeof(ResourceData
));
1890 r
->fHasFallback
=(UBool
)!r
->fResData
.noFallback
;
1891 r
->fRes
= r
->fResData
.rootRes
;
1892 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1894 if(r->fData->fPath != NULL) {
1895 ures_setResPath(r, r->fData->fPath);
1896 ures_appendResPath(r, RES_PATH_PACKAGE_S);
1897 ures_appendResPath(r, r->fData->fName);
1899 ures_setResPath(r, r->fData->fName);
1908 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
1909 * or sought. However, alias substitution will happen!
1911 U_CAPI UResourceBundle
* U_EXPORT2
1912 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
1914 UErrorCode subStatus
= U_ZERO_ERROR
;
1916 if(status
== NULL
|| U_FAILURE(*status
)) {
1920 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1922 *status
= U_MEMORY_ALLOCATION_ERROR
;
1926 r
->fHasFallback
= FALSE
;
1927 r
->fIsTopLevel
= TRUE
;
1928 ures_setIsStackObject(r
, FALSE
);
1930 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
1931 if(U_FAILURE(subStatus
)) {
1932 *status
= subStatus
;
1936 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
1937 /* we didn't find one we were looking for - so openDirect */
1939 entryClose(r
->fData
);
1941 *status
= U_MISSING_RESOURCE_ERROR
;
1947 uprv_memcpy(&r
->fResData
, &r
->fData
->fData
, sizeof(ResourceData
));
1948 /* r->fHasFallback remains FALSE here in ures_openDirect() */
1949 r
->fRes
= r
->fResData
.rootRes
;
1950 /*r->fParent = RES_BOGUS;*/
1951 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1954 r
->fParentRes
= NULL
;
1955 r
->fTopLevelData
= r
->fData
;
1961 * API: Counts members. For arrays and tables, returns number of resources.
1962 * For strings, returns 1.
1964 U_CAPI
int32_t U_EXPORT2
1965 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
1966 const char* resourceKey
,
1969 UResourceBundle resData
;
1970 ures_initStackObject(&resData
);
1971 if (status
==NULL
|| U_FAILURE(*status
)) {
1974 if(resourceBundle
== NULL
) {
1975 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1978 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
1980 if(resData
.fResData
.data
!= NULL
) {
1981 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
1982 ures_close(&resData
);
1985 *status
= U_MISSING_RESOURCE_ERROR
;
1986 ures_close(&resData
);
1991 U_CAPI
const char* U_EXPORT2
1992 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
1994 if (!resourceBundle
) return NULL
;
1996 if(resourceBundle
->fVersion
== NULL
) {
1998 /* If the version ID has not been built yet, then do so. Retrieve */
1999 /* the minor version from the file. */
2000 UErrorCode status
= U_ZERO_ERROR
;
2001 int32_t minor_len
= 0;
2004 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
2006 /* Determine the length of of the final version string. This is */
2007 /* the length of the major part + the length of the separator */
2008 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2011 len
= (minor_len
> 0) ? minor_len
: 1;
2013 /* Allocate the string, and build it up. */
2014 /* + 1 for zero byte */
2017 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
2020 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
2021 resourceBundle
->fVersion
[len
] = '\0';
2024 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
2028 return resourceBundle
->fVersion
;
2031 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
2034 u_versionFromString(versionInfo
, ures_getVersionNumber(resB
));
2037 /** Tree support functions *******************************/
2038 #define INDEX_LOCALE_NAME "res_index"
2039 #define INDEX_TAG "InstalledLocales"
2040 #define DEFAULT_TAG "default"
2042 #if defined(URES_TREE_DEBUG)
2046 typedef struct ULocalesContext
{
2047 UResourceBundle installed
;
2048 UResourceBundle curr
;
2051 static void U_CALLCONV
2052 ures_loc_closeLocales(UEnumeration
*enumerator
) {
2053 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
2054 ures_close(&ctx
->curr
);
2055 ures_close(&ctx
->installed
);
2057 uprv_free(enumerator
);
2060 static int32_t U_CALLCONV
2061 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
*status
) {
2062 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2063 return ures_getSize(&ctx
->installed
);
2066 static const char* U_CALLCONV
2067 ures_loc_nextLocale(UEnumeration
* en
,
2068 int32_t* resultLength
,
2069 UErrorCode
* status
) {
2070 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
2071 UResourceBundle
*res
= &(ctx
->installed
);
2072 UResourceBundle
*k
= NULL
;
2073 const char *result
= NULL
;
2075 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
2076 result
= ures_getKey(k
);
2077 len
= (int32_t)uprv_strlen(result
);
2080 *resultLength
= len
;
2085 static void U_CALLCONV
2086 ures_loc_resetLocales(UEnumeration
* en
,
2087 UErrorCode
* status
) {
2088 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
2089 ures_resetIterator(res
);
2093 static const UEnumeration gLocalesEnum
= {
2096 ures_loc_closeLocales
,
2097 ures_loc_countLocales
,
2099 ures_loc_nextLocale
,
2100 ures_loc_resetLocales
2104 U_CAPI UEnumeration
* U_EXPORT2
2105 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
2107 UResourceBundle
*index
= NULL
;
2108 UEnumeration
*en
= NULL
;
2109 ULocalesContext
*myContext
= NULL
;
2111 if(U_FAILURE(*status
)) {
2114 myContext
= uprv_malloc(sizeof(ULocalesContext
));
2115 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
2116 if(!en
|| !myContext
) {
2117 *status
= U_MEMORY_ALLOCATION_ERROR
;
2119 uprv_free(myContext
);
2122 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
2124 ures_initStackObject(&myContext
->installed
);
2125 ures_initStackObject(&myContext
->curr
);
2126 index
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
2127 ures_getByKey(index
, INDEX_TAG
, &myContext
->installed
, status
);
2128 if(U_SUCCESS(*status
)) {
2129 #if defined(URES_TREE_DEBUG)
2130 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
2131 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
2133 en
->context
= myContext
;
2135 #if defined(URES_TREE_DEBUG)
2136 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
2138 ures_close(&myContext
->installed
);
2139 uprv_free(myContext
);
2149 U_CAPI
int32_t U_EXPORT2
2150 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
2151 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
2152 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
2154 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
2155 char defVal
[1024] = ""; /* default value for given locale */
2156 char defLoc
[1024] = ""; /* default value for given locale */
2157 char base
[1024] = ""; /* base locale */
2160 char full
[1024] = "";
2161 UResourceBundle bund1
, bund2
;
2162 UResourceBundle
*res
= NULL
;
2163 UErrorCode subStatus
= U_ZERO_ERROR
;
2165 if(U_FAILURE(*status
)) return 0;
2167 *isAvailable
= TRUE
;
2169 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2170 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2173 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2174 #if defined(URES_TREE_DEBUG)
2175 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2176 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2178 ures_initStackObject(&bund1
);
2179 ures_initStackObject(&bund2
);
2182 uprv_strcpy(parent
, base
);
2183 uprv_strcpy(found
, base
);
2185 if(U_FAILURE(subStatus
)) {
2186 *status
= subStatus
;
2191 subStatus
= U_ZERO_ERROR
;
2192 res
= ures_open(path
, parent
, &subStatus
);
2193 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2194 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
) {
2195 *isAvailable
= FALSE
;
2197 isAvailable
= NULL
; /* only want to set this the first time around */
2199 #if defined(URES_TREE_DEBUG)
2200 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2202 if(U_FAILURE(subStatus
)) {
2203 *status
= subStatus
;
2204 } else if(subStatus
== U_ZERO_ERROR
) {
2205 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2206 if(subStatus
== U_ZERO_ERROR
) {
2207 const UChar
*defUstr
;
2209 /* look for default item */
2210 #if defined(URES_TREE_DEBUG)
2211 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2212 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2214 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2215 if(U_SUCCESS(subStatus
) && defLen
) {
2216 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2217 #if defined(URES_TREE_DEBUG)
2218 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2219 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2221 uprv_strcpy(defLoc
, parent
);
2223 uprv_strcpy(kwVal
, defVal
);
2224 #if defined(URES_TREE_DEBUG)
2225 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2226 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2233 subStatus
= U_ZERO_ERROR
;
2235 uprv_strcpy(found
, parent
);
2236 uloc_getParent(found
,parent
,1023,&subStatus
);
2238 } while(!defVal
[0] && *found
&& U_SUCCESS(*status
));
2240 /* Now, see if we can find the kwVal collator.. start the search over.. */
2241 uprv_strcpy(parent
, base
);
2242 uprv_strcpy(found
, base
);
2245 subStatus
= U_ZERO_ERROR
;
2246 res
= ures_open(path
, parent
, &subStatus
);
2247 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2248 *isAvailable
= FALSE
;
2250 isAvailable
= NULL
; /* only want to set this the first time around */
2252 #if defined(URES_TREE_DEBUG)
2253 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2254 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2256 if(U_FAILURE(subStatus
)) {
2257 *status
= subStatus
;
2258 } else if(subStatus
== U_ZERO_ERROR
) {
2259 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2260 #if defined(URES_TREE_DEBUG)
2261 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2263 if(subStatus
== U_ZERO_ERROR
) {
2264 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2265 #if defined(URES_TREE_DEBUG)
2266 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2268 if(subStatus
== U_ZERO_ERROR
) {
2269 #if defined(URES_TREE_DEBUG)
2270 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2271 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2273 uprv_strcpy(full
, parent
);
2275 uprv_strcpy(full
, "root");
2277 /* now, recalculate default kw if need be */
2278 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2279 const UChar
*defUstr
;
2281 /* look for default item */
2282 #if defined(URES_TREE_DEBUG)
2283 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2284 path
?path
:"ICUDATA", full
);
2286 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2287 if(U_SUCCESS(subStatus
) && defLen
) {
2288 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2289 #if defined(URES_TREE_DEBUG)
2290 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2291 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2293 uprv_strcpy(defLoc
, full
);
2295 } /* end of recalculate default KW */
2296 #if defined(URES_TREE_DEBUG)
2298 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2302 #if defined(URES_TREE_DEBUG)
2303 fprintf(stderr
, "err=%s in %s looking for %s\n",
2304 u_errorName(subStatus
), parent
, kwVal
);
2310 subStatus
= U_ZERO_ERROR
;
2312 uprv_strcpy(found
, parent
);
2313 uloc_getParent(found
,parent
,1023,&subStatus
);
2315 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2317 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2318 #if defined(URES_TREE_DEBUG)
2319 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2321 uprv_strcpy(kwVal
, defVal
);
2322 uprv_strcpy(parent
, base
);
2323 uprv_strcpy(found
, base
);
2325 do { /* search for 'default' named item */
2326 subStatus
= U_ZERO_ERROR
;
2327 res
= ures_open(path
, parent
, &subStatus
);
2328 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2329 *isAvailable
= FALSE
;
2331 isAvailable
= NULL
; /* only want to set this the first time around */
2333 #if defined(URES_TREE_DEBUG)
2334 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2335 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2337 if(U_FAILURE(subStatus
)) {
2338 *status
= subStatus
;
2339 } else if(subStatus
== U_ZERO_ERROR
) {
2340 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2341 if(subStatus
== U_ZERO_ERROR
) {
2342 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2343 if(subStatus
== U_ZERO_ERROR
) {
2344 #if defined(URES_TREE_DEBUG)
2345 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2346 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2348 uprv_strcpy(full
, parent
);
2350 uprv_strcpy(full
, "root");
2353 /* now, recalculate default kw if need be */
2354 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2355 const UChar
*defUstr
;
2357 /* look for default item */
2358 #if defined(URES_TREE_DEBUG)
2359 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2360 path
?path
:"ICUDATA", full
);
2362 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2363 if(U_SUCCESS(subStatus
) && defLen
) {
2364 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2365 #if defined(URES_TREE_DEBUG)
2366 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2367 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2369 uprv_strcpy(defLoc
, full
);
2371 } /* end of recalculate default KW */
2372 #if defined(URES_TREE_DEBUG)
2374 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
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
));
2388 if(U_SUCCESS(*status
)) {
2390 #if defined(URES_TREE_DEBUG)
2391 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2393 *status
= U_MISSING_RESOURCE_ERROR
;
2394 } else if(omitDefault
) {
2395 #if defined(URES_TREE_DEBUG)
2396 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2398 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2399 /* found the keyword in a *child* of where the default tag was present. */
2400 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2401 /* and the default is in or in an ancestor of the current locale */
2402 #if defined(URES_TREE_DEBUG)
2403 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2409 uprv_strcpy(found
, full
);
2411 uprv_strcat(found
, "@");
2412 uprv_strcat(found
, keyword
);
2413 uprv_strcat(found
, "=");
2414 uprv_strcat(found
, kwVal
);
2415 } else if(!omitDefault
) {
2416 uprv_strcat(found
, "@");
2417 uprv_strcat(found
, keyword
);
2418 uprv_strcat(found
, "=");
2419 uprv_strcat(found
, defVal
);
2422 /* we found the default locale - no need to repeat it.*/
2427 length
= (int32_t)uprv_strlen(found
);
2429 if(U_SUCCESS(*status
)) {
2430 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2432 uprv_strncpy(result
, found
, copyLength
);
2435 *status
= U_MISSING_RESOURCE_ERROR
;
2441 return u_terminateChars(result
, resultCapacity
, length
, status
);
2444 U_CAPI UEnumeration
* U_EXPORT2
2445 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2447 #define VALUES_BUF_SIZE 2048
2448 #define VALUES_LIST_SIZE 512
2450 char valuesBuf
[VALUES_BUF_SIZE
];
2451 int32_t valuesIndex
= 0;
2452 const char *valuesList
[VALUES_LIST_SIZE
];
2453 int32_t valuesCount
= 0;
2458 UEnumeration
*locs
= NULL
;
2460 UResourceBundle item
;
2461 UResourceBundle subItem
;
2463 ures_initStackObject(&item
);
2464 ures_initStackObject(&subItem
);
2465 locs
= ures_openAvailableLocales(path
, status
);
2467 if(U_FAILURE(*status
)) {
2469 ures_close(&subItem
);
2476 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2477 UResourceBundle
*bund
= NULL
;
2478 UResourceBundle
*subPtr
= NULL
;
2479 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2480 bund
= ures_openDirect(path
, locale
, &subStatus
);
2482 #if defined(URES_TREE_DEBUG)
2483 if(!bund
|| U_FAILURE(subStatus
)) {
2484 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2485 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2489 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2491 if(!bund
|| U_FAILURE(subStatus
)) {
2492 #if defined(URES_TREE_DEBUG)
2493 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2494 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2501 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2502 && U_SUCCESS(subStatus
)) {
2505 k
= ures_getKey(subPtr
);
2507 #if defined(URES_TREE_DEBUG)
2508 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2510 for(i
=0;k
&&i
<valuesCount
;i
++) {
2511 if(!uprv_strcmp(valuesList
[i
],k
)) {
2512 k
= NULL
; /* found duplicate */
2516 int32_t kLen
= (int32_t)uprv_strlen(k
);
2517 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2518 continue; /* don't need 'default'. */
2520 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2521 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2522 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2524 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2525 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2526 valuesIndex
+= kLen
;
2527 #if defined(URES_TREE_DEBUG)
2528 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2529 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2531 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2537 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2540 ures_close(&subItem
);
2542 #if defined(URES_TREE_DEBUG)
2543 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2544 valuesIndex
, valuesCount
);
2546 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);
2548 U_INTERNAL UBool U_EXPORT2
2549 ures_equal(const UResourceBundle
* res1
, const UResourceBundle
* res2
){
2550 if(res1
==NULL
|| res2
==NULL
){
2551 return res1
==res2
; /* pointer comparision */
2553 if(res1
->fKey
==NULL
|| res2
->fKey
==NULL
){
2554 return (res1
->fKey
==res2
->fKey
);
2556 if(uprv_strcmp(res1
->fKey
, res2
->fKey
)!=0){
2560 if(uprv_strcmp(res1
->fData
->fName
, res2
->fData
->fName
)!=0){
2563 if(res1
->fData
->fPath
== NULL
|| res2
->fData
->fPath
==NULL
){
2564 return (res1
->fData
->fPath
== res2
->fData
->fPath
);
2566 if(uprv_strcmp(res1
->fData
->fPath
, res2
->fData
->fPath
)!=0){
2570 if(uprv_strcmp(res1
->fData
->fParent
->fName
, res2
->fData
->fParent
->fName
)!=0){
2573 if(uprv_strcmp(res1
->fData
->fParent
->fPath
, res2
->fData
->fParent
->fPath
)!=0){
2576 if(uprv_strncmp(res1
->fResPath
, res2
->fResPath
, res1
->fResPathLen
)!=0){
2579 if(res1
->fRes
!= res2
->fRes
){
2584 U_INTERNAL UResourceBundle
* U_EXPORT2
2585 ures_clone(const UResourceBundle
* res
, UErrorCode
* status
){
2586 UResourceBundle
* bundle
= NULL
;
2587 UResourceBundle
* ret
= NULL
;
2588 if(U_FAILURE(*status
) || res
== NULL
){
2591 bundle
= ures_open(res
->fData
->fPath
, res
->fData
->fName
, status
);
2592 if(res
->fResPath
!=NULL
){
2593 ret
= ures_findSubResource(bundle
, res
->fResPath
, NULL
, status
);
2600 U_INTERNAL
const UResourceBundle
* U_EXPORT2
2601 ures_getParentBundle(const UResourceBundle
* res
){
2605 return res
->fParentRes
;