2 ******************************************************************************
3 * Copyright (C) 1997-2004, 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_EXPORT2 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_EXPORT2 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
, 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(uprv_strlen(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 r
->fData
.data
= NULL
;
324 r
->fData
.pRoot
= NULL
;
325 r
->fData
.rootRes
= 0;
326 r
->fBogus
= U_ZERO_ERROR
;
328 /* this is the acutal loading - returns bool true/false */
329 result
= res_load(&(r
->fData
), r
->fPath
, r
->fName
, status
);
331 if (result
== FALSE
|| U_FAILURE(*status
)) {
332 /* we have no such entry in dll, so it will always use fallback */
333 *status
= U_USING_FALLBACK_WARNING
;
334 r
->fBogus
= U_USING_FALLBACK_WARNING
;
335 } else { /* if we have a regular entry */
336 /* We might be able to do this a wee bit more efficiently (we could check whether the aliased data) */
337 /* is already in the cache), but it's good the way it is */
338 /* handle the alias by trying to get out the %%Alias tag.*/
339 /* We'll try to get alias string from the bundle */
340 Resource aliasres
= res_getResource(&(r
->fData
), "%%ALIAS");
341 const UChar
*alias
= res_getString(&(r
->fData
), aliasres
, &aliasLen
);
342 if(alias
!= NULL
&& aliasLen
> 0) { /* if there is actual alias - unload and load new data */
343 u_UCharsToChars(alias
, aliasName
, aliasLen
+1);
345 res_unload(&(r
->fData
));
346 result
= res_load(&(r
->fData
), r
->fPath
, aliasName
, status
);
347 if (result
== FALSE
|| U_FAILURE(*status
)) {
348 /* we couldn't load aliased data - so we have no data */
349 *status
= U_USING_FALLBACK_WARNING
;
350 r
->fBogus
= U_USING_FALLBACK_WARNING
;
352 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
) {
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
) {
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
);
583 static UResourceBundle
*init_resb_result(const ResourceData
*rdata
, Resource r
,
584 const char *key
, int32_t index
, UResourceDataEntry
*realData
,
585 const UResourceBundle
*parent
, int32_t noAlias
,
586 UResourceBundle
*resB
, UErrorCode
*status
)
588 if(status
== NULL
|| U_FAILURE(*status
)) {
591 if(RES_GET_TYPE(r
) == URES_ALIAS
) { /* This is an alias, need to exchange with real data */
592 if(noAlias
< URES_MAX_ALIAS_LEVEL
) {
594 const UChar
*alias
= res_getAlias(rdata
, r
, &len
);
596 /* we have an alias, now let's cut it up */
597 char stackAlias
[200];
598 char *chAlias
= NULL
, *path
= NULL
, *locale
= NULL
, *keyPath
= NULL
;
602 * Allocate enough space for both the char * version
603 * of the alias and parent->fResPath.
605 * We do this so that res_findResource() can modify the path,
606 * which allows us to remove redundant _res_findResource() variants
608 * res_findResource() now NUL-terminates each segment so that table keys
609 * can always be compared with strcmp() instead of strncmp().
610 * Saves code there and simplifies testing and code coverage.
614 ++len
; /* count the terminating NUL */
615 if(parent
!= NULL
&& parent
->fResPath
!= NULL
) {
616 capacity
= uprv_strlen(parent
->fResPath
) + 1;
623 if(capacity
<= sizeof(stackAlias
)) {
624 capacity
= sizeof(stackAlias
);
625 chAlias
= stackAlias
;
627 chAlias
= (char *)uprv_malloc(capacity
);
629 if(chAlias
== NULL
) {
630 *status
= U_MEMORY_ALLOCATION_ERROR
;
634 u_UCharsToChars(alias
, chAlias
, len
);
636 if(*chAlias
== RES_PATH_SEPARATOR
) {
637 /* there is a path included */
638 locale
= uprv_strchr(chAlias
+1, RES_PATH_SEPARATOR
);
640 locale
= uprv_strchr(chAlias
, 0); /* avoid locale == NULL to make code below work */
646 if(uprv_strcmp(path
, "ICUDATA") == 0) { /* want ICU data */
650 /* no path, start with a locale */
652 path
= realData
->fPath
;
654 keyPath
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
660 /* got almost everything, let's try to open */
661 /* first, open the bundle with real data */
662 UResourceBundle
*result
= resB
;
663 const char* temp
= NULL
;
664 UErrorCode intStatus
= U_ZERO_ERROR
;
665 UResourceBundle
*mainRes
= ures_openDirect(path
, locale
, &intStatus
);
666 if(U_SUCCESS(intStatus
)) {
667 if(keyPath
== NULL
) {
668 /* no key path. This means that we are going to
669 * to use the corresponding resource from
672 /* first, we are going to get a corresponding parent
673 * resource to the one we are searching.
675 char *aKey
= parent
->fResPath
;
677 uprv_strcpy(chAlias
, aKey
); /* allocated large enough above */
679 r
= res_findResource(&(mainRes
->fResData
), mainRes
->fRes
, &aKey
, &temp
);
684 /* we need to make keyPath from parent's fResPath and
685 * current key, if there is a key associated
687 len
= uprv_strlen(key
) + 1;
690 if(chAlias
== stackAlias
) {
691 chAlias
= (char *)uprv_malloc(capacity
);
693 chAlias
= (char *)uprv_realloc(chAlias
, capacity
);
695 if(chAlias
== NULL
) {
697 *status
= U_MEMORY_ALLOCATION_ERROR
;
701 uprv_memcpy(chAlias
, key
, len
);
703 r
= res_findResource(&(mainRes
->fResData
), r
, &aKey
, &temp
);
704 } else if(index
!= -1) {
705 /* if there is no key, but there is an index, try to get by the index */
706 /* here we have either a table or an array, so get the element */
707 if(RES_GET_TYPE(r
) == URES_TABLE
|| RES_GET_TYPE(r
) == URES_TABLE32
) {
708 r
= res_getTableItemByIndex(&(mainRes
->fResData
), r
, index
, (const char **)&aKey
);
710 r
= res_getArrayItem(&(mainRes
->fResData
), r
, index
);
714 result
= init_resb_result(&(mainRes
->fResData
), r
, key
, -1, mainRes
->fData
, parent
, noAlias
+1, resB
, status
);
716 *status
= U_MISSING_RESOURCE_ERROR
;
720 /* this one is a bit trickier.
721 * we start finding keys, but after we resolve one alias, the path might continue.
723 * aliastest:alias { "testtypes/anotheralias/Sequence" }
724 * anotheralias:alias { "/ICUDATA/sh/CollationElements" }
725 * aliastest resource should finally have the sequence, not collation elements.
728 while(*keyPath
&& U_SUCCESS(*status
)) {
729 r
= res_findResource(&(result
->fResData
), result
->fRes
, &keyPath
, &temp
);
731 *status
= U_MISSING_RESOURCE_ERROR
;
735 resB
= init_resb_result(&(result
->fResData
), r
, key
, -1, result
->fData
, parent
, noAlias
+1, resB
, status
);
739 } else { /* we failed to open the resource we're aliasing to */
742 if(chAlias
!= stackAlias
) {
745 if(mainRes
!= result
) {
751 /* bad alias, should be an error */
752 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
756 *status
= U_TOO_MANY_ALIASES_ERROR
;
761 resB
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
764 *status
= U_MEMORY_ALLOCATION_ERROR
;
767 ures_setIsStackObject(resB
, FALSE
);
768 resB
->fResPath
= NULL
;
769 resB
->fResPathLen
= 0;
771 if(resB
->fData
!= NULL
) {
772 entryClose(resB
->fData
);
774 if(resB
->fVersion
!= NULL
) {
775 uprv_free(resB
->fVersion
);
777 if(ures_isStackObject(resB
) != FALSE
) {
778 ures_initStackObject(resB
);
781 ures_freeResPath(resB
);
784 resB
->fData
= realData
;
785 entryIncrease(resB
->fData
);
786 resB
->fHasFallback
= FALSE
;
787 resB
->fIsTopLevel
= FALSE
;
790 resB
->fParentRes
= parent
;
791 resB
->fTopLevelData
= parent
->fTopLevelData
;
792 if(parent
->fResPath
&& parent
!= resB
) {
793 ures_appendResPath(resB
, parent
->fResPath
, parent
->fResPathLen
);
796 ures_appendResPath(resB
, key
, uprv_strlen(key
));
797 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1);
800 int32_t len
= T_CString_integerToString(buf
, index
, 10);
801 ures_appendResPath(resB
, buf
, len
);
802 ures_appendResPath(resB
, RES_PATH_SEPARATOR_S
, 1);
805 resB
->fVersion
= NULL
;
807 /*resB->fParent = parent->fRes;*/
808 resB
->fResData
.data
= rdata
->data
;
809 resB
->fResData
.pRoot
= rdata
->pRoot
;
810 resB
->fResData
.rootRes
= rdata
->rootRes
;
811 resB
->fSize
= res_countArrayItems(&(resB
->fResData
), resB
->fRes
);
815 UResourceBundle
*ures_copyResb(UResourceBundle
*r
, const UResourceBundle
*original
, UErrorCode
*status
) {
817 if(U_FAILURE(*status
) || r
== original
) {
820 if(original
!= NULL
) {
822 isStackObject
= FALSE
;
823 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
826 *status
= U_MEMORY_ALLOCATION_ERROR
;
830 isStackObject
= ures_isStackObject(r
);
831 if(U_FAILURE(*status
)) {
835 if(isStackObject
== FALSE
) {
836 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
839 *status
= U_MEMORY_ALLOCATION_ERROR
;
844 uprv_memcpy(r
, original
, sizeof(UResourceBundle
));
847 if(original
->fResPath
) {
848 ures_appendResPath(r
, original
->fResPath
, original
->fResPathLen
);
850 ures_setIsStackObject(r
, isStackObject
);
851 if(r
->fData
!= NULL
) {
852 entryIncrease(r
->fData
);
861 * Functions to retrieve data from resource bundles.
864 U_CAPI
const UChar
* U_EXPORT2
ures_getString(const UResourceBundle
* resB
, int32_t* len
, UErrorCode
* status
) {
866 if (status
==NULL
|| U_FAILURE(*status
)) {
870 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
874 switch(RES_GET_TYPE(resB
->fRes
)) {
876 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
878 case URES_INT_VECTOR
:
884 *status
= U_RESOURCE_TYPE_MISMATCH
;
890 U_CAPI
const uint8_t* U_EXPORT2
ures_getBinary(const UResourceBundle
* resB
, int32_t* len
,
891 UErrorCode
* status
) {
892 if (status
==NULL
|| U_FAILURE(*status
)) {
896 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
899 switch(RES_GET_TYPE(resB
->fRes
)) {
901 return res_getBinary(&(resB
->fResData
), resB
->fRes
, len
);
904 case URES_INT_VECTOR
:
909 *status
= U_RESOURCE_TYPE_MISMATCH
;
915 U_CAPI
const int32_t* U_EXPORT2
ures_getIntVector(const UResourceBundle
* resB
, int32_t* len
,
916 UErrorCode
* status
) {
917 if (status
==NULL
|| U_FAILURE(*status
)) {
921 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
924 switch(RES_GET_TYPE(resB
->fRes
)) {
925 case URES_INT_VECTOR
:
926 return res_getIntVector(&(resB
->fResData
), resB
->fRes
, len
);
934 *status
= U_RESOURCE_TYPE_MISMATCH
;
940 /* this function returns a signed integer */
941 /* it performs sign extension */
942 U_CAPI
int32_t U_EXPORT2
ures_getInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
943 if (status
==NULL
|| U_FAILURE(*status
)) {
947 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
950 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
951 *status
= U_RESOURCE_TYPE_MISMATCH
;
954 return RES_GET_INT(resB
->fRes
);
957 U_CAPI
uint32_t U_EXPORT2
ures_getUInt(const UResourceBundle
* resB
, UErrorCode
*status
) {
958 if (status
==NULL
|| U_FAILURE(*status
)) {
962 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
965 if(RES_GET_TYPE(resB
->fRes
) != URES_INT
) {
966 *status
= U_RESOURCE_TYPE_MISMATCH
;
969 return RES_GET_UINT(resB
->fRes
);
973 U_CAPI UResType U_EXPORT2
ures_getType(const UResourceBundle
*resB
) {
979 type
= (UResType
) RES_GET_TYPE(resB
->fRes
);
980 return type
== URES_TABLE32
? URES_TABLE
: type
;
983 U_CAPI
const char * U_EXPORT2
ures_getKey(const UResourceBundle
*resB
) {
991 U_CAPI
int32_t U_EXPORT2
ures_getSize(const UResourceBundle
*resB
) {
999 static const UChar
* ures_getStringWithAlias(const UResourceBundle
*resB
, Resource r
, int32_t sIndex
, int32_t *len
, UErrorCode
*status
) {
1000 if(RES_GET_TYPE(r
) == URES_ALIAS
) {
1001 const UChar
* result
= 0;
1002 UResourceBundle
*tempRes
= ures_getByIndex(resB
, sIndex
, NULL
, status
);
1003 result
= ures_getString(tempRes
, len
, status
);
1004 ures_close(tempRes
);
1007 return res_getString(&(resB
->fResData
), r
, len
);
1011 U_CAPI
void U_EXPORT2
ures_resetIterator(UResourceBundle
*resB
){
1018 U_CAPI UBool U_EXPORT2
ures_hasNext(const UResourceBundle
*resB
) {
1022 return (UBool
)(resB
->fIndex
< resB
->fSize
-1);
1025 U_CAPI
const UChar
* U_EXPORT2
ures_getNextString(UResourceBundle
*resB
, int32_t* len
, const char ** key
, UErrorCode
*status
) {
1026 Resource r
= RES_BOGUS
;
1028 if (status
==NULL
|| U_FAILURE(*status
)) {
1032 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1036 if(resB
->fIndex
== resB
->fSize
-1) {
1037 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1040 switch(RES_GET_TYPE(resB
->fRes
)) {
1044 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1047 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, key
);
1048 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1049 /* TODO: do the fallback */
1051 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1053 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1054 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1055 /* TODO: do the fallback */
1057 return ures_getStringWithAlias(resB
, r
, resB
->fIndex
, len
, status
);
1059 return ures_getStringWithAlias(resB
, resB
->fRes
, resB
->fIndex
, len
, status
);
1060 case URES_INT_VECTOR
:
1070 U_CAPI UResourceBundle
* U_EXPORT2
ures_getNextResource(UResourceBundle
*resB
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1071 const char *key
= NULL
;
1072 Resource r
= RES_BOGUS
;
1074 if (status
==NULL
|| U_FAILURE(*status
)) {
1079 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1084 if(resB
->fIndex
== resB
->fSize
-1) {
1085 *status
= U_INDEX_OUTOFBOUNDS_ERROR
;
1089 switch(RES_GET_TYPE(resB
->fRes
)) {
1093 return ures_copyResb(fillIn
, resB
, status
);
1096 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
, &key
);
1097 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1098 /* TODO: do the fallback */
1100 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1102 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, resB
->fIndex
);
1103 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1104 /* TODO: do the fallback */
1106 return init_resb_result(&(resB
->fResData
), r
, key
, resB
->fIndex
, resB
->fData
, resB
, 0, fillIn
, status
);
1107 case URES_INT_VECTOR
:
1117 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByIndex(const UResourceBundle
*resB
, int32_t indexR
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1118 const char* key
= NULL
;
1119 Resource r
= RES_BOGUS
;
1121 if (status
==NULL
|| U_FAILURE(*status
)) {
1126 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1131 if(indexR
>= 0 && resB
->fSize
> indexR
) {
1132 switch(RES_GET_TYPE(resB
->fRes
)) {
1136 return ures_copyResb(fillIn
, resB
, status
);
1139 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexR
, &key
);
1140 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1141 /* TODO: do the fallback */
1143 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1145 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexR
);
1146 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1147 /* TODO: do the fallback */
1149 return init_resb_result(&(resB
->fResData
), r
, key
, indexR
, resB
->fData
, resB
, 0, fillIn
, status
);
1150 case URES_INT_VECTOR
:
1156 *status
= U_MISSING_RESOURCE_ERROR
;
1162 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByIndex(const UResourceBundle
*resB
, int32_t indexS
, int32_t* len
, UErrorCode
*status
) {
1163 const char* key
= NULL
;
1164 Resource r
= RES_BOGUS
;
1166 if (status
==NULL
|| U_FAILURE(*status
)) {
1170 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1174 if(indexS
>= 0 && resB
->fSize
> indexS
) {
1175 switch(RES_GET_TYPE(resB
->fRes
)) {
1179 return res_getString(&(resB
->fResData
), resB
->fRes
, len
);
1182 r
= res_getTableItemByIndex(&(resB
->fResData
), resB
->fRes
, indexS
, &key
);
1183 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1184 /* TODO: do the fallback */
1186 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1188 r
= res_getArrayItem(&(resB
->fResData
), resB
->fRes
, indexS
);
1189 if(r
== RES_BOGUS
&& resB
->fHasFallback
) {
1190 /* TODO: do the fallback */
1192 return ures_getStringWithAlias(resB
, r
, indexS
, len
, status
);
1194 return ures_getStringWithAlias(resB
, resB
->fRes
, indexS
, len
, status
);
1196 /*case URES_INT_VECTOR:*/
1201 *status
= U_MISSING_RESOURCE_ERROR
;
1206 /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1207 return resB->fResPath;
1210 U_CAPI UResourceBundle
* U_EXPORT2
1211 ures_findResource(const char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1213 UResourceBundle
*first
= NULL
;
1214 UResourceBundle
*result
= fillIn
;
1215 char *packageName
= NULL
;
1216 char *pathToResource
= NULL
;
1217 char *locale
= NULL
, *localeEnd
= NULL
;
1220 if(status
== NULL
|| U_FAILURE(*status
)) {
1224 length
= uprv_strlen(path
)+1;
1225 pathToResource
= (char *)uprv_malloc(length
*sizeof(char));
1227 if(pathToResource
== NULL
) {
1228 *status
= U_MEMORY_ALLOCATION_ERROR
;
1231 uprv_memcpy(pathToResource
, path
, length
);
1233 locale
= pathToResource
;
1234 if(*pathToResource
== RES_PATH_SEPARATOR
) { /* there is a path specification */
1236 packageName
= pathToResource
;
1237 pathToResource
= uprv_strchr(pathToResource
, RES_PATH_SEPARATOR
);
1238 if(pathToResource
== NULL
) {
1239 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1241 *pathToResource
= 0;
1242 locale
= pathToResource
+1;
1246 localeEnd
= uprv_strchr(locale
, RES_PATH_SEPARATOR
);
1247 if(localeEnd
!= NULL
) {
1251 first
= ures_open(packageName
, locale
, status
);
1253 if(U_SUCCESS(*status
)) {
1255 result
= ures_findSubResource(first
, localeEnd
+1, fillIn
, status
);
1257 result
= ures_copyResb(fillIn
, first
, status
);
1261 uprv_free(pathToResource
);
1265 U_CAPI UResourceBundle
* U_EXPORT2
1266 ures_findSubResource(const UResourceBundle
*resB
, char* path
, UResourceBundle
*fillIn
, UErrorCode
*status
)
1268 Resource res
= RES_BOGUS
;
1269 UResourceBundle
*result
= fillIn
;
1272 if(status
== NULL
|| U_FAILURE(*status
)) {
1276 /* here we do looping and circular alias checking */
1277 /* this loop is here because aliasing is resolved on this level, not on res level */
1278 /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1280 res
= res_findResource(&(resB
->fResData
), resB
->fRes
, &path
, &key
);
1281 if(res
!= RES_BOGUS
) {
1282 result
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1285 *status
= U_MISSING_RESOURCE_ERROR
;
1288 } while(uprv_strlen(path
)); /* there is more stuff in the path */
1293 U_CAPI UResourceBundle
* U_EXPORT2
1294 ures_getByKeyWithFallback(const UResourceBundle
*resB
,
1296 UResourceBundle
*fillIn
,
1297 UErrorCode
*status
) {
1298 Resource res
= RES_BOGUS
;
1299 /*UResourceDataEntry *realData = NULL;*/
1300 const char *key
= inKey
;
1301 UResourceBundle
*helper
= NULL
;
1303 if (status
==NULL
|| U_FAILURE(*status
)) {
1307 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1311 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1313 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1314 if(res
== RES_BOGUS
) {
1315 UResourceDataEntry
*dataEntry
= resB
->fData
;
1317 char* myPath
= path
;
1319 while(res
== RES_BOGUS
&& dataEntry
->fParent
!= NULL
) { /* Otherwise, we'll look in parents */
1320 dataEntry
= dataEntry
->fParent
;
1321 if(dataEntry
->fBogus
== U_ZERO_ERROR
) {
1322 uprv_strncpy(path
, resB
->fResPath
, resB
->fResPathLen
);
1323 uprv_strcpy(path
+resB
->fResPathLen
, inKey
);
1327 res
= res_findResource(&(dataEntry
->fData
), dataEntry
->fData
.rootRes
, &myPath
, &key
);
1328 if (RES_GET_TYPE(res
) == URES_ALIAS
&& *myPath
) {
1329 /* We hit an alias, but we didn't finish following the path. */
1330 helper
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, helper
, status
);
1331 dataEntry
= helper
->fData
;
1333 } while(uprv_strlen(myPath
));
1336 /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1337 if(res
!= RES_BOGUS
) {
1338 /* check if resB->fResPath gives the right name here */
1339 fillIn
= init_resb_result(&(dataEntry
->fData
), res
, inKey
, -1, dataEntry
, resB
, 0, fillIn
, status
);
1341 *status
= U_MISSING_RESOURCE_ERROR
;
1344 fillIn
= init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1348 *status
= U_RESOURCE_TYPE_MISMATCH
;
1355 U_CAPI UResourceBundle
* U_EXPORT2
ures_getByKey(const UResourceBundle
*resB
, const char* inKey
, UResourceBundle
*fillIn
, UErrorCode
*status
) {
1356 Resource res
= RES_BOGUS
;
1357 UResourceDataEntry
*realData
= NULL
;
1358 const char *key
= inKey
;
1360 if (status
==NULL
|| U_FAILURE(*status
)) {
1364 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1368 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1370 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1371 if(res
== RES_BOGUS
) {
1373 if(resB
->fHasFallback
== TRUE
) {
1374 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1375 if(U_SUCCESS(*status
)) {
1376 /* check if resB->fResPath gives the right name here */
1377 return init_resb_result(rd
, res
, key
, -1, realData
, resB
, 0, fillIn
, status
);
1379 *status
= U_MISSING_RESOURCE_ERROR
;
1382 *status
= U_MISSING_RESOURCE_ERROR
;
1385 return init_resb_result(&(resB
->fResData
), res
, key
, -1, resB
->fData
, resB
, 0, fillIn
, status
);
1389 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1391 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1392 /* here should go a first attempt to locate the key using index table */
1393 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1394 if(U_SUCCESS(*status
)) {
1395 return init_resb_result(rd
, res
, key
, realData
, resB
, fillIn
, status
);
1397 *status
= U_MISSING_RESOURCE_ERROR
;
1402 *status
= U_RESOURCE_TYPE_MISMATCH
;
1407 U_CAPI
const UChar
* U_EXPORT2
ures_getStringByKey(const UResourceBundle
*resB
, const char* inKey
, int32_t* len
, UErrorCode
*status
) {
1408 Resource res
= RES_BOGUS
;
1409 UResourceDataEntry
*realData
= NULL
;
1410 const char* key
= inKey
;
1412 if (status
==NULL
|| U_FAILURE(*status
)) {
1416 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1420 if(RES_GET_TYPE(resB
->fRes
) == URES_TABLE
|| RES_GET_TYPE(resB
->fRes
) == URES_TABLE32
) {
1423 res
= res_getTableItemByKey(&(resB
->fResData
), resB
->fRes
, &t
, &key
);
1425 if(res
== RES_BOGUS
) {
1427 if(resB
->fHasFallback
== TRUE
) {
1428 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1429 if(U_SUCCESS(*status
)) {
1430 switch (RES_GET_TYPE(res
)) {
1435 return res_getString(rd
, res
, len
);
1438 const UChar
* result
= 0;
1439 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1440 result
= ures_getString(tempRes
, len
, status
);
1441 ures_close(tempRes
);
1445 *status
= U_RESOURCE_TYPE_MISMATCH
;
1448 *status
= U_MISSING_RESOURCE_ERROR
;
1451 *status
= U_MISSING_RESOURCE_ERROR
;
1454 switch (RES_GET_TYPE(res
)) {
1459 return res_getString(&(resB
->fResData
), res
, len
);
1462 const UChar
* result
= 0;
1463 UResourceBundle
*tempRes
= ures_getByKey(resB
, inKey
, NULL
, status
);
1464 result
= ures_getString(tempRes
, len
, status
);
1465 ures_close(tempRes
);
1469 *status
= U_RESOURCE_TYPE_MISMATCH
;
1474 /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
1476 else if(RES_GET_TYPE(resB
->fRes
) == URES_ARRAY
&& resB
->fHasFallback
== TRUE
) {
1477 /* here should go a first attempt to locate the key using index table */
1478 const ResourceData
*rd
= getFallbackData(resB
, &key
, &realData
, &res
, status
);
1479 if(U_SUCCESS(*status
)) {
1480 return res_getString(rd
, res
, len
);
1482 *status
= U_MISSING_RESOURCE_ERROR
;
1487 *status
= U_RESOURCE_TYPE_MISMATCH
;
1493 /* TODO: clean from here down */
1496 * INTERNAL: Get the name of the first real locale (not placeholder)
1497 * that has resource bundle data.
1499 U_CAPI
const char* U_EXPORT2
1500 ures_getLocale(const UResourceBundle
* resourceBundle
, UErrorCode
* status
)
1502 if (status
==NULL
|| U_FAILURE(*status
)) {
1505 if (!resourceBundle
) {
1506 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1509 return resourceBundle
->fData
->fName
;
1513 U_CAPI
const char* U_EXPORT2
1514 ures_getLocaleByType(const UResourceBundle
* resourceBundle
,
1515 ULocDataLocaleType type
,
1516 UErrorCode
* status
) {
1517 if (status
==NULL
|| U_FAILURE(*status
)) {
1520 if (!resourceBundle
) {
1521 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1525 case ULOC_ACTUAL_LOCALE
:
1526 return resourceBundle
->fData
->fName
;
1528 case ULOC_VALID_LOCALE
:
1529 return resourceBundle
->fTopLevelData
->fName
;
1531 case ULOC_REQUESTED_LOCALE
:
1535 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1543 U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) {
1544 if(resB->fResPath == NULL) {
1545 resB->fResPath = resB->fResBuf;
1546 *(resB->fResPath) = 0;
1548 resB->fResPathLen = uprv_strlen(toAdd);
1549 if(RES_BUFSIZE <= resB->fResPathLen+1) {
1550 if(resB->fResPath == resB->fResBuf) {
1551 resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));
1553 resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));
1556 uprv_strcpy(resB->fResPath, toAdd);
1559 U_CFUNC
void ures_appendResPath(UResourceBundle
*resB
, const char* toAdd
, int32_t lenToAdd
) {
1560 int32_t resPathLenOrig
= resB
->fResPathLen
;
1561 if(resB
->fResPath
== NULL
) {
1562 resB
->fResPath
= resB
->fResBuf
;
1563 *(resB
->fResPath
) = 0;
1564 resB
->fResPathLen
= 0;
1566 resB
->fResPathLen
+= lenToAdd
;
1567 if(RES_BUFSIZE
<= resB
->fResPathLen
+1) {
1568 if(resB
->fResPath
== resB
->fResBuf
) {
1569 resB
->fResPath
= (char *)uprv_malloc((resB
->fResPathLen
+1)*sizeof(char));
1570 uprv_strcpy(resB
->fResPath
, resB
->fResBuf
);
1572 resB
->fResPath
= (char *)uprv_realloc(resB
->fResPath
, (resB
->fResPathLen
+1)*sizeof(char));
1575 uprv_strcpy(resB
->fResPath
+ resPathLenOrig
, toAdd
);
1578 U_CFUNC
void ures_freeResPath(UResourceBundle
*resB
) {
1579 if (resB
->fResPath
&& resB
->fResPath
!= resB
->fResBuf
) {
1580 uprv_free(resB
->fResPath
);
1582 resB
->fResPath
= NULL
;
1583 resB
->fResPathLen
= 0;
1587 U_CFUNC
const char* ures_getName(const UResourceBundle
* resB
) {
1592 return resB
->fData
->fName
;
1595 U_CFUNC
const char* ures_getPath(const UResourceBundle
* resB
) {
1600 return resB
->fData
->fPath
;
1603 /* OLD API implementation */
1606 * API: This function is used to open a resource bundle
1607 * proper fallback chaining is executed while initialization.
1608 * The result is stored in cache for later fallback search.
1610 U_CAPI
void U_EXPORT2
1611 ures_openFillIn(UResourceBundle
*r
, const char* path
,
1612 const char* localeID
, UErrorCode
* status
) {
1614 *status
= U_INTERNAL_PROGRAM_ERROR
;
1616 UResourceDataEntry
*firstData
;
1617 r
->fHasFallback
= TRUE
;
1618 r
->fIsTopLevel
= TRUE
;
1622 if(r
->fData
!= NULL
) {
1623 entryClose(r
->fData
);
1625 if(r
->fVersion
!= NULL
) {
1626 uprv_free(r
->fVersion
);
1628 r
->fData
= entryOpen(path
, localeID
, status
);
1629 /* this is a quick fix to get regular data in bundle - until construction is cleaned up */
1630 firstData
= r
->fData
;
1631 while(firstData
->fBogus
!= U_ZERO_ERROR
&& firstData
->fParent
!= NULL
) {
1632 firstData
= firstData
->fParent
;
1634 r
->fResData
.data
= firstData
->fData
.data
;
1635 r
->fResData
.pRoot
= firstData
->fData
.pRoot
;
1636 r
->fResData
.rootRes
= firstData
->fData
.rootRes
;
1637 r
->fRes
= r
->fResData
.rootRes
;
1638 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1639 /*r->fParent = RES_BOGUS;*/
1640 /*r->fResPath = NULL;*/
1641 r
->fParentRes
= NULL
;
1642 r
->fTopLevelData
= r
->fData
;
1644 ures_freeResPath(r
);
1648 U_CAPI UResourceBundle
* U_EXPORT2
1649 ures_open(const char* path
,
1650 const char* localeID
,
1653 char canonLocaleID
[100];
1654 UResourceDataEntry
*hasData
= NULL
;
1658 if(status
== NULL
|| U_FAILURE(*status
)) {
1662 /* first "canonicalize" the locale ID */
1663 length
= uloc_getBaseName(localeID
, canonLocaleID
, sizeof(canonLocaleID
), status
);
1664 if(U_FAILURE(*status
) || *status
== U_STRING_NOT_TERMINATED_WARNING
) {
1665 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1669 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1671 *status
= U_MEMORY_ALLOCATION_ERROR
;
1675 r
->fHasFallback
= TRUE
;
1676 r
->fIsTopLevel
= TRUE
;
1677 ures_setIsStackObject(r
, FALSE
);
1681 r
->fData
= entryOpen(path
, canonLocaleID
, status
);
1682 if(U_FAILURE(*status
)) {
1686 r
->fParentRes
= NULL
;
1687 r
->fTopLevelData
= r
->fData
;
1690 while(hasData
->fBogus
!= U_ZERO_ERROR
) {
1691 hasData
= hasData
->fParent
;
1692 if(hasData
== NULL
) {
1693 /* This can happen only if fallback chain gets broken by an act of God */
1694 /* TODO: this unlikely to happen, consider removing it */
1695 entryClose(r
->fData
);
1697 *status
= U_MISSING_RESOURCE_ERROR
;
1702 r
->fResData
.data
= hasData
->fData
.data
;
1703 r
->fResData
.pRoot
= hasData
->fData
.pRoot
;
1704 r
->fResData
.rootRes
= hasData
->fData
.rootRes
;
1705 r
->fRes
= r
->fResData
.rootRes
;
1706 /*r->fParent = RES_BOGUS;*/
1707 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1711 if(r->fData->fPath != NULL) {
1712 ures_setResPath(r, r->fData->fPath);
1713 ures_appendResPath(r, RES_PATH_PACKAGE_S);
1714 ures_appendResPath(r, r->fData->fName);
1716 ures_setResPath(r, r->fData->fName);
1725 * Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed
1726 * or sought. However, alias substitution will happen!
1728 U_CAPI UResourceBundle
* U_EXPORT2
1729 ures_openDirect(const char* path
, const char* localeID
, UErrorCode
* status
) {
1731 UErrorCode subStatus
= U_ZERO_ERROR
;
1733 if(status
== NULL
|| U_FAILURE(*status
)) {
1737 r
= (UResourceBundle
*)uprv_malloc(sizeof(UResourceBundle
));
1739 *status
= U_MEMORY_ALLOCATION_ERROR
;
1743 r
->fHasFallback
= FALSE
;
1744 r
->fIsTopLevel
= TRUE
;
1745 ures_setIsStackObject(r
, FALSE
);
1747 r
->fData
= entryOpen(path
, localeID
, &subStatus
);
1748 if(U_FAILURE(subStatus
)) {
1749 *status
= subStatus
;
1753 if(subStatus
!= U_ZERO_ERROR
/*r->fData->fBogus != U_ZERO_ERROR*/) {
1754 /* we didn't find one we were looking for - so openDirect */
1756 entryClose(r
->fData
);
1758 *status
= U_MISSING_RESOURCE_ERROR
;
1764 r
->fResData
.data
= r
->fData
->fData
.data
;
1765 r
->fResData
.pRoot
= r
->fData
->fData
.pRoot
;
1766 r
->fResData
.rootRes
= r
->fData
->fData
.rootRes
;
1767 r
->fRes
= r
->fResData
.rootRes
;
1768 /*r->fParent = RES_BOGUS;*/
1769 r
->fSize
= res_countArrayItems(&(r
->fResData
), r
->fRes
);
1772 r
->fParentRes
= NULL
;
1773 r
->fTopLevelData
= r
->fData
;
1779 * API: Counts members. For arrays and tables, returns number of resources.
1780 * For strings, returns 1.
1782 U_CAPI
int32_t U_EXPORT2
1783 ures_countArrayItems(const UResourceBundle
* resourceBundle
,
1784 const char* resourceKey
,
1787 UResourceBundle resData
;
1788 ures_initStackObject(&resData
);
1789 if (status
==NULL
|| U_FAILURE(*status
)) {
1792 if(resourceBundle
== NULL
) {
1793 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
1796 ures_getByKey(resourceBundle
, resourceKey
, &resData
, status
);
1798 if(resData
.fResData
.data
!= NULL
) {
1799 int32_t result
= res_countArrayItems(&resData
.fResData
, resData
.fRes
);
1800 ures_close(&resData
);
1803 *status
= U_MISSING_RESOURCE_ERROR
;
1804 ures_close(&resData
);
1809 U_CAPI
void U_EXPORT2
1810 ures_close(UResourceBundle
* resB
)
1813 if(resB
->fData
!= NULL
) {
1814 entryClose(resB
->fData
);
1816 if(resB
->fVersion
!= NULL
) {
1817 uprv_free(resB
->fVersion
);
1819 ures_freeResPath(resB
);
1821 if(ures_isStackObject(resB
) == FALSE
) {
1826 /* poison the data */
1827 uprv_memset(resB
, -1, sizeof(UResourceBundle
));
1833 U_CAPI
const char* U_EXPORT2
1834 ures_getVersionNumber(const UResourceBundle
* resourceBundle
)
1836 if (!resourceBundle
) return NULL
;
1838 if(resourceBundle
->fVersion
== NULL
) {
1840 /* If the version ID has not been built yet, then do so. Retrieve */
1841 /* the minor version from the file. */
1842 UErrorCode status
= U_ZERO_ERROR
;
1843 int32_t minor_len
= 0;
1846 const UChar
* minor_version
= ures_getStringByKey(resourceBundle
, kVersionTag
, &minor_len
, &status
);
1848 /* Determine the length of of the final version string. This is */
1849 /* the length of the major part + the length of the separator */
1850 /* (==1) + the length of the minor part (+ 1 for the zero byte at */
1853 len
= (minor_len
> 0) ? minor_len
: 1;
1855 /* Allocate the string, and build it up. */
1856 /* + 1 for zero byte */
1859 ((UResourceBundle
*)resourceBundle
)->fVersion
= (char *)uprv_malloc(1 + len
);
1862 u_UCharsToChars(minor_version
, resourceBundle
->fVersion
, minor_len
);
1863 resourceBundle
->fVersion
[len
] = '\0';
1866 uprv_strcpy(resourceBundle
->fVersion
, kDefaultMinorVersion
);
1870 return resourceBundle
->fVersion
;
1873 U_CAPI
void U_EXPORT2
ures_getVersion(const UResourceBundle
* resB
, UVersionInfo versionInfo
) {
1876 u_versionFromString(versionInfo
, ures_getVersionNumber(resB
));
1879 /** Tree support functions *******************************/
1880 #define INDEX_LOCALE_NAME "res_index"
1881 #define INDEX_TAG "InstalledLocales"
1882 #define DEFAULT_TAG "default"
1884 #if defined(URES_TREE_DEBUG)
1888 typedef struct ULocalesContext
{
1889 UResourceBundle installed
;
1890 UResourceBundle curr
;
1893 static void U_CALLCONV
1894 ures_loc_closeLocales(UEnumeration
*enumerator
) {
1895 ULocalesContext
*ctx
= (ULocalesContext
*)enumerator
->context
;
1896 ures_close(&ctx
->curr
);
1897 ures_close(&ctx
->installed
);
1899 uprv_free(enumerator
);
1902 static int32_t U_CALLCONV
1903 ures_loc_countLocales(UEnumeration
*en
, UErrorCode
*status
) {
1904 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
1905 return ures_getSize(&ctx
->installed
);
1908 static const char* U_CALLCONV
1909 ures_loc_nextLocale(UEnumeration
* en
,
1910 int32_t* resultLength
,
1911 UErrorCode
* status
) {
1912 ULocalesContext
*ctx
= (ULocalesContext
*)en
->context
;
1913 UResourceBundle
*res
= &(ctx
->installed
);
1914 UResourceBundle
*k
= NULL
;
1915 const char *result
= NULL
;
1917 if(ures_hasNext(res
) && (k
= ures_getNextResource(res
, &ctx
->curr
, status
))) {
1918 result
= ures_getKey(k
);
1919 len
= uprv_strlen(result
);
1922 *resultLength
= len
;
1927 static void U_CALLCONV
1928 ures_loc_resetLocales(UEnumeration
* en
,
1929 UErrorCode
* status
) {
1930 UResourceBundle
*res
= &((ULocalesContext
*)en
->context
)->installed
;
1931 ures_resetIterator(res
);
1935 static const UEnumeration gLocalesEnum
= {
1938 ures_loc_closeLocales
,
1939 ures_loc_countLocales
,
1941 ures_loc_nextLocale
,
1942 ures_loc_resetLocales
1946 U_CAPI UEnumeration
* U_EXPORT2
1947 ures_openAvailableLocales(const char *path
, UErrorCode
*status
)
1949 UResourceBundle
*index
= NULL
;
1950 UEnumeration
*en
= NULL
;
1951 ULocalesContext
*myContext
= NULL
;
1953 if(U_FAILURE(*status
)) {
1956 myContext
= uprv_malloc(sizeof(ULocalesContext
));
1957 en
= (UEnumeration
*)uprv_malloc(sizeof(UEnumeration
));
1958 if(!en
|| !myContext
) {
1959 *status
= U_MEMORY_ALLOCATION_ERROR
;
1961 uprv_free(myContext
);
1964 uprv_memcpy(en
, &gLocalesEnum
, sizeof(UEnumeration
));
1966 ures_initStackObject(&myContext
->installed
);
1967 ures_initStackObject(&myContext
->curr
);
1968 index
= ures_openDirect(path
, INDEX_LOCALE_NAME
, status
);
1969 ures_getByKey(index
, INDEX_TAG
, &myContext
->installed
, status
);
1970 if(U_SUCCESS(*status
)) {
1971 #if defined(URES_TREE_DEBUG)
1972 fprintf(stderr
, "Got %s::%s::[%s] : %s\n",
1973 path
, INDEX_LOCALE_NAME
, INDEX_TAG
, ures_getKey(&myContext
->installed
));
1975 en
->context
= myContext
;
1977 #if defined(URES_TREE_DEBUG)
1978 fprintf(stderr
, "%s open failed - %s\n", path
, u_errorName(*status
));
1980 ures_close(&myContext
->installed
);
1981 uprv_free(myContext
);
1991 U_CAPI
int32_t U_EXPORT2
1992 ures_getFunctionalEquivalent(char *result
, int32_t resultCapacity
,
1993 const char *path
, const char *resName
, const char *keyword
, const char *locid
,
1994 UBool
*isAvailable
, UBool omitDefault
, UErrorCode
*status
)
1996 char kwVal
[1024] = ""; /* value of keyword 'keyword' */
1997 char defVal
[1024] = ""; /* default value for given locale */
1998 char defLoc
[1024] = ""; /* default value for given locale */
1999 char base
[1024] = ""; /* base locale */
2002 char full
[1024] = "";
2003 UResourceBundle bund1
, bund2
;
2004 UResourceBundle
*res
= NULL
;
2005 UErrorCode subStatus
= U_ZERO_ERROR
;
2007 if(U_FAILURE(*status
)) return 0;
2009 *isAvailable
= TRUE
;
2011 uloc_getKeywordValue(locid
, keyword
, kwVal
, 1024-1,&subStatus
);
2012 if(!uprv_strcmp(kwVal
, DEFAULT_TAG
)) {
2015 uloc_getBaseName(locid
, base
, 1024-1,&subStatus
);
2016 #if defined(URES_TREE_DEBUG)
2017 fprintf(stderr
, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",
2018 locid
, keyword
, kwVal
, base
, u_errorName(subStatus
));
2020 ures_initStackObject(&bund1
);
2021 ures_initStackObject(&bund2
);
2024 uprv_strcpy(parent
, base
);
2025 uprv_strcpy(found
, base
);
2027 if(U_FAILURE(subStatus
)) {
2028 *status
= subStatus
;
2033 subStatus
= U_ZERO_ERROR
;
2034 res
= ures_open(path
, parent
, &subStatus
);
2035 if(((subStatus
== U_USING_FALLBACK_WARNING
) ||
2036 (subStatus
== U_USING_DEFAULT_WARNING
)) && isAvailable
) {
2037 *isAvailable
= FALSE
;
2039 isAvailable
= NULL
; /* only want to set this the first time around */
2041 #if defined(URES_TREE_DEBUG)
2042 fprintf(stderr
, "%s;%s -> %s [%s]\n", path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), ures_getLocale(res
, &subStatus
));
2044 if(U_FAILURE(subStatus
)) {
2045 *status
= subStatus
;
2046 } else if(subStatus
== U_ZERO_ERROR
) {
2047 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2048 if(subStatus
== U_ZERO_ERROR
) {
2049 const UChar
*defUstr
;
2051 /* look for default item */
2052 #if defined(URES_TREE_DEBUG)
2053 fprintf(stderr
, "%s;%s : loaded default -> %s\n",
2054 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
));
2056 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2057 if(U_SUCCESS(subStatus
) && defLen
) {
2058 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2059 #if defined(URES_TREE_DEBUG)
2060 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2061 path
?path
:"ICUDATA", parent
, keyword
, defVal
, u_errorName(subStatus
));
2063 uprv_strcpy(defLoc
, parent
);
2065 uprv_strcpy(kwVal
, defVal
);
2066 #if defined(URES_TREE_DEBUG)
2067 fprintf(stderr
, "%s;%s -> kwVal = %s\n",
2068 path
?path
:"ICUDATA", parent
, keyword
, kwVal
);
2075 subStatus
= U_ZERO_ERROR
;
2077 uprv_strcpy(found
, parent
);
2078 uloc_getParent(found
,parent
,1023,&subStatus
);
2080 } while(!defVal
[0] && *found
&& U_SUCCESS(*status
));
2082 /* Now, see if we can find the kwVal collator.. start the search over.. */
2083 uprv_strcpy(parent
, base
);
2084 uprv_strcpy(found
, base
);
2087 subStatus
= U_ZERO_ERROR
;
2088 res
= ures_open(path
, parent
, &subStatus
);
2089 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2090 *isAvailable
= FALSE
;
2092 isAvailable
= NULL
; /* only want to set this the first time around */
2094 #if defined(URES_TREE_DEBUG)
2095 fprintf(stderr
, "%s;%s -> %s (looking for %s)\n",
2096 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2098 if(U_FAILURE(subStatus
)) {
2099 *status
= subStatus
;
2100 } else if(subStatus
== U_ZERO_ERROR
) {
2101 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2102 #if defined(URES_TREE_DEBUG)
2103 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, resName
, u_errorName(subStatus
));
2105 if(subStatus
== U_ZERO_ERROR
) {
2106 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2107 #if defined(URES_TREE_DEBUG)
2108 /**/ fprintf(stderr
,"@%d [%s] %s\n", __LINE__
, kwVal
, u_errorName(subStatus
));
2110 if(subStatus
== U_ZERO_ERROR
) {
2111 #if defined(URES_TREE_DEBUG)
2112 fprintf(stderr
, "%s;%s -> full0 %s=%s, %s\n",
2113 path
?path
:"ICUDATA", parent
, keyword
, kwVal
, u_errorName(subStatus
));
2115 uprv_strcpy(full
, parent
);
2117 uprv_strcpy(full
, "root");
2119 /* now, recalculate default kw if need be */
2120 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2121 const UChar
*defUstr
;
2123 /* look for default item */
2124 #if defined(URES_TREE_DEBUG)
2125 fprintf(stderr
, "%s;%s -> recalculating Default0\n",
2126 path
?path
:"ICUDATA", full
);
2128 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2129 if(U_SUCCESS(subStatus
) && defLen
) {
2130 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2131 #if defined(URES_TREE_DEBUG)
2132 fprintf(stderr
, "%s;%s -> default0 %s=%s, %s\n",
2133 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2135 uprv_strcpy(defLoc
, full
);
2137 } /* end of recalculate default KW */
2138 #if defined(URES_TREE_DEBUG)
2140 fprintf(stderr
, "No trim0, %s <= %s\n", defLoc
, full
);
2144 #if defined(URES_TREE_DEBUG)
2145 fprintf(stderr
, "err=%s in %s looking for %s\n",
2146 u_errorName(subStatus
), parent
, kwVal
);
2152 subStatus
= U_ZERO_ERROR
;
2154 uprv_strcpy(found
, parent
);
2155 uloc_getParent(found
,parent
,1023,&subStatus
);
2157 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2159 if((full
[0]==0) && uprv_strcmp(kwVal
, defVal
)) {
2160 #if defined(URES_TREE_DEBUG)
2161 fprintf(stderr
, "Failed to locate kw %s - try default %s\n", kwVal
, defVal
);
2163 uprv_strcpy(kwVal
, defVal
);
2164 uprv_strcpy(parent
, base
);
2165 uprv_strcpy(found
, base
);
2167 do { /* search for 'default' named item */
2168 subStatus
= U_ZERO_ERROR
;
2169 res
= ures_open(path
, parent
, &subStatus
);
2170 if((subStatus
== U_USING_FALLBACK_WARNING
) && isAvailable
) {
2171 *isAvailable
= FALSE
;
2173 isAvailable
= NULL
; /* only want to set this the first time around */
2175 #if defined(URES_TREE_DEBUG)
2176 fprintf(stderr
, "%s;%s -> %s (looking for default %s)\n",
2177 path
?path
:"ICUDATA", parent
, u_errorName(subStatus
), kwVal
);
2179 if(U_FAILURE(subStatus
)) {
2180 *status
= subStatus
;
2181 } else if(subStatus
== U_ZERO_ERROR
) {
2182 ures_getByKey(res
,resName
,&bund1
, &subStatus
);
2183 if(subStatus
== U_ZERO_ERROR
) {
2184 ures_getByKey(&bund1
, kwVal
, &bund2
, &subStatus
);
2185 if(subStatus
== U_ZERO_ERROR
) {
2186 #if defined(URES_TREE_DEBUG)
2187 fprintf(stderr
, "%s;%s -> full1 %s=%s, %s\n", path
?path
:"ICUDATA",
2188 parent
, keyword
, kwVal
, u_errorName(subStatus
));
2190 uprv_strcpy(full
, parent
);
2192 uprv_strcpy(full
, "root");
2195 /* now, recalculate default kw if need be */
2196 if(uprv_strlen(defLoc
) > uprv_strlen(full
)) {
2197 const UChar
*defUstr
;
2199 /* look for default item */
2200 #if defined(URES_TREE_DEBUG)
2201 fprintf(stderr
, "%s;%s -> recalculating Default1\n",
2202 path
?path
:"ICUDATA", full
);
2204 defUstr
= ures_getStringByKey(&bund1
, DEFAULT_TAG
, &defLen
, &subStatus
);
2205 if(U_SUCCESS(subStatus
) && defLen
) {
2206 u_UCharsToChars(defUstr
, defVal
, u_strlen(defUstr
));
2207 #if defined(URES_TREE_DEBUG)
2208 fprintf(stderr
, "%s;%s -> default %s=%s, %s\n",
2209 path
?path
:"ICUDATA", full
, keyword
, defVal
, u_errorName(subStatus
));
2211 uprv_strcpy(defLoc
, full
);
2213 } /* end of recalculate default KW */
2214 #if defined(URES_TREE_DEBUG)
2216 fprintf(stderr
, "No trim1, %s <= %s\n", defLoc
, full
);
2222 subStatus
= U_ZERO_ERROR
;
2224 uprv_strcpy(found
, parent
);
2225 uloc_getParent(found
,parent
,1023,&subStatus
);
2227 } while(!full
[0] && *found
&& U_SUCCESS(*status
));
2230 if(U_SUCCESS(*status
)) {
2232 #if defined(URES_TREE_DEBUG)
2233 fprintf(stderr
, "Still could not load keyword %s=%s\n", keyword
, kwVal
);
2235 *status
= U_MISSING_RESOURCE_ERROR
;
2236 } else if(omitDefault
) {
2237 #if defined(URES_TREE_DEBUG)
2238 fprintf(stderr
,"Trim? full=%s, defLoc=%s, found=%s\n", full
, defLoc
, found
);
2240 if(uprv_strlen(defLoc
) <= uprv_strlen(full
)) {
2241 /* found the keyword in a *child* of where the default tag was present. */
2242 if(!uprv_strcmp(kwVal
, defVal
)) { /* if the requested kw is default, */
2243 /* and the default is in or in an ancestor of the current locale */
2244 #if defined(URES_TREE_DEBUG)
2245 fprintf(stderr
, "Removing unneeded var %s=%s\n", keyword
, kwVal
);
2251 uprv_strcpy(found
, full
);
2253 uprv_strcat(found
, "@");
2254 uprv_strcat(found
, keyword
);
2255 uprv_strcat(found
, "=");
2256 uprv_strcat(found
, kwVal
);
2257 } else if(!omitDefault
) {
2258 uprv_strcat(found
, "@");
2259 uprv_strcat(found
, keyword
);
2260 uprv_strcat(found
, "=");
2261 uprv_strcat(found
, defVal
);
2264 /* we found the default locale - no need to repeat it.*/
2269 length
= uprv_strlen(found
);
2271 if(U_SUCCESS(*status
)) {
2272 int32_t copyLength
= uprv_min(length
, resultCapacity
);
2274 uprv_strncpy(result
, found
, copyLength
);
2277 *status
= U_MISSING_RESOURCE_ERROR
;
2283 return u_terminateChars(result
, resultCapacity
, length
, status
);
2286 U_CAPI UEnumeration
* U_EXPORT2
2287 ures_getKeywordValues(const char *path
, const char *keyword
, UErrorCode
*status
)
2289 #define VALUES_BUF_SIZE 2048
2290 #define VALUES_LIST_SIZE 512
2292 char valuesBuf
[VALUES_BUF_SIZE
];
2293 int32_t valuesIndex
= 0;
2294 const char *valuesList
[VALUES_LIST_SIZE
];
2295 int32_t valuesCount
= 0;
2300 UEnumeration
*locs
= NULL
;
2302 UResourceBundle item
;
2303 UResourceBundle subItem
;
2305 ures_initStackObject(&item
);
2306 ures_initStackObject(&subItem
);
2307 locs
= ures_openAvailableLocales(path
, status
);
2309 if(U_FAILURE(*status
)) {
2311 ures_close(&subItem
);
2318 while((locale
= uenum_next(locs
, &locLen
, status
))) {
2319 UResourceBundle
*bund
= NULL
;
2320 UResourceBundle
*subPtr
= NULL
;
2321 UErrorCode subStatus
= U_ZERO_ERROR
; /* don't fail if a bundle is unopenable */
2322 bund
= ures_openDirect(path
, locale
, &subStatus
);
2324 #if defined(URES_TREE_DEBUG)
2325 if(!bund
|| U_FAILURE(subStatus
)) {
2326 fprintf(stderr
, "%s-%s values: Can't open %s locale - skipping. (%s)\n",
2327 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2331 ures_getByKey(bund
, keyword
, &item
, &subStatus
);
2333 if(!bund
|| U_FAILURE(subStatus
)) {
2334 #if defined(URES_TREE_DEBUG)
2335 fprintf(stderr
, "%s-%s values: Can't find in %s - skipping. (%s)\n",
2336 path
?path
:"<ICUDATA>", keyword
, locale
, u_errorName(subStatus
));
2343 while((subPtr
= ures_getNextResource(&item
,&subItem
,&subStatus
))
2344 && U_SUCCESS(subStatus
)) {
2347 k
= ures_getKey(&subItem
);
2349 #if defined(URES_TREE_DEBUG)
2350 /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2352 for(i
=0;k
&&i
<valuesCount
;i
++) {
2353 if(!uprv_strcmp(valuesList
[i
],k
)) {
2354 k
= NULL
; /* found duplicate */
2358 int32_t kLen
= uprv_strlen(k
);
2359 if(!uprv_strcmp(k
,DEFAULT_TAG
)) {
2360 continue; /* don't need 'default'. */
2362 if((valuesCount
>= (VALUES_LIST_SIZE
-1)) || /* no more space in list .. */
2363 ((valuesIndex
+kLen
+1+1) >= VALUES_BUF_SIZE
)) { /* no more space in buffer (string + 2 nulls) */
2364 *status
= U_ILLEGAL_ARGUMENT_ERROR
; /* out of space.. */
2366 uprv_strcpy(valuesBuf
+valuesIndex
, k
);
2367 valuesList
[valuesCount
++] = valuesBuf
+valuesIndex
;
2368 valuesIndex
+= kLen
;
2369 #if defined(URES_TREE_DEBUG)
2370 fprintf(stderr
, "%s | %s | %s | [%s] (UNIQUE)\n",
2371 path
?path
:"<ICUDATA>", keyword
, locale
, k
);
2373 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2379 valuesBuf
[valuesIndex
++] = 0; /* terminate */
2382 ures_close(&subItem
);
2384 #if defined(URES_TREE_DEBUG)
2385 fprintf(stderr
, "%s: size %d, #%d\n", u_errorName(*status
),
2386 valuesIndex
, valuesCount
);
2388 return uloc_openKeywordList(valuesBuf
, valuesIndex
, status
);