/src/icu/source/common/uresbund.cpp
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | // © 2016 and later: Unicode, Inc. and others.  | 
2  |  | // License & terms of use: http://www.unicode.org/copyright.html  | 
3  |  | /*  | 
4  |  | ******************************************************************************  | 
5  |  | * Copyright (C) 1997-2016, International Business Machines Corporation and  | 
6  |  | * others. All Rights Reserved.  | 
7  |  | ******************************************************************************  | 
8  |  | *  | 
9  |  | * File uresbund.cpp  | 
10  |  | *  | 
11  |  | * Modification History:  | 
12  |  | *  | 
13  |  | *   Date        Name        Description  | 
14  |  | *   04/01/97    aliu        Creation.  | 
15  |  | *   06/14/99    stephen     Removed functions taking a filename suffix.  | 
16  |  | *   07/20/99    stephen     Changed for UResourceBundle typedef'd to void*  | 
17  |  | *   11/09/99    weiv            Added ures_getLocale()  | 
18  |  | *   March 2000  weiv        Total overhaul - using data in DLLs  | 
19  |  | *   06/20/2000  helena      OS/400 port changes; mostly typecast.  | 
20  |  | *   06/24/02    weiv        Added support for resource sharing  | 
21  |  | ******************************************************************************  | 
22  |  | */  | 
23  |  |  | 
24  |  | #include "unicode/ures.h"  | 
25  |  | #include "unicode/ustring.h"  | 
26  |  | #include "unicode/ucnv.h"  | 
27  |  | #include "charstr.h"  | 
28  |  | #include "uresimp.h"  | 
29  |  | #include "ustr_imp.h"  | 
30  |  | #include "cwchar.h"  | 
31  |  | #include "ucln_cmn.h"  | 
32  |  | #include "cmemory.h"  | 
33  |  | #include "cstring.h"  | 
34  |  | #include "mutex.h"  | 
35  |  | #include "uhash.h"  | 
36  |  | #include "unicode/uenum.h"  | 
37  |  | #include "uenumimp.h"  | 
38  |  | #include "ulocimp.h"  | 
39  |  | #include "umutex.h"  | 
40  |  | #include "putilimp.h"  | 
41  |  | #include "uassert.h"  | 
42  |  | #include "uresdata.h"  | 
43  |  |  | 
44  |  | using namespace icu;  | 
45  |  |  | 
46  |  | /*  | 
47  |  | Static cache for already opened resource bundles - mostly for keeping fallback info  | 
48  |  | TODO: This cache should probably be removed when the deprecated code is  | 
49  |  |       completely removed.  | 
50  |  | */  | 
51  |  | static UHashtable *cache = NULL;  | 
52  |  | static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;  | 
53  |  |  | 
54  |  | static UMutex resbMutex;  | 
55  |  |  | 
56  |  | /* INTERNAL: hashes an entry  */  | 
57  | 0  | static int32_t U_CALLCONV hashEntry(const UHashTok parm) { | 
58  | 0  |     UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer;  | 
59  | 0  |     UHashTok namekey, pathkey;  | 
60  | 0  |     namekey.pointer = b->fName;  | 
61  | 0  |     pathkey.pointer = b->fPath;  | 
62  | 0  |     return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);  | 
63  | 0  | }  | 
64  |  |  | 
65  |  | /* INTERNAL: compares two entries */  | 
66  | 0  | static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) { | 
67  | 0  |     UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer;  | 
68  | 0  |     UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer;  | 
69  | 0  |     UHashTok name1, name2, path1, path2;  | 
70  | 0  |     name1.pointer = b1->fName;  | 
71  | 0  |     name2.pointer = b2->fName;  | 
72  | 0  |     path1.pointer = b1->fPath;  | 
73  | 0  |     path2.pointer = b2->fPath;  | 
74  | 0  |     return (UBool)(uhash_compareChars(name1, name2) &&  | 
75  | 0  |         uhash_compareChars(path1, path2));  | 
76  | 0  | }  | 
77  |  |  | 
78  |  |  | 
79  |  | /**  | 
80  |  |  *  Internal function, gets parts of locale name according   | 
81  |  |  *  to the position of '_' character  | 
82  |  |  */  | 
83  | 0  | static UBool chopLocale(char *name) { | 
84  | 0  |     char *i = uprv_strrchr(name, '_');  | 
85  |  | 
  | 
86  | 0  |     if(i != NULL) { | 
87  | 0  |         *i = '\0';  | 
88  | 0  |         return TRUE;  | 
89  | 0  |     }  | 
90  |  |  | 
91  | 0  |     return FALSE;  | 
92  | 0  | }  | 
93  |  |  | 
94  |  | /**  | 
95  |  |  *  Called to check whether a name without '_' needs to be checked for a parent.  | 
96  |  |  *  Some code had assumed that locale IDs with '_' could not have a non-root parent.  | 
97  |  |  *  We may want a better way of doing this.  | 
98  |  |  */  | 
99  | 0  | static UBool mayHaveParent(char *name) { | 
100  | 0  |     return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr); | 
101  | 0  | }  | 
102  |  |  | 
103  |  | /**  | 
104  |  |  *  Internal function  | 
105  |  |  */  | 
106  | 0  | static void entryIncrease(UResourceDataEntry *entry) { | 
107  | 0  |     Mutex lock(&resbMutex);  | 
108  | 0  |     entry->fCountExisting++;  | 
109  | 0  |     while(entry->fParent != NULL) { | 
110  | 0  |       entry = entry->fParent;  | 
111  | 0  |       entry->fCountExisting++;  | 
112  | 0  |     }  | 
113  | 0  | }  | 
114  |  |  | 
115  |  | /**  | 
116  |  |  *  Internal function. Tries to find a resource in given Resource   | 
117  |  |  *  Bundle, as well as in its parents  | 
118  |  |  */  | 
119  | 0  | static const ResourceData *getFallbackData(const UResourceBundle* resBundle, const char* * resTag, UResourceDataEntry* *realData, Resource *res, UErrorCode *status) { | 
120  | 0  |     UResourceDataEntry *resB = resBundle->fData;  | 
121  | 0  |     int32_t indexR = -1;  | 
122  | 0  |     int32_t i = 0;  | 
123  | 0  |     *res = RES_BOGUS;  | 
124  | 0  |     if(resB != NULL) { | 
125  | 0  |         if(resB->fBogus == U_ZERO_ERROR) { /* if this resource is real, */ | 
126  | 0  |             *res = res_getTableItemByKey(&(resB->fData), resB->fData.rootRes, &indexR, resTag); /* try to get data from there */  | 
127  | 0  |             i++;  | 
128  | 0  |         }  | 
129  | 0  |         if(resBundle->fHasFallback == TRUE) { | 
130  | 0  |             while(*res == RES_BOGUS && resB->fParent != NULL) { /* Otherwise, we'll look in parents */ | 
131  | 0  |                 resB = resB->fParent;  | 
132  | 0  |                 if(resB->fBogus == U_ZERO_ERROR) { | 
133  | 0  |                     i++;  | 
134  | 0  |                     *res = res_getTableItemByKey(&(resB->fData), resB->fData.rootRes, &indexR, resTag);  | 
135  | 0  |                 }  | 
136  | 0  |             }  | 
137  | 0  |         }  | 
138  |  | 
  | 
139  | 0  |         if(*res != RES_BOGUS) { /* If the resource is found in parents, we need to adjust the error */ | 
140  | 0  |             if(i>1) { | 
141  | 0  |                 if(uprv_strcmp(resB->fName, uloc_getDefault())==0 || uprv_strcmp(resB->fName, kRootLocaleName)==0) { | 
142  | 0  |                     *status = U_USING_DEFAULT_WARNING;  | 
143  | 0  |                 } else { | 
144  | 0  |                     *status = U_USING_FALLBACK_WARNING;  | 
145  | 0  |                 }  | 
146  | 0  |             }  | 
147  | 0  |             *realData = resB;  | 
148  | 0  |             return (&(resB->fData));  | 
149  | 0  |         } else { /* If resource is not found, we need to give an error */ | 
150  | 0  |             *status = U_MISSING_RESOURCE_ERROR;  | 
151  | 0  |             return NULL;  | 
152  | 0  |         }  | 
153  | 0  |     } else { | 
154  | 0  |             *status = U_MISSING_RESOURCE_ERROR;  | 
155  | 0  |             return NULL;  | 
156  | 0  |     }  | 
157  | 0  | }  | 
158  |  |  | 
159  |  | static void  | 
160  | 0  | free_entry(UResourceDataEntry *entry) { | 
161  | 0  |     UResourceDataEntry *alias;  | 
162  | 0  |     res_unload(&(entry->fData));  | 
163  | 0  |     if(entry->fName != NULL && entry->fName != entry->fNameBuffer) { | 
164  | 0  |         uprv_free(entry->fName);  | 
165  | 0  |     }  | 
166  | 0  |     if(entry->fPath != NULL) { | 
167  | 0  |         uprv_free(entry->fPath);  | 
168  | 0  |     }  | 
169  | 0  |     if(entry->fPool != NULL) { | 
170  | 0  |         --entry->fPool->fCountExisting;  | 
171  | 0  |     }  | 
172  | 0  |     alias = entry->fAlias;  | 
173  | 0  |     if(alias != NULL) { | 
174  | 0  |         while(alias->fAlias != NULL) { | 
175  | 0  |             alias = alias->fAlias;  | 
176  | 0  |         }  | 
177  | 0  |         --alias->fCountExisting;  | 
178  | 0  |     }  | 
179  | 0  |     uprv_free(entry);  | 
180  | 0  | }  | 
181  |  |  | 
182  |  | /* Works just like ucnv_flushCache() */  | 
183  |  | static int32_t ures_flushCache()  | 
184  | 0  | { | 
185  | 0  |     UResourceDataEntry *resB;  | 
186  | 0  |     int32_t pos;  | 
187  | 0  |     int32_t rbDeletedNum = 0;  | 
188  | 0  |     const UHashElement *e;  | 
189  | 0  |     UBool deletedMore;  | 
190  |  |  | 
191  |  |     /*if shared data hasn't even been lazy evaluated yet  | 
192  |  |     * return 0  | 
193  |  |     */  | 
194  | 0  |     Mutex lock(&resbMutex);  | 
195  | 0  |     if (cache == NULL) { | 
196  | 0  |         return 0;  | 
197  | 0  |     }  | 
198  |  |  | 
199  | 0  |     do { | 
200  | 0  |         deletedMore = FALSE;  | 
201  |  |         /*creates an enumeration to iterate through every element in the table */  | 
202  | 0  |         pos = UHASH_FIRST;  | 
203  | 0  |         while ((e = uhash_nextElement(cache, &pos)) != NULL)  | 
204  | 0  |         { | 
205  | 0  |             resB = (UResourceDataEntry *) e->value.pointer;  | 
206  |  |             /* Deletes only if reference counter == 0  | 
207  |  |              * Don't worry about the children of this node.  | 
208  |  |              * Those will eventually get deleted too, if not already.  | 
209  |  |              * Don't worry about the parents of this node.  | 
210  |  |              * Those will eventually get deleted too, if not already.  | 
211  |  |              */  | 
212  |  |             /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that    */  | 
213  |  |             /* some resource bundles are still open somewhere. */  | 
214  |  | 
  | 
215  | 0  |             if (resB->fCountExisting == 0) { | 
216  | 0  |                 rbDeletedNum++;  | 
217  | 0  |                 deletedMore = TRUE;  | 
218  | 0  |                 uhash_removeElement(cache, e);  | 
219  | 0  |                 free_entry(resB);  | 
220  | 0  |             }  | 
221  | 0  |         }  | 
222  |  |         /*  | 
223  |  |          * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting  | 
224  |  |          * got decremented by free_entry().  | 
225  |  |          */  | 
226  | 0  |     } while(deletedMore);  | 
227  |  | 
  | 
228  | 0  |     return rbDeletedNum;  | 
229  | 0  | }  | 
230  |  |  | 
231  |  | #ifdef URES_DEBUG  | 
232  |  | #include <stdio.h>  | 
233  |  |  | 
234  |  | U_CAPI UBool U_EXPORT2 ures_dumpCacheContents(void) { | 
235  |  |   UBool cacheNotEmpty = FALSE;  | 
236  |  |   int32_t pos = UHASH_FIRST;  | 
237  |  |   const UHashElement *e;  | 
238  |  |   UResourceDataEntry *resB;  | 
239  |  |     | 
240  |  |     Mutex lock(&resbMutex);  | 
241  |  |     if (cache == NULL) { | 
242  |  |       fprintf(stderr,"%s:%d: RB Cache is NULL.\n", __FILE__, __LINE__);  | 
243  |  |       return FALSE;  | 
244  |  |     }  | 
245  |  |  | 
246  |  |     while ((e = uhash_nextElement(cache, &pos)) != NULL) { | 
247  |  |       cacheNotEmpty=TRUE;  | 
248  |  |       resB = (UResourceDataEntry *) e->value.pointer;  | 
249  |  |       fprintf(stderr,"%s:%d: RB Cache: Entry @0x%p, refcount %d, name %s:%s.  Pool 0x%p, alias 0x%p, parent 0x%p\n",  | 
250  |  |               __FILE__, __LINE__,  | 
251  |  |               (void*)resB, resB->fCountExisting,  | 
252  |  |               resB->fName?resB->fName:"NULL",  | 
253  |  |               resB->fPath?resB->fPath:"NULL",  | 
254  |  |               (void*)resB->fPool,  | 
255  |  |               (void*)resB->fAlias,  | 
256  |  |               (void*)resB->fParent);         | 
257  |  |     }  | 
258  |  |       | 
259  |  |     fprintf(stderr,"%s:%d: RB Cache still contains %d items.\n", __FILE__, __LINE__, uhash_count(cache));  | 
260  |  |     return cacheNotEmpty;  | 
261  |  | }  | 
262  |  |  | 
263  |  | #endif  | 
264  |  |  | 
265  |  | static UBool U_CALLCONV ures_cleanup(void)  | 
266  | 0  | { | 
267  | 0  |     if (cache != NULL) { | 
268  | 0  |         ures_flushCache();  | 
269  | 0  |         uhash_close(cache);  | 
270  | 0  |         cache = NULL;  | 
271  | 0  |     }  | 
272  | 0  |     gCacheInitOnce.reset();  | 
273  | 0  |     return TRUE;  | 
274  | 0  | }  | 
275  |  |  | 
276  |  | /** INTERNAL: Initializes the cache for resources */  | 
277  | 0  | static void U_CALLCONV createCache(UErrorCode &status) { | 
278  | 0  |     U_ASSERT(cache == NULL);  | 
279  | 0  |     cache = uhash_open(hashEntry, compareEntries, NULL, &status);  | 
280  | 0  |     ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);  | 
281  | 0  | }  | 
282  |  |        | 
283  | 0  | static void initCache(UErrorCode *status) { | 
284  | 0  |     umtx_initOnce(gCacheInitOnce, &createCache, *status);  | 
285  | 0  | }  | 
286  |  |  | 
287  |  | /** INTERNAL: sets the name (locale) of the resource bundle to given name */  | 
288  |  |  | 
289  | 0  | static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) { | 
290  | 0  |     int32_t len = (int32_t)uprv_strlen(name);  | 
291  | 0  |     if(res->fName != NULL && res->fName != res->fNameBuffer) { | 
292  | 0  |         uprv_free(res->fName);  | 
293  | 0  |     }  | 
294  | 0  |     if (len < (int32_t)sizeof(res->fNameBuffer)) { | 
295  | 0  |         res->fName = res->fNameBuffer;  | 
296  | 0  |     }  | 
297  | 0  |     else { | 
298  | 0  |         res->fName = (char *)uprv_malloc(len+1);  | 
299  | 0  |     }  | 
300  | 0  |     if(res->fName == NULL) { | 
301  | 0  |         *status = U_MEMORY_ALLOCATION_ERROR;  | 
302  | 0  |     } else { | 
303  | 0  |         uprv_strcpy(res->fName, name);  | 
304  | 0  |     }  | 
305  | 0  | }  | 
306  |  |  | 
307  |  | static UResourceDataEntry *  | 
308  |  | getPoolEntry(const char *path, UErrorCode *status);  | 
309  |  |  | 
310  |  | /**  | 
311  |  |  *  INTERNAL: Inits and opens an entry from a data DLL.  | 
312  |  |  *    CAUTION:  resbMutex must be locked when calling this function.  | 
313  |  |  */  | 
314  | 0  | static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) { | 
315  | 0  |     UResourceDataEntry *r = NULL;  | 
316  | 0  |     UResourceDataEntry find;  | 
317  |  |     /*int32_t hashValue;*/  | 
318  | 0  |     const char *name;  | 
319  | 0  |     char aliasName[100] = { 0 }; | 
320  | 0  |     int32_t aliasLen = 0;  | 
321  |  |     /*UBool isAlias = FALSE;*/  | 
322  |  |     /*UHashTok hashkey; */  | 
323  |  | 
  | 
324  | 0  |     if(U_FAILURE(*status)) { | 
325  | 0  |         return NULL;  | 
326  | 0  |     }  | 
327  |  |  | 
328  |  |     /* here we try to deduce the right locale name */  | 
329  | 0  |     if(localeID == NULL) { /* if localeID is NULL, we're trying to open default locale */ | 
330  | 0  |         name = uloc_getDefault();  | 
331  | 0  |     } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */ | 
332  | 0  |         name = kRootLocaleName;  | 
333  | 0  |     } else { /* otherwise, we'll open what we're given */ | 
334  | 0  |         name = localeID;  | 
335  | 0  |     }  | 
336  |  | 
  | 
337  | 0  |     find.fName = (char *)name;  | 
338  | 0  |     find.fPath = (char *)path;  | 
339  |  |  | 
340  |  |     /* calculate the hash value of the entry */  | 
341  |  |     /*hashkey.pointer = (void *)&find;*/  | 
342  |  |     /*hashValue = hashEntry(hashkey);*/  | 
343  |  |  | 
344  |  |     /* check to see if we already have this entry */  | 
345  | 0  |     r = (UResourceDataEntry *)uhash_get(cache, &find);  | 
346  | 0  |     if(r == NULL) { | 
347  |  |         /* if the entry is not yet in the hash table, we'll try to construct a new one */  | 
348  | 0  |         r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry));  | 
349  | 0  |         if(r == NULL) { | 
350  | 0  |             *status = U_MEMORY_ALLOCATION_ERROR;  | 
351  | 0  |             return NULL;  | 
352  | 0  |         }  | 
353  |  |  | 
354  | 0  |         uprv_memset(r, 0, sizeof(UResourceDataEntry));  | 
355  |  |         /*r->fHashKey = hashValue;*/  | 
356  |  | 
  | 
357  | 0  |         setEntryName(r, name, status);  | 
358  | 0  |         if (U_FAILURE(*status)) { | 
359  | 0  |             uprv_free(r);  | 
360  | 0  |             return NULL;  | 
361  | 0  |         }  | 
362  |  |  | 
363  | 0  |         if(path != NULL) { | 
364  | 0  |             r->fPath = (char *)uprv_strdup(path);  | 
365  | 0  |             if(r->fPath == NULL) { | 
366  | 0  |                 *status = U_MEMORY_ALLOCATION_ERROR;  | 
367  | 0  |                 uprv_free(r);  | 
368  | 0  |                 return NULL;  | 
369  | 0  |             }  | 
370  | 0  |         }  | 
371  |  |  | 
372  |  |         /* this is the actual loading */  | 
373  | 0  |         res_load(&(r->fData), r->fPath, r->fName, status);  | 
374  |  | 
  | 
375  | 0  |         if (U_FAILURE(*status)) { | 
376  |  |             /* if we failed to load due to an out-of-memory error, exit early. */  | 
377  | 0  |             if (*status == U_MEMORY_ALLOCATION_ERROR) { | 
378  | 0  |                 uprv_free(r);  | 
379  | 0  |                 return NULL;  | 
380  | 0  |             }  | 
381  |  |             /* we have no such entry in dll, so it will always use fallback */  | 
382  | 0  |             *status = U_USING_FALLBACK_WARNING;  | 
383  | 0  |             r->fBogus = U_USING_FALLBACK_WARNING;  | 
384  | 0  |         } else { /* if we have a regular entry */ | 
385  | 0  |             Resource aliasres;  | 
386  | 0  |             if (r->fData.usesPoolBundle) { | 
387  | 0  |                 r->fPool = getPoolEntry(r->fPath, status);  | 
388  | 0  |                 if (U_SUCCESS(*status)) { | 
389  | 0  |                     const int32_t *poolIndexes = r->fPool->fData.pRoot + 1;  | 
390  | 0  |                     if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) { | 
391  | 0  |                         r->fData.poolBundleKeys = (const char *)(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));  | 
392  | 0  |                         r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits;  | 
393  | 0  |                     } else { | 
394  | 0  |                         r->fBogus = *status = U_INVALID_FORMAT_ERROR;  | 
395  | 0  |                     }  | 
396  | 0  |                 } else { | 
397  | 0  |                     r->fBogus = *status;  | 
398  | 0  |                 }  | 
399  | 0  |             }  | 
400  | 0  |             if (U_SUCCESS(*status)) { | 
401  |  |                 /* handle the alias by trying to get out the %%Alias tag.*/  | 
402  |  |                 /* We'll try to get alias string from the bundle */  | 
403  | 0  |                 aliasres = res_getResource(&(r->fData), "%%ALIAS");  | 
404  | 0  |                 if (aliasres != RES_BOGUS) { | 
405  |  |                     // No tracing: called during initial data loading  | 
406  | 0  |                     const UChar *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen);  | 
407  | 0  |                     if(alias != NULL && aliasLen > 0) { /* if there is actual alias - unload and load new data */ | 
408  | 0  |                         u_UCharsToChars(alias, aliasName, aliasLen+1);  | 
409  | 0  |                         r->fAlias = init_entry(aliasName, path, status);  | 
410  | 0  |                     }  | 
411  | 0  |                 }  | 
412  | 0  |             }  | 
413  | 0  |         }  | 
414  |  |  | 
415  | 0  |         { | 
416  | 0  |             UResourceDataEntry *oldR = NULL;  | 
417  | 0  |             if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == NULL) { /* if the data is not cached */ | 
418  |  |                 /* just insert it in the cache */  | 
419  | 0  |                 UErrorCode cacheStatus = U_ZERO_ERROR;  | 
420  | 0  |                 uhash_put(cache, (void *)r, r, &cacheStatus);  | 
421  | 0  |                 if (U_FAILURE(cacheStatus)) { | 
422  | 0  |                     *status = cacheStatus;  | 
423  | 0  |                     free_entry(r);  | 
424  | 0  |                     r = NULL;  | 
425  | 0  |                 }  | 
426  | 0  |             } else { | 
427  |  |                 /* somebody have already inserted it while we were working, discard newly opened data */  | 
428  |  |                 /* Also, we could get here IF we opened an alias */  | 
429  | 0  |                 free_entry(r);  | 
430  | 0  |                 r = oldR;  | 
431  | 0  |             }  | 
432  | 0  |         }  | 
433  |  | 
  | 
434  | 0  |     }  | 
435  | 0  |     if(r != NULL) { | 
436  |  |         /* return the real bundle */  | 
437  | 0  |         while(r->fAlias != NULL) { | 
438  | 0  |             r = r->fAlias;  | 
439  | 0  |         }  | 
440  | 0  |         r->fCountExisting++; /* we increase its reference count */  | 
441  |  |         /* if the resource has a warning */  | 
442  |  |         /* we don't want to overwrite a status with no error */  | 
443  | 0  |         if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) { | 
444  | 0  |              *status = r->fBogus; /* set the returning status */  | 
445  | 0  |         }  | 
446  | 0  |     }  | 
447  | 0  |     return r;  | 
448  | 0  | }  | 
449  |  |  | 
450  |  | static UResourceDataEntry *  | 
451  | 0  | getPoolEntry(const char *path, UErrorCode *status) { | 
452  | 0  |     UResourceDataEntry *poolBundle = init_entry(kPoolBundleName, path, status);  | 
453  | 0  |     if( U_SUCCESS(*status) &&  | 
454  | 0  |         (poolBundle == NULL || poolBundle->fBogus != U_ZERO_ERROR || !poolBundle->fData.isPoolBundle)  | 
455  | 0  |     ) { | 
456  | 0  |         *status = U_INVALID_FORMAT_ERROR;  | 
457  | 0  |     }  | 
458  | 0  |     return poolBundle;  | 
459  | 0  | }  | 
460  |  |  | 
461  |  | /* INTERNAL: */  | 
462  |  | /*   CAUTION:  resbMutex must be locked when calling this function! */  | 
463  |  | static UResourceDataEntry *  | 
464  |  | findFirstExisting(const char* path, char* name,  | 
465  | 0  |                   UBool *isRoot, UBool *hasChopped, UBool *isDefault, UErrorCode* status) { | 
466  | 0  |     UResourceDataEntry *r = NULL;  | 
467  | 0  |     UBool hasRealData = FALSE;  | 
468  | 0  |     const char *defaultLoc = uloc_getDefault();  | 
469  | 0  |     *hasChopped = TRUE; /* we're starting with a fresh name */  | 
470  |  | 
  | 
471  | 0  |     while(*hasChopped && !hasRealData) { | 
472  | 0  |         r = init_entry(name, path, status);  | 
473  |  |         /* Null pointer test */  | 
474  | 0  |         if (U_FAILURE(*status)) { | 
475  | 0  |             return NULL;  | 
476  | 0  |         }  | 
477  | 0  |         *isDefault = (UBool)(uprv_strncmp(name, defaultLoc, uprv_strlen(name)) == 0);  | 
478  | 0  |         hasRealData = (UBool)(r->fBogus == U_ZERO_ERROR);  | 
479  | 0  |         if(!hasRealData) { | 
480  |  |             /* this entry is not real. We will discard it. */  | 
481  |  |             /* However, the parent line for this entry is  */  | 
482  |  |             /* not to be used - as there might be parent   */  | 
483  |  |             /* lines in cache from previous openings that  */  | 
484  |  |             /* are not updated yet. */  | 
485  | 0  |             r->fCountExisting--;  | 
486  |  |             /*entryCloseInt(r);*/  | 
487  | 0  |             r = NULL;  | 
488  | 0  |             *status = U_USING_FALLBACK_WARNING;  | 
489  | 0  |         } else { | 
490  | 0  |             uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */  | 
491  | 0  |         }  | 
492  |  | 
  | 
493  | 0  |         *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0);  | 
494  |  |  | 
495  |  |         /*Fallback data stuff*/  | 
496  | 0  |         *hasChopped = chopLocale(name);  | 
497  | 0  |         if (*hasChopped && *name == '\0') { | 
498  | 0  |             uprv_strcpy(name, "und");  | 
499  | 0  |         }  | 
500  | 0  |     }  | 
501  | 0  |     return r;  | 
502  | 0  | }  | 
503  |  |  | 
504  | 0  | static void ures_setIsStackObject( UResourceBundle* resB, UBool state) { | 
505  | 0  |     if(state) { | 
506  | 0  |         resB->fMagic1 = 0;  | 
507  | 0  |         resB->fMagic2 = 0;  | 
508  | 0  |     } else { | 
509  | 0  |         resB->fMagic1 = MAGIC1;  | 
510  | 0  |         resB->fMagic2 = MAGIC2;  | 
511  | 0  |     }  | 
512  | 0  | }  | 
513  |  |  | 
514  | 0  | static UBool ures_isStackObject(const UResourceBundle* resB) { | 
515  | 0  |   return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?FALSE:TRUE);  | 
516  | 0  | }  | 
517  |  |  | 
518  |  |  | 
519  | 0  | U_CFUNC void ures_initStackObject(UResourceBundle* resB) { | 
520  | 0  |   uprv_memset(resB, 0, sizeof(UResourceBundle));  | 
521  | 0  |   ures_setIsStackObject(resB, TRUE);  | 
522  | 0  | }  | 
523  |  |  | 
524  |  | U_NAMESPACE_BEGIN  | 
525  |  |  | 
526  | 0  | StackUResourceBundle::StackUResourceBundle() { | 
527  | 0  |     ures_initStackObject(&bundle);  | 
528  | 0  | }  | 
529  |  |  | 
530  | 0  | StackUResourceBundle::~StackUResourceBundle() { | 
531  | 0  |     ures_close(&bundle);  | 
532  | 0  | }  | 
533  |  |  | 
534  |  | U_NAMESPACE_END  | 
535  |  |  | 
536  |  | static UBool  // returns U_SUCCESS(*status)  | 
537  |  | loadParentsExceptRoot(UResourceDataEntry *&t1,  | 
538  |  |                       char name[], int32_t nameCapacity,  | 
539  | 0  |                       UBool usingUSRData, char usrDataPath[], UErrorCode *status) { | 
540  | 0  |     if (U_FAILURE(*status)) { return FALSE; } | 
541  | 0  |     UBool checkParent = TRUE;  | 
542  | 0  |     while (checkParent && t1->fParent == NULL && !t1->fData.noFallback &&  | 
543  | 0  |             res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) { | 
544  | 0  |         Resource parentRes = res_getResource(&t1->fData, "%%Parent");  | 
545  | 0  |         if (parentRes != RES_BOGUS) {  // An explicit parent was found. | 
546  | 0  |             int32_t parentLocaleLen = 0;  | 
547  |  |             // No tracing: called during initial data loading  | 
548  | 0  |             const UChar *parentLocaleName = res_getStringNoTrace(&(t1->fData), parentRes, &parentLocaleLen);  | 
549  | 0  |             if(parentLocaleName != NULL && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) { | 
550  | 0  |                 u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1);  | 
551  | 0  |                 if (uprv_strcmp(name, kRootLocaleName) == 0) { | 
552  | 0  |                     return TRUE;  | 
553  | 0  |                 }  | 
554  | 0  |             }  | 
555  | 0  |         }  | 
556  |  |         // Insert regular parents.  | 
557  | 0  |         UErrorCode parentStatus = U_ZERO_ERROR;  | 
558  | 0  |         UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus);  | 
559  | 0  |         if (U_FAILURE(parentStatus)) { | 
560  | 0  |             *status = parentStatus;  | 
561  | 0  |             return FALSE;  | 
562  | 0  |         }  | 
563  | 0  |         UResourceDataEntry *u2 = NULL;  | 
564  | 0  |         UErrorCode usrStatus = U_ZERO_ERROR;  | 
565  | 0  |         if (usingUSRData) {  // This code inserts user override data into the inheritance chain. | 
566  | 0  |             u2 = init_entry(name, usrDataPath, &usrStatus);  | 
567  |  |             // If we failed due to out-of-memory, report that to the caller and exit early.  | 
568  | 0  |             if (usrStatus == U_MEMORY_ALLOCATION_ERROR) { | 
569  | 0  |                 *status = usrStatus;  | 
570  | 0  |                 return FALSE;  | 
571  | 0  |             }  | 
572  | 0  |         }  | 
573  |  |  | 
574  | 0  |         if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) { | 
575  | 0  |             t1->fParent = u2;  | 
576  | 0  |             u2->fParent = t2;  | 
577  | 0  |         } else { | 
578  | 0  |             t1->fParent = t2;  | 
579  | 0  |             if (usingUSRData) { | 
580  |  |                 // The USR override data wasn't found, set it to be deleted.  | 
581  | 0  |                 u2->fCountExisting = 0;  | 
582  | 0  |             }  | 
583  | 0  |         }  | 
584  | 0  |         t1 = t2;  | 
585  | 0  |         checkParent = chopLocale(name) || mayHaveParent(name);  | 
586  | 0  |     }  | 
587  | 0  |     return TRUE;  | 
588  | 0  | }  | 
589  |  |  | 
590  |  | static UBool  // returns U_SUCCESS(*status)  | 
591  | 0  | insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) { | 
592  | 0  |     if (U_FAILURE(*status)) { return FALSE; } | 
593  | 0  |     UErrorCode parentStatus = U_ZERO_ERROR;  | 
594  | 0  |     UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);  | 
595  | 0  |     if (U_FAILURE(parentStatus)) { | 
596  | 0  |         *status = parentStatus;  | 
597  | 0  |         return FALSE;  | 
598  | 0  |     }  | 
599  | 0  |     t1->fParent = t2;  | 
600  | 0  |     t1 = t2;  | 
601  | 0  |     return TRUE;  | 
602  | 0  | }  | 
603  |  |  | 
604  |  | enum UResOpenType { | 
605  |  |     /**  | 
606  |  |      * Open a resource bundle for the locale;  | 
607  |  |      * if there is not even a base language bundle, then fall back to the default locale;  | 
608  |  |      * if there is no bundle for that either, then load the root bundle.  | 
609  |  |      *  | 
610  |  |      * This is the default bundle loading behavior.  | 
611  |  |      */  | 
612  |  |     URES_OPEN_LOCALE_DEFAULT_ROOT,  | 
613  |  |     // TODO: ICU ticket #11271 "consistent default locale across locale trees"  | 
614  |  |     // Add an option to look at the main locale tree for whether to  | 
615  |  |     // fall back to root directly (if the locale has main data) or  | 
616  |  |     // fall back to the default locale first (if the locale does not even have main data).  | 
617  |  |     /**  | 
618  |  |      * Open a resource bundle for the locale;  | 
619  |  |      * if there is not even a base language bundle, then load the root bundle;  | 
620  |  |      * never fall back to the default locale.  | 
621  |  |      *  | 
622  |  |      * This is used for algorithms that have good pan-Unicode default behavior,  | 
623  |  |      * such as case mappings, collation, and segmentation (BreakIterator).  | 
624  |  |      */  | 
625  |  |     URES_OPEN_LOCALE_ROOT,  | 
626  |  |     /**  | 
627  |  |      * Open a resource bundle for the exact bundle name as requested;  | 
628  |  |      * no fallbacks, do not load parent bundles.  | 
629  |  |      *  | 
630  |  |      * This is used for supplemental (non-locale) data.  | 
631  |  |      */  | 
632  |  |     URES_OPEN_DIRECT  | 
633  |  | };  | 
634  |  | typedef enum UResOpenType UResOpenType;  | 
635  |  |  | 
636  |  | static UResourceDataEntry *entryOpen(const char* path, const char* localeID,  | 
637  | 0  |                                      UResOpenType openType, UErrorCode* status) { | 
638  | 0  |     U_ASSERT(openType != URES_OPEN_DIRECT);  | 
639  | 0  |     UErrorCode intStatus = U_ZERO_ERROR;  | 
640  | 0  |     UResourceDataEntry *r = NULL;  | 
641  | 0  |     UResourceDataEntry *t1 = NULL;  | 
642  | 0  |     UBool isDefault = FALSE;  | 
643  | 0  |     UBool isRoot = FALSE;  | 
644  | 0  |     UBool hasRealData = FALSE;  | 
645  | 0  |     UBool hasChopped = TRUE;  | 
646  | 0  |     UBool usingUSRData = U_USE_USRDATA && ( path == NULL || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0);  | 
647  |  | 
  | 
648  | 0  |     char name[ULOC_FULLNAME_CAPACITY];  | 
649  | 0  |     char usrDataPath[96];  | 
650  |  | 
  | 
651  | 0  |     initCache(status);  | 
652  |  | 
  | 
653  | 0  |     if(U_FAILURE(*status)) { | 
654  | 0  |         return NULL;  | 
655  | 0  |     }  | 
656  |  |  | 
657  | 0  |     uprv_strncpy(name, localeID, sizeof(name) - 1);  | 
658  | 0  |     name[sizeof(name) - 1] = 0;  | 
659  |  | 
  | 
660  | 0  |     if ( usingUSRData ) { | 
661  | 0  |         if ( path == NULL ) { | 
662  | 0  |             uprv_strcpy(usrDataPath, U_USRDATA_NAME);  | 
663  | 0  |         } else { | 
664  | 0  |             uprv_strncpy(usrDataPath, path, sizeof(usrDataPath) - 1);  | 
665  | 0  |             usrDataPath[0] = 'u';  | 
666  | 0  |             usrDataPath[1] = 's';  | 
667  | 0  |             usrDataPath[2] = 'r';  | 
668  | 0  |             usrDataPath[sizeof(usrDataPath) - 1] = 0;  | 
669  | 0  |         }  | 
670  | 0  |     }  | 
671  |  |    | 
672  | 0  |     Mutex lock(&resbMutex);    // Lock resbMutex until the end of this function.  | 
673  |  |  | 
674  |  |     /* We're going to skip all the locales that do not have any data */  | 
675  | 0  |     r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);  | 
676  |  |  | 
677  |  |     // If we failed due to out-of-memory, report the failure and exit early.  | 
678  | 0  |     if (intStatus == U_MEMORY_ALLOCATION_ERROR) { | 
679  | 0  |         *status = intStatus;  | 
680  | 0  |         goto finish;  | 
681  | 0  |     }  | 
682  |  |  | 
683  | 0  |     if(r != NULL) { /* if there is one real locale, we can look for parents. */ | 
684  | 0  |         t1 = r;  | 
685  | 0  |         hasRealData = TRUE;  | 
686  | 0  |         if ( usingUSRData ) {  /* This code inserts user override data into the inheritance chain */ | 
687  | 0  |             UErrorCode usrStatus = U_ZERO_ERROR;  | 
688  | 0  |             UResourceDataEntry *u1 = init_entry(t1->fName, usrDataPath, &usrStatus);  | 
689  |  |             // If we failed due to out-of-memory, report the failure and exit early.  | 
690  | 0  |             if (intStatus == U_MEMORY_ALLOCATION_ERROR) { | 
691  | 0  |                 *status = intStatus;  | 
692  | 0  |                 goto finish;  | 
693  | 0  |             }  | 
694  | 0  |             if ( u1 != NULL ) { | 
695  | 0  |                 if(u1->fBogus == U_ZERO_ERROR) { | 
696  | 0  |                     u1->fParent = t1;  | 
697  | 0  |                     r = u1;  | 
698  | 0  |                 } else { | 
699  |  |                     /* the USR override data wasn't found, set it to be deleted */  | 
700  | 0  |                     u1->fCountExisting = 0;  | 
701  | 0  |                 }  | 
702  | 0  |             }  | 
703  | 0  |         }  | 
704  | 0  |         if ((hasChopped || mayHaveParent(name)) && !isRoot) { | 
705  | 0  |             if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { | 
706  | 0  |                 goto finish;  | 
707  | 0  |             }  | 
708  | 0  |         }  | 
709  | 0  |     }  | 
710  |  |  | 
711  |  |     /* we could have reached this point without having any real data */  | 
712  |  |     /* if that is the case, we need to chain in the default locale   */  | 
713  | 0  |     if(r==NULL && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) { | 
714  |  |         /* insert default locale */  | 
715  | 0  |         uprv_strcpy(name, uloc_getDefault());  | 
716  | 0  |         r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);  | 
717  |  |         // If we failed due to out-of-memory, report the failure and exit early.  | 
718  | 0  |         if (intStatus == U_MEMORY_ALLOCATION_ERROR) { | 
719  | 0  |             *status = intStatus;  | 
720  | 0  |             goto finish;  | 
721  | 0  |         }  | 
722  | 0  |         intStatus = U_USING_DEFAULT_WARNING;  | 
723  | 0  |         if(r != NULL) { /* the default locale exists */ | 
724  | 0  |             t1 = r;  | 
725  | 0  |             hasRealData = TRUE;  | 
726  | 0  |             isDefault = TRUE;  | 
727  |  |             // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path? | 
728  | 0  |             if ((hasChopped || mayHaveParent(name)) && !isRoot) { | 
729  | 0  |                 if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) { | 
730  | 0  |                     goto finish;  | 
731  | 0  |                 }  | 
732  | 0  |             }  | 
733  | 0  |         }  | 
734  | 0  |     }  | 
735  |  |  | 
736  |  |     /* we could still have r == NULL at this point - maybe even default locale is not */  | 
737  |  |     /* present */  | 
738  | 0  |     if(r == NULL) { | 
739  | 0  |         uprv_strcpy(name, kRootLocaleName);  | 
740  | 0  |         r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);  | 
741  |  |         // If we failed due to out-of-memory, report the failure and exit early.  | 
742  | 0  |         if (intStatus == U_MEMORY_ALLOCATION_ERROR) { | 
743  | 0  |             *status = intStatus;  | 
744  | 0  |             goto finish;  | 
745  | 0  |         }  | 
746  | 0  |         if(r != NULL) { | 
747  | 0  |             t1 = r;  | 
748  | 0  |             intStatus = U_USING_DEFAULT_WARNING;  | 
749  | 0  |             hasRealData = TRUE;  | 
750  | 0  |         } else { /* we don't even have the root locale */ | 
751  | 0  |             *status = U_MISSING_RESOURCE_ERROR;  | 
752  | 0  |             goto finish;  | 
753  | 0  |         }  | 
754  | 0  |     } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&  | 
755  | 0  |             t1->fParent == NULL && !r->fData.noFallback) { | 
756  | 0  |         if (!insertRootBundle(t1, status)) { | 
757  | 0  |             goto finish;  | 
758  | 0  |         }  | 
759  | 0  |         if(!hasRealData) { | 
760  | 0  |             r->fBogus = U_USING_DEFAULT_WARNING;  | 
761  | 0  |         }  | 
762  | 0  |     }  | 
763  |  |  | 
764  |  |     // TODO: Does this ever loop?  | 
765  | 0  |     while(r != NULL && !isRoot && t1->fParent != NULL) { | 
766  | 0  |         t1->fParent->fCountExisting++;  | 
767  | 0  |         t1 = t1->fParent;  | 
768  | 0  |     }  | 
769  |  | 
  | 
770  | 0  | finish:  | 
771  | 0  |     if(U_SUCCESS(*status)) { | 
772  | 0  |         if(intStatus != U_ZERO_ERROR) { | 
773  | 0  |             *status = intStatus;    | 
774  | 0  |         }  | 
775  | 0  |         return r;  | 
776  | 0  |     } else { | 
777  | 0  |         return NULL;  | 
778  | 0  |     }  | 
779  | 0  | }  | 
780  |  |  | 
781  |  | /**  | 
782  |  |  * Version of entryOpen() and findFirstExisting() for ures_openDirect(),  | 
783  |  |  * with no fallbacks.  | 
784  |  |  * Parent and root locale bundles are loaded if  | 
785  |  |  * the requested bundle does not have the "nofallback" flag.  | 
786  |  |  */  | 
787  |  | static UResourceDataEntry *  | 
788  | 0  | entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) { | 
789  | 0  |     initCache(status);  | 
790  | 0  |     if(U_FAILURE(*status)) { | 
791  | 0  |         return NULL;  | 
792  | 0  |     }  | 
793  |  |  | 
794  | 0  |     Mutex lock(&resbMutex);  | 
795  |  |     // findFirstExisting() without fallbacks.  | 
796  | 0  |     UResourceDataEntry *r = init_entry(localeID, path, status);  | 
797  | 0  |     if(U_SUCCESS(*status)) { | 
798  | 0  |         if(r->fBogus != U_ZERO_ERROR) { | 
799  | 0  |             r->fCountExisting--;  | 
800  | 0  |             r = NULL;  | 
801  | 0  |         }  | 
802  | 0  |     } else { | 
803  | 0  |         r = NULL;  | 
804  | 0  |     }  | 
805  |  |  | 
806  |  |     // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,  | 
807  |  |     // unless it is marked with "nofallback".  | 
808  | 0  |     UResourceDataEntry *t1 = r;  | 
809  | 0  |     if(r != NULL && uprv_strcmp(localeID, kRootLocaleName) != 0 &&  // not root  | 
810  | 0  |             r->fParent == NULL && !r->fData.noFallback &&  | 
811  | 0  |             uprv_strlen(localeID) < ULOC_FULLNAME_CAPACITY) { | 
812  | 0  |         char name[ULOC_FULLNAME_CAPACITY];  | 
813  | 0  |         uprv_strcpy(name, localeID);  | 
814  | 0  |         if(!chopLocale(name) || uprv_strcmp(name, kRootLocaleName) == 0 ||  | 
815  | 0  |                 loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), FALSE, NULL, status)) { | 
816  | 0  |             if(uprv_strcmp(t1->fName, kRootLocaleName) != 0 && t1->fParent == NULL) { | 
817  | 0  |                 insertRootBundle(t1, status);  | 
818  | 0  |             }  | 
819  | 0  |         }  | 
820  | 0  |         if(U_FAILURE(*status)) { | 
821  | 0  |             r = NULL;  | 
822  | 0  |         }  | 
823  | 0  |     }  | 
824  |  | 
  | 
825  | 0  |     if(r != NULL) { | 
826  |  |         // TODO: Does this ever loop?  | 
827  | 0  |         while(t1->fParent != NULL) { | 
828  | 0  |             t1->fParent->fCountExisting++;  | 
829  | 0  |             t1 = t1->fParent;  | 
830  | 0  |         }  | 
831  | 0  |     }  | 
832  | 0  |     return r;  | 
833  | 0  | }  | 
834  |  |  | 
835  |  | /**  | 
836  |  |  * Functions to create and destroy resource bundles.  | 
837  |  |  *     CAUTION:  resbMutex must be locked when calling this function.  | 
838  |  |  */  | 
839  |  | /* INTERNAL: */  | 
840  | 0  | static void entryCloseInt(UResourceDataEntry *resB) { | 
841  | 0  |     UResourceDataEntry *p = resB;  | 
842  |  | 
  | 
843  | 0  |     while(resB != NULL) { | 
844  | 0  |         p = resB->fParent;  | 
845  | 0  |         resB->fCountExisting--;  | 
846  |  |  | 
847  |  |         /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush  | 
848  |  |          of the cache. */  | 
849  |  | /*  | 
850  |  |         if(resB->fCountExisting <= 0) { | 
851  |  |             uhash_remove(cache, resB);  | 
852  |  |             if(resB->fBogus == U_ZERO_ERROR) { | 
853  |  |                 res_unload(&(resB->fData));  | 
854  |  |             }  | 
855  |  |             if(resB->fName != NULL) { | 
856  |  |                 uprv_free(resB->fName);  | 
857  |  |             }  | 
858  |  |             if(resB->fPath != NULL) { | 
859  |  |                 uprv_free(resB->fPath);  | 
860  |  |             }  | 
861  |  |             uprv_free(resB);  | 
862  |  |         }  | 
863  |  | */  | 
864  |  | 
  | 
865  | 0  |         resB = p;  | 
866  | 0  |     }  | 
867  | 0  | }  | 
868  |  |  | 
869  |  | /**   | 
870  |  |  *  API: closes a resource bundle and cleans up.  | 
871  |  |  */  | 
872  |  |  | 
873  | 0  | static void entryClose(UResourceDataEntry *resB) { | 
874  | 0  |   Mutex lock(&resbMutex);  | 
875  | 0  |   entryCloseInt(resB);  | 
876  | 0  | }  | 
877  |  |  | 
878  |  | /*  | 
879  |  | U_CFUNC void ures_setResPath(UResourceBundle *resB, const char* toAdd) { | 
880  |  |   if(resB->fResPath == NULL) { | 
881  |  |     resB->fResPath = resB->fResBuf;  | 
882  |  |     *(resB->fResPath) = 0;  | 
883  |  |   }   | 
884  |  |   resB->fResPathLen = uprv_strlen(toAdd);  | 
885  |  |   if(RES_BUFSIZE <= resB->fResPathLen+1) { | 
886  |  |     if(resB->fResPath == resB->fResBuf) { | 
887  |  |       resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));  | 
888  |  |     } else { | 
889  |  |       resB->fResPath = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));  | 
890  |  |     }  | 
891  |  |   }  | 
892  |  |   uprv_strcpy(resB->fResPath, toAdd);  | 
893  |  | }  | 
894  |  | */  | 
895  | 0  | static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) { | 
896  | 0  |     int32_t resPathLenOrig = resB->fResPathLen;  | 
897  | 0  |     if(resB->fResPath == NULL) { | 
898  | 0  |         resB->fResPath = resB->fResBuf;  | 
899  | 0  |         *(resB->fResPath) = 0;  | 
900  | 0  |         resB->fResPathLen = 0;  | 
901  | 0  |     }   | 
902  | 0  |     resB->fResPathLen += lenToAdd;  | 
903  | 0  |     if(RES_BUFSIZE <= resB->fResPathLen+1) { | 
904  | 0  |         if(resB->fResPath == resB->fResBuf) { | 
905  | 0  |             resB->fResPath = (char *)uprv_malloc((resB->fResPathLen+1)*sizeof(char));  | 
906  |  |             /* Check that memory was allocated correctly. */  | 
907  | 0  |             if (resB->fResPath == NULL) { | 
908  | 0  |                 *status = U_MEMORY_ALLOCATION_ERROR;  | 
909  | 0  |                 return;  | 
910  | 0  |             }  | 
911  | 0  |             uprv_strcpy(resB->fResPath, resB->fResBuf);  | 
912  | 0  |         } else { | 
913  | 0  |             char *temp = (char *)uprv_realloc(resB->fResPath, (resB->fResPathLen+1)*sizeof(char));  | 
914  |  |             /* Check that memory was reallocated correctly. */  | 
915  | 0  |             if (temp == NULL) { | 
916  | 0  |                 *status = U_MEMORY_ALLOCATION_ERROR;  | 
917  | 0  |                 return;  | 
918  | 0  |             }  | 
919  | 0  |             resB->fResPath = temp;  | 
920  | 0  |         }  | 
921  | 0  |     }  | 
922  | 0  |     uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd);  | 
923  | 0  | }  | 
924  |  |  | 
925  | 0  | static void ures_freeResPath(UResourceBundle *resB) { | 
926  | 0  |     if (resB->fResPath && resB->fResPath != resB->fResBuf) { | 
927  | 0  |         uprv_free(resB->fResPath);  | 
928  | 0  |     }  | 
929  | 0  |     resB->fResPath = NULL;  | 
930  | 0  |     resB->fResPathLen = 0;  | 
931  | 0  | }  | 
932  |  |  | 
933  |  | static void  | 
934  |  | ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj)  | 
935  | 0  | { | 
936  | 0  |     if(resB != NULL) { | 
937  | 0  |         if(resB->fData != NULL) { | 
938  | 0  |             entryClose(resB->fData);  | 
939  | 0  |         }  | 
940  | 0  |         if(resB->fVersion != NULL) { | 
941  | 0  |             uprv_free(resB->fVersion);  | 
942  | 0  |         }  | 
943  | 0  |         ures_freeResPath(resB);  | 
944  |  | 
  | 
945  | 0  |         if(ures_isStackObject(resB) == FALSE && freeBundleObj) { | 
946  | 0  |             uprv_free(resB);  | 
947  | 0  |         }  | 
948  |  | #if 0 /*U_DEBUG*/  | 
949  |  |         else { | 
950  |  |             /* poison the data */  | 
951  |  |             uprv_memset(resB, -1, sizeof(UResourceBundle));  | 
952  |  |         }  | 
953  |  | #endif  | 
954  | 0  |     }  | 
955  | 0  | }  | 
956  |  |  | 
957  |  | U_CAPI void  U_EXPORT2  | 
958  |  | ures_close(UResourceBundle* resB)  | 
959  | 0  | { | 
960  | 0  |     ures_closeBundle(resB, TRUE);  | 
961  | 0  | }  | 
962  |  |  | 
963  |  | static UResourceBundle *init_resb_result(const ResourceData *rdata, Resource r,   | 
964  |  |                                          const char *key, int32_t idx, UResourceDataEntry *realData,   | 
965  |  |                                          const UResourceBundle *parent, int32_t noAlias,  | 
966  |  |                                          UResourceBundle *resB, UErrorCode *status)   | 
967  | 0  | { | 
968  | 0  |     if(status == NULL || U_FAILURE(*status)) { | 
969  | 0  |         return resB;  | 
970  | 0  |     }  | 
971  | 0  |     if (parent == NULL) { | 
972  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
973  | 0  |         return NULL;  | 
974  | 0  |     }  | 
975  | 0  |     if(RES_GET_TYPE(r) == URES_ALIAS) { /* This is an alias, need to exchange with real data */ | 
976  | 0  |         if(noAlias < URES_MAX_ALIAS_LEVEL) {  | 
977  | 0  |             int32_t len = 0;  | 
978  | 0  |             const UChar *alias = res_getAlias(rdata, r, &len);   | 
979  | 0  |             if(len > 0) { | 
980  |  |                 /* we have an alias, now let's cut it up */  | 
981  | 0  |                 char stackAlias[200];  | 
982  | 0  |                 char *chAlias = NULL, *path = NULL, *locale = NULL, *keyPath = NULL;  | 
983  | 0  |                 int32_t capacity;  | 
984  |  |  | 
985  |  |                 /*  | 
986  |  |                 * Allocate enough space for both the char * version  | 
987  |  |                 * of the alias and parent->fResPath.  | 
988  |  |                 *  | 
989  |  |                 * We do this so that res_findResource() can modify the path,  | 
990  |  |                 * which allows us to remove redundant _res_findResource() variants  | 
991  |  |                 * in uresdata.c.  | 
992  |  |                 * res_findResource() now NUL-terminates each segment so that table keys  | 
993  |  |                 * can always be compared with strcmp() instead of strncmp().  | 
994  |  |                 * Saves code there and simplifies testing and code coverage.  | 
995  |  |                 *  | 
996  |  |                 * markus 2003oct17  | 
997  |  |                 */  | 
998  | 0  |                 ++len; /* count the terminating NUL */  | 
999  | 0  |                 if(parent->fResPath != NULL) { | 
1000  | 0  |                     capacity = (int32_t)uprv_strlen(parent->fResPath) + 1;  | 
1001  | 0  |                 } else { | 
1002  | 0  |                     capacity = 0;  | 
1003  | 0  |                 }  | 
1004  | 0  |                 if(capacity < len) { | 
1005  | 0  |                     capacity = len;  | 
1006  | 0  |                 }  | 
1007  | 0  |                 if(capacity <= (int32_t)sizeof(stackAlias)) { | 
1008  | 0  |                     capacity = (int32_t)sizeof(stackAlias);  | 
1009  | 0  |                     chAlias = stackAlias;  | 
1010  | 0  |                 } else { | 
1011  | 0  |                     chAlias = (char *)uprv_malloc(capacity);  | 
1012  |  |                     /* test for NULL */  | 
1013  | 0  |                     if(chAlias == NULL) { | 
1014  | 0  |                         *status = U_MEMORY_ALLOCATION_ERROR;  | 
1015  | 0  |                         return NULL;  | 
1016  | 0  |                     }  | 
1017  | 0  |                 }  | 
1018  | 0  |                 u_UCharsToChars(alias, chAlias, len);  | 
1019  |  | 
  | 
1020  | 0  |                 if(*chAlias == RES_PATH_SEPARATOR) { | 
1021  |  |                     /* there is a path included */  | 
1022  | 0  |                     locale = uprv_strchr(chAlias+1, RES_PATH_SEPARATOR);  | 
1023  | 0  |                     if(locale == NULL) { | 
1024  | 0  |                         locale = uprv_strchr(chAlias, 0); /* avoid locale == NULL to make code below work */  | 
1025  | 0  |                     } else { | 
1026  | 0  |                         *locale = 0;  | 
1027  | 0  |                         locale++;  | 
1028  | 0  |                     }  | 
1029  | 0  |                     path = chAlias+1;  | 
1030  | 0  |                     if(uprv_strcmp(path, "LOCALE") == 0) { | 
1031  |  |                         /* this is an XPath alias, starting with "/LOCALE/" */  | 
1032  |  |                         /* it contains the path to a resource which should be looked up */  | 
1033  |  |                         /* starting in the requested locale */  | 
1034  | 0  |                         keyPath = locale;   | 
1035  | 0  |                         locale = parent->fTopLevelData->fName; /* this is the requested locale's name */  | 
1036  | 0  |                         path = realData->fPath; /* we will be looking in the same package */  | 
1037  | 0  |                     } else { | 
1038  | 0  |                         if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */ | 
1039  | 0  |                             path = NULL;  | 
1040  | 0  |                         }  | 
1041  | 0  |                         keyPath = uprv_strchr(locale, RES_PATH_SEPARATOR);  | 
1042  | 0  |                         if(keyPath) { | 
1043  | 0  |                             *keyPath = 0;  | 
1044  | 0  |                             keyPath++;  | 
1045  | 0  |                         }  | 
1046  | 0  |                     }  | 
1047  | 0  |                 } else { | 
1048  |  |                     /* no path, start with a locale */  | 
1049  | 0  |                     locale = chAlias;  | 
1050  | 0  |                     keyPath = uprv_strchr(locale, RES_PATH_SEPARATOR);  | 
1051  | 0  |                     if(keyPath) { | 
1052  | 0  |                         *keyPath = 0;  | 
1053  | 0  |                         keyPath++;  | 
1054  | 0  |                     }  | 
1055  | 0  |                     path = realData->fPath;  | 
1056  | 0  |                 }  | 
1057  |  |  | 
1058  |  | 
  | 
1059  | 0  |                 { | 
1060  |  |                     /* got almost everything, let's try to open */  | 
1061  |  |                     /* first, open the bundle with real data */  | 
1062  | 0  |                     UResourceBundle *result = resB;  | 
1063  | 0  |                     const char* temp = NULL;  | 
1064  | 0  |                     UErrorCode intStatus = U_ZERO_ERROR;  | 
1065  | 0  |                     UResourceBundle *mainRes = ures_openDirect(path, locale, &intStatus);   | 
1066  | 0  |                     if(U_SUCCESS(intStatus)) { | 
1067  | 0  |                         if(keyPath == NULL) { | 
1068  |  |                             /* no key path. This means that we are going to   | 
1069  |  |                             * to use the corresponding resource from  | 
1070  |  |                             * another bundle  | 
1071  |  |                             */  | 
1072  |  |                             /* first, we are going to get a corresponding parent   | 
1073  |  |                             * resource to the one we are searching.  | 
1074  |  |                             */  | 
1075  | 0  |                             char *aKey = parent->fResPath;  | 
1076  | 0  |                             if(aKey) { | 
1077  | 0  |                                 uprv_strcpy(chAlias, aKey); /* allocated large enough above */  | 
1078  | 0  |                                 aKey = chAlias;  | 
1079  | 0  |                                 r = res_findResource(&(mainRes->fResData), mainRes->fRes, &aKey, &temp);  | 
1080  | 0  |                             } else { | 
1081  | 0  |                                 r = mainRes->fRes;  | 
1082  | 0  |                             }  | 
1083  | 0  |                             if(key) { | 
1084  |  |                                 /* we need to make keyPath from parent's fResPath and  | 
1085  |  |                                 * current key, if there is a key associated  | 
1086  |  |                                 */  | 
1087  | 0  |                                 len = (int32_t)(uprv_strlen(key) + 1);  | 
1088  | 0  |                                 if(len > capacity) { | 
1089  | 0  |                                     capacity = len;  | 
1090  | 0  |                                     if(chAlias == stackAlias) { | 
1091  | 0  |                                         chAlias = (char *)uprv_malloc(capacity);  | 
1092  | 0  |                                     } else { | 
1093  | 0  |                                         chAlias = (char *)uprv_realloc(chAlias, capacity);  | 
1094  | 0  |                                     }  | 
1095  | 0  |                                     if(chAlias == NULL) { | 
1096  | 0  |                                         ures_close(mainRes);  | 
1097  | 0  |                                         *status = U_MEMORY_ALLOCATION_ERROR;  | 
1098  | 0  |                                         return NULL;  | 
1099  | 0  |                                     }  | 
1100  | 0  |                                 }  | 
1101  | 0  |                                 uprv_memcpy(chAlias, key, len);  | 
1102  | 0  |                                 aKey = chAlias;  | 
1103  | 0  |                                 r = res_findResource(&(mainRes->fResData), r, &aKey, &temp);  | 
1104  | 0  |                             } else if(idx != -1) { | 
1105  |  |                                 /* if there is no key, but there is an index, try to get by the index */  | 
1106  |  |                                 /* here we have either a table or an array, so get the element */  | 
1107  | 0  |                                 int32_t type = RES_GET_TYPE(r);  | 
1108  | 0  |                                 if(URES_IS_TABLE(type)) { | 
1109  | 0  |                                     r = res_getTableItemByIndex(&(mainRes->fResData), r, idx, (const char **)&aKey);  | 
1110  | 0  |                                 } else { /* array */ | 
1111  | 0  |                                     r = res_getArrayItem(&(mainRes->fResData), r, idx);  | 
1112  | 0  |                                 }  | 
1113  | 0  |                             }  | 
1114  | 0  |                             if(r != RES_BOGUS) { | 
1115  | 0  |                                 result = init_resb_result(&(mainRes->fResData), r, temp, -1, mainRes->fData, mainRes, noAlias+1, resB, status);  | 
1116  | 0  |                             } else { | 
1117  | 0  |                                 *status = U_MISSING_RESOURCE_ERROR;  | 
1118  | 0  |                                 result = resB;  | 
1119  | 0  |                             }  | 
1120  | 0  |                         } else { | 
1121  |  |                             /* this one is a bit trickier.   | 
1122  |  |                             * we start finding keys, but after we resolve one alias, the path might continue.  | 
1123  |  |                             * Consider:   | 
1124  |  |                             *     aliastest:alias { "testtypes/anotheralias/Sequence" } | 
1125  |  |                             *     anotheralias:alias { "/ICUDATA/sh/CollationElements" } | 
1126  |  |                             * aliastest resource should finally have the sequence, not collation elements.  | 
1127  |  |                             */  | 
1128  | 0  |                             UResourceDataEntry *dataEntry = mainRes->fData;  | 
1129  | 0  |                             char stackPath[URES_MAX_BUFFER_SIZE];  | 
1130  | 0  |                             char *pathBuf = stackPath, *myPath = pathBuf;  | 
1131  | 0  |                             if(uprv_strlen(keyPath) >= UPRV_LENGTHOF(stackPath)) { | 
1132  | 0  |                                 pathBuf = (char *)uprv_malloc((uprv_strlen(keyPath)+1)*sizeof(char));  | 
1133  | 0  |                                 if(pathBuf == NULL) { | 
1134  | 0  |                                     *status = U_MEMORY_ALLOCATION_ERROR;  | 
1135  | 0  |                                     ures_close(mainRes);  | 
1136  | 0  |                                     return NULL;  | 
1137  | 0  |                                 }  | 
1138  | 0  |                             }  | 
1139  | 0  |                             uprv_strcpy(pathBuf, keyPath);  | 
1140  | 0  |                             result = mainRes;  | 
1141  |  |                             /* now we have fallback following here */  | 
1142  | 0  |                             do { | 
1143  | 0  |                                 r = dataEntry->fData.rootRes;       | 
1144  |  |                                 /* this loop handles 'found' resources over several levels */  | 
1145  | 0  |                                 while(*myPath && U_SUCCESS(*status)) { | 
1146  | 0  |                                     r = res_findResource(&(dataEntry->fData), r, &myPath, &temp);  | 
1147  | 0  |                                     if(r != RES_BOGUS) { /* found a resource, but it might be an indirection */ | 
1148  | 0  |                                         resB = init_resb_result(&(dataEntry->fData), r, temp, -1, dataEntry, result, noAlias+1, resB, status);  | 
1149  | 0  |                                         if (temp == NULL || uprv_strcmp(keyPath, temp) != 0) { | 
1150  |  |                                             // the call to init_resb_result() above will set resB->fKeyPath to be  | 
1151  |  |                                             // the same as resB->fKey, throwing away any additional path elements if we  | 
1152  |  |                                             // had them-- if the key path wasn't just a single resource ID, clear out  | 
1153  |  |                                             // the bundle's key path and re-set it to be equal to keyPath  | 
1154  | 0  |                                             ures_freeResPath(resB);  | 
1155  | 0  |                                             ures_appendResPath(resB, keyPath, (int32_t)uprv_strlen(keyPath), status);  | 
1156  | 0  |                                             if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { | 
1157  | 0  |                                                 ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);  | 
1158  | 0  |                                             }  | 
1159  | 0  |                                         }  | 
1160  | 0  |                                         result = resB;  | 
1161  | 0  |                                         if(result) { | 
1162  | 0  |                                             r = result->fRes; /* switch to a new resource, possibly a new tree */  | 
1163  | 0  |                                             dataEntry = result->fData;  | 
1164  | 0  |                                         }  | 
1165  | 0  |                                     } else { /* no resource found, we don't really want to look anymore on this level */ | 
1166  | 0  |                                         break;  | 
1167  | 0  |                                     }  | 
1168  | 0  |                                 }  | 
1169  | 0  |                                 dataEntry = dataEntry->fParent;  | 
1170  | 0  |                                 uprv_strcpy(pathBuf, keyPath);  | 
1171  | 0  |                                 myPath = pathBuf;  | 
1172  | 0  |                             } while(r == RES_BOGUS && dataEntry != NULL);  | 
1173  | 0  |                             if(r == RES_BOGUS) { | 
1174  | 0  |                                 *status = U_MISSING_RESOURCE_ERROR;  | 
1175  | 0  |                                 result = resB;  | 
1176  | 0  |                             }  | 
1177  | 0  |                             if(pathBuf != stackPath) { | 
1178  | 0  |                                 uprv_free(pathBuf);  | 
1179  | 0  |                             }  | 
1180  | 0  |                         }  | 
1181  | 0  |                     } else { /* we failed to open the resource we're aliasing to */ | 
1182  | 0  |                         *status = intStatus;  | 
1183  | 0  |                     }  | 
1184  | 0  |                     if(chAlias != stackAlias) { | 
1185  | 0  |                         uprv_free(chAlias);  | 
1186  | 0  |                     }  | 
1187  | 0  |                     if(mainRes != result) { | 
1188  | 0  |                         ures_close(mainRes);  | 
1189  | 0  |                     }  | 
1190  | 0  |                     ResourceTracer(resB).maybeTrace("getalias"); | 
1191  | 0  |                     return result;  | 
1192  | 0  |                 }  | 
1193  | 0  |             } else { | 
1194  |  |                 /* bad alias, should be an error */   | 
1195  | 0  |                 *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1196  | 0  |                 return resB;  | 
1197  | 0  |             }  | 
1198  | 0  |         } else { | 
1199  | 0  |             *status = U_TOO_MANY_ALIASES_ERROR;  | 
1200  | 0  |             return resB;  | 
1201  | 0  |         }  | 
1202  | 0  |     }  | 
1203  | 0  |     if(resB == NULL) { | 
1204  | 0  |         resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));  | 
1205  |  |         /* test for NULL */  | 
1206  | 0  |         if (resB == NULL) { | 
1207  | 0  |             *status = U_MEMORY_ALLOCATION_ERROR;  | 
1208  | 0  |             return NULL;  | 
1209  | 0  |         }  | 
1210  | 0  |         ures_setIsStackObject(resB, FALSE);  | 
1211  | 0  |         resB->fResPath = NULL;  | 
1212  | 0  |         resB->fResPathLen = 0;  | 
1213  | 0  |     } else { | 
1214  | 0  |         if(resB->fData != NULL) { | 
1215  | 0  |             entryClose(resB->fData);  | 
1216  | 0  |         }  | 
1217  | 0  |         if(resB->fVersion != NULL) { | 
1218  | 0  |             uprv_free(resB->fVersion);  | 
1219  | 0  |         }  | 
1220  |  |         /*   | 
1221  |  |         weiv: if stack object was passed in, it doesn't really need to be reinited,  | 
1222  |  |         since the purpose of initing is to remove stack junk. However, at this point   | 
1223  |  |         we would not do anything to an allocated object, so stack object should be  | 
1224  |  |         treated the same  | 
1225  |  |         */  | 
1226  |  |         /*  | 
1227  |  |         if(ures_isStackObject(resB) != FALSE) { | 
1228  |  |         ures_initStackObject(resB);  | 
1229  |  |         }  | 
1230  |  |         */  | 
1231  | 0  |         if(parent != resB) { | 
1232  | 0  |             ures_freeResPath(resB);  | 
1233  | 0  |         }  | 
1234  | 0  |     }  | 
1235  | 0  |     resB->fData = realData;  | 
1236  | 0  |     entryIncrease(resB->fData);  | 
1237  | 0  |     resB->fHasFallback = FALSE;  | 
1238  | 0  |     resB->fIsTopLevel = FALSE;  | 
1239  | 0  |     resB->fIndex = -1;  | 
1240  | 0  |     resB->fKey = key;   | 
1241  |  |     /*resB->fParentRes = parent;*/  | 
1242  | 0  |     resB->fTopLevelData = parent->fTopLevelData;  | 
1243  | 0  |     if(parent->fResPath && parent != resB) { | 
1244  | 0  |         ures_appendResPath(resB, parent->fResPath, parent->fResPathLen, status);  | 
1245  | 0  |     }  | 
1246  | 0  |     if(key != NULL) { | 
1247  | 0  |         ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status);  | 
1248  | 0  |         if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { | 
1249  | 0  |             ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);  | 
1250  | 0  |         }  | 
1251  | 0  |     } else if(idx >= 0) { | 
1252  | 0  |         char buf[256];  | 
1253  | 0  |         int32_t len = T_CString_integerToString(buf, idx, 10);  | 
1254  | 0  |         ures_appendResPath(resB, buf, len, status);  | 
1255  | 0  |         if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) { | 
1256  | 0  |             ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);  | 
1257  | 0  |         }  | 
1258  | 0  |     }  | 
1259  |  |     /* Make sure that Purify doesn't complain about uninitialized memory copies. */  | 
1260  | 0  |     { | 
1261  | 0  |         int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);  | 
1262  | 0  |         uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);  | 
1263  | 0  |     }  | 
1264  |  | 
  | 
1265  | 0  |     resB->fVersion = NULL;  | 
1266  | 0  |     resB->fRes = r;  | 
1267  |  |     /*resB->fParent = parent->fRes;*/  | 
1268  | 0  |     uprv_memmove(&resB->fResData, rdata, sizeof(ResourceData));  | 
1269  | 0  |     resB->fSize = res_countArrayItems(&(resB->fResData), resB->fRes);  | 
1270  | 0  |     ResourceTracer(resB).trace("get"); | 
1271  | 0  |     return resB;  | 
1272  | 0  | }  | 
1273  |  |  | 
1274  | 0  | UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) { | 
1275  | 0  |     UBool isStackObject;  | 
1276  | 0  |     if(U_FAILURE(*status) || r == original) { | 
1277  | 0  |         return r;  | 
1278  | 0  |     }  | 
1279  | 0  |     if(original != NULL) { | 
1280  | 0  |         if(r == NULL) { | 
1281  | 0  |             isStackObject = FALSE;  | 
1282  | 0  |             r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));  | 
1283  |  |             /* test for NULL */  | 
1284  | 0  |             if (r == NULL) { | 
1285  | 0  |                 *status = U_MEMORY_ALLOCATION_ERROR;  | 
1286  | 0  |                 return NULL;  | 
1287  | 0  |             }  | 
1288  | 0  |         } else { | 
1289  | 0  |             isStackObject = ures_isStackObject(r);  | 
1290  | 0  |             ures_closeBundle(r, FALSE);  | 
1291  | 0  |         }  | 
1292  | 0  |         uprv_memcpy(r, original, sizeof(UResourceBundle));  | 
1293  | 0  |         r->fResPath = NULL;  | 
1294  | 0  |         r->fResPathLen = 0;  | 
1295  | 0  |         if(original->fResPath) { | 
1296  | 0  |             ures_appendResPath(r, original->fResPath, original->fResPathLen, status);  | 
1297  | 0  |         }  | 
1298  | 0  |         ures_setIsStackObject(r, isStackObject);  | 
1299  | 0  |         if(r->fData != NULL) { | 
1300  | 0  |             entryIncrease(r->fData);  | 
1301  | 0  |         }  | 
1302  | 0  |     }  | 
1303  | 0  |     return r;  | 
1304  | 0  | }  | 
1305  |  |  | 
1306  |  | /**  | 
1307  |  |  * Functions to retrieve data from resource bundles.  | 
1308  |  |  */  | 
1309  |  |  | 
1310  | 0  | U_CAPI const UChar* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) { | 
1311  | 0  |     const UChar *s;  | 
1312  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
1313  | 0  |         return NULL;  | 
1314  | 0  |     }  | 
1315  | 0  |     if(resB == NULL) { | 
1316  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1317  | 0  |         return NULL;  | 
1318  | 0  |     }  | 
1319  | 0  |     s = res_getString({resB}, &(resB->fResData), resB->fRes, len); | 
1320  | 0  |     if (s == NULL) { | 
1321  | 0  |         *status = U_RESOURCE_TYPE_MISMATCH;  | 
1322  | 0  |     }  | 
1323  | 0  |     return s;  | 
1324  | 0  | }  | 
1325  |  |  | 
1326  |  | static const char *  | 
1327  |  | ures_toUTF8String(const UChar *s16, int32_t length16,  | 
1328  |  |                   char *dest, int32_t *pLength,  | 
1329  |  |                   UBool forceCopy,  | 
1330  | 0  |                   UErrorCode *status) { | 
1331  | 0  |     int32_t capacity;  | 
1332  |  | 
  | 
1333  | 0  |     if (U_FAILURE(*status)) { | 
1334  | 0  |         return NULL;  | 
1335  | 0  |     }  | 
1336  | 0  |     if (pLength != NULL) { | 
1337  | 0  |         capacity = *pLength;  | 
1338  | 0  |     } else { | 
1339  | 0  |         capacity = 0;  | 
1340  | 0  |     }  | 
1341  | 0  |     if (capacity < 0 || (capacity > 0 && dest == NULL)) { | 
1342  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1343  | 0  |         return NULL;  | 
1344  | 0  |     }  | 
1345  |  |  | 
1346  | 0  |     if (length16 == 0) { | 
1347  |  |         /* empty string, return as read-only pointer */  | 
1348  | 0  |         if (pLength != NULL) { | 
1349  | 0  |             *pLength = 0;  | 
1350  | 0  |         }  | 
1351  | 0  |         if (forceCopy) { | 
1352  | 0  |             u_terminateChars(dest, capacity, 0, status);  | 
1353  | 0  |             return dest;  | 
1354  | 0  |         } else { | 
1355  | 0  |             return "";  | 
1356  | 0  |         }  | 
1357  | 0  |     } else { | 
1358  |  |         /* We need to transform the string to the destination buffer. */  | 
1359  | 0  |         if (capacity < length16) { | 
1360  |  |             /* No chance for the string to fit. Pure preflighting. */  | 
1361  | 0  |             return u_strToUTF8(NULL, 0, pLength, s16, length16, status);  | 
1362  | 0  |         }  | 
1363  | 0  |         if (!forceCopy && (length16 <= 0x2aaaaaaa)) { | 
1364  |  |             /*  | 
1365  |  |              * We know the string will fit into dest because each UChar turns  | 
1366  |  |              * into at most three UTF-8 bytes. Fill the latter part of dest  | 
1367  |  |              * so that callers do not expect to use dest as a string pointer,  | 
1368  |  |              * hopefully leading to more robust code for when resource bundles  | 
1369  |  |              * may store UTF-8 natively.  | 
1370  |  |              * (In which case dest would not be used at all.)  | 
1371  |  |              *  | 
1372  |  |              * We do not do this if forceCopy=TRUE because then the caller  | 
1373  |  |              * expects the string to start exactly at dest.  | 
1374  |  |              *  | 
1375  |  |              * The test above for <= 0x2aaaaaaa prevents overflows.  | 
1376  |  |              * The +1 is for the NUL terminator.  | 
1377  |  |              */  | 
1378  | 0  |             int32_t maxLength = 3 * length16 + 1;  | 
1379  | 0  |             if (capacity > maxLength) { | 
1380  | 0  |                 dest += capacity - maxLength;  | 
1381  | 0  |                 capacity = maxLength;  | 
1382  | 0  |             }  | 
1383  | 0  |         }  | 
1384  | 0  |         return u_strToUTF8(dest, capacity, pLength, s16, length16, status);  | 
1385  | 0  |     }  | 
1386  | 0  | }  | 
1387  |  |  | 
1388  |  | U_CAPI const char * U_EXPORT2  | 
1389  |  | ures_getUTF8String(const UResourceBundle *resB,  | 
1390  |  |                    char *dest, int32_t *pLength,  | 
1391  |  |                    UBool forceCopy,  | 
1392  | 0  |                    UErrorCode *status) { | 
1393  | 0  |     int32_t length16;  | 
1394  | 0  |     const UChar *s16 = ures_getString(resB, &length16, status);  | 
1395  | 0  |     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);  | 
1396  | 0  | }  | 
1397  |  |  | 
1398  |  | U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len,   | 
1399  | 0  |                                                UErrorCode*               status) { | 
1400  | 0  |   const uint8_t *p;  | 
1401  | 0  |   if (status==NULL || U_FAILURE(*status)) { | 
1402  | 0  |     return NULL;  | 
1403  | 0  |   }  | 
1404  | 0  |   if(resB == NULL) { | 
1405  | 0  |     *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1406  | 0  |     return NULL;  | 
1407  | 0  |   }  | 
1408  | 0  |   p = res_getBinary({resB}, &(resB->fResData), resB->fRes, len); | 
1409  | 0  |   if (p == NULL) { | 
1410  | 0  |     *status = U_RESOURCE_TYPE_MISMATCH;  | 
1411  | 0  |   }  | 
1412  | 0  |   return p;  | 
1413  | 0  | }  | 
1414  |  |  | 
1415  |  | U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len,   | 
1416  | 0  |                                                    UErrorCode*               status) { | 
1417  | 0  |   const int32_t *p;  | 
1418  | 0  |   if (status==NULL || U_FAILURE(*status)) { | 
1419  | 0  |     return NULL;  | 
1420  | 0  |   }  | 
1421  | 0  |   if(resB == NULL) { | 
1422  | 0  |     *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1423  | 0  |     return NULL;  | 
1424  | 0  |   }  | 
1425  | 0  |   p = res_getIntVector({resB}, &(resB->fResData), resB->fRes, len); | 
1426  | 0  |   if (p == NULL) { | 
1427  | 0  |     *status = U_RESOURCE_TYPE_MISMATCH;  | 
1428  | 0  |   }  | 
1429  | 0  |   return p;  | 
1430  | 0  | }  | 
1431  |  |  | 
1432  |  | /* this function returns a signed integer */   | 
1433  |  | /* it performs sign extension */  | 
1434  | 0  | U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) { | 
1435  | 0  |   if (status==NULL || U_FAILURE(*status)) { | 
1436  | 0  |     return 0xffffffff;  | 
1437  | 0  |   }  | 
1438  | 0  |   if(resB == NULL) { | 
1439  | 0  |     *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1440  | 0  |     return 0xffffffff;  | 
1441  | 0  |   }  | 
1442  | 0  |   if(RES_GET_TYPE(resB->fRes) != URES_INT) { | 
1443  | 0  |     *status = U_RESOURCE_TYPE_MISMATCH;  | 
1444  | 0  |     return 0xffffffff;  | 
1445  | 0  |   }  | 
1446  | 0  |   return res_getInt({resB}, resB->fRes); | 
1447  | 0  | }  | 
1448  |  |  | 
1449  | 0  | U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) { | 
1450  | 0  |   if (status==NULL || U_FAILURE(*status)) { | 
1451  | 0  |     return 0xffffffff;  | 
1452  | 0  |   }  | 
1453  | 0  |   if(resB == NULL) { | 
1454  | 0  |     *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1455  | 0  |     return 0xffffffff;  | 
1456  | 0  |   }  | 
1457  | 0  |   if(RES_GET_TYPE(resB->fRes) != URES_INT) { | 
1458  | 0  |     *status = U_RESOURCE_TYPE_MISMATCH;  | 
1459  | 0  |     return 0xffffffff;  | 
1460  | 0  |   }  | 
1461  | 0  |   return res_getUInt({resB}, resB->fRes); | 
1462  | 0  | }  | 
1463  |  |  | 
1464  | 0  | U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) { | 
1465  | 0  |   if(resB == NULL) { | 
1466  | 0  |     return URES_NONE;  | 
1467  | 0  |   }  | 
1468  | 0  |   return res_getPublicType(resB->fRes);  | 
1469  | 0  | }  | 
1470  |  |  | 
1471  | 0  | U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) { | 
1472  |  |   //  | 
1473  |  |   // TODO: Trace ures_getKey? I guess not usually.  | 
1474  |  |   //  | 
1475  |  |   // We usually get the key string to decide whether we want the value, or to  | 
1476  |  |   // make a key-value pair. Tracing the value should suffice.  | 
1477  |  |   //  | 
1478  |  |   // However, I believe we have some data (e.g., in res_index) where the key  | 
1479  |  |   // strings are the data. Tracing the enclosing table should suffice.  | 
1480  |  |   //  | 
1481  | 0  |   if(resB == NULL) { | 
1482  | 0  |     return NULL;  | 
1483  | 0  |   }  | 
1484  | 0  |   return(resB->fKey);  | 
1485  | 0  | }  | 
1486  |  |  | 
1487  | 0  | U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) { | 
1488  | 0  |   if(resB == NULL) { | 
1489  | 0  |     return 0;  | 
1490  | 0  |   }  | 
1491  |  |     | 
1492  | 0  |   return resB->fSize;  | 
1493  | 0  | }  | 
1494  |  |  | 
1495  | 0  | static const UChar* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) { | 
1496  | 0  |   if(RES_GET_TYPE(r) == URES_ALIAS) { | 
1497  | 0  |     const UChar* result = 0;  | 
1498  | 0  |     UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, NULL, status);  | 
1499  | 0  |     result = ures_getString(tempRes, len, status);  | 
1500  | 0  |     ures_close(tempRes);  | 
1501  | 0  |     return result;  | 
1502  | 0  |   } else { | 
1503  | 0  |     return res_getString({resB, sIndex}, &(resB->fResData), r, len);  | 
1504  | 0  |   }  | 
1505  | 0  | }  | 
1506  |  |  | 
1507  | 0  | U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){ | 
1508  | 0  |   if(resB == NULL) { | 
1509  | 0  |     return;  | 
1510  | 0  |   }  | 
1511  | 0  |   resB->fIndex = -1;  | 
1512  | 0  | }  | 
1513  |  |  | 
1514  | 0  | U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) { | 
1515  | 0  |   if(resB == NULL) { | 
1516  | 0  |     return FALSE;  | 
1517  | 0  |   }  | 
1518  | 0  |   return (UBool)(resB->fIndex < resB->fSize-1);  | 
1519  | 0  | }  | 
1520  |  |  | 
1521  | 0  | U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) { | 
1522  | 0  |   Resource r = RES_BOGUS;  | 
1523  |  |     | 
1524  | 0  |   if (status==NULL || U_FAILURE(*status)) { | 
1525  | 0  |     return NULL;  | 
1526  | 0  |   }  | 
1527  | 0  |   if(resB == NULL) { | 
1528  | 0  |     *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1529  | 0  |     return NULL;  | 
1530  | 0  |   }  | 
1531  |  |     | 
1532  | 0  |   if(resB->fIndex == resB->fSize-1) { | 
1533  | 0  |     *status = U_INDEX_OUTOFBOUNDS_ERROR;  | 
1534  | 0  |   } else { | 
1535  | 0  |     resB->fIndex++;  | 
1536  | 0  |     switch(RES_GET_TYPE(resB->fRes)) { | 
1537  | 0  |     case URES_STRING:  | 
1538  | 0  |     case URES_STRING_V2:  | 
1539  | 0  |       return res_getString({resB}, &(resB->fResData), resB->fRes, len); | 
1540  | 0  |     case URES_TABLE:  | 
1541  | 0  |     case URES_TABLE16:  | 
1542  | 0  |     case URES_TABLE32:  | 
1543  | 0  |       r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, key);  | 
1544  | 0  |       if(r == RES_BOGUS && resB->fHasFallback) { | 
1545  |  |         /* TODO: do the fallback */  | 
1546  | 0  |       }  | 
1547  | 0  |       return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);  | 
1548  | 0  |     case URES_ARRAY:  | 
1549  | 0  |     case URES_ARRAY16:  | 
1550  | 0  |       r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);  | 
1551  | 0  |       if(r == RES_BOGUS && resB->fHasFallback) { | 
1552  |  |         /* TODO: do the fallback */  | 
1553  | 0  |       }  | 
1554  | 0  |       return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);  | 
1555  | 0  |     case URES_ALIAS:  | 
1556  | 0  |       return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status);  | 
1557  | 0  |     case URES_INT:  | 
1558  | 0  |     case URES_BINARY:  | 
1559  | 0  |     case URES_INT_VECTOR:  | 
1560  | 0  |         *status = U_RESOURCE_TYPE_MISMATCH;  | 
1561  | 0  |         U_FALLTHROUGH;  | 
1562  | 0  |     default:  | 
1563  | 0  |       return NULL;  | 
1564  | 0  |     }  | 
1565  | 0  |   }  | 
1566  |  |  | 
1567  | 0  |   return NULL;  | 
1568  | 0  | }  | 
1569  |  |  | 
1570  | 0  | U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) { | 
1571  | 0  |     const char *key = NULL;  | 
1572  | 0  |     Resource r = RES_BOGUS;  | 
1573  |  | 
  | 
1574  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
1575  |  |             /*return NULL;*/  | 
1576  | 0  |             return fillIn;  | 
1577  | 0  |     }  | 
1578  | 0  |     if(resB == NULL) { | 
1579  | 0  |             *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1580  |  |             /*return NULL;*/  | 
1581  | 0  |             return fillIn;  | 
1582  | 0  |     }  | 
1583  |  |  | 
1584  | 0  |     if(resB->fIndex == resB->fSize-1) { | 
1585  | 0  |       *status = U_INDEX_OUTOFBOUNDS_ERROR;  | 
1586  |  |       /*return NULL;*/  | 
1587  | 0  |     } else { | 
1588  | 0  |         resB->fIndex++;  | 
1589  | 0  |         switch(RES_GET_TYPE(resB->fRes)) { | 
1590  | 0  |         case URES_INT:  | 
1591  | 0  |         case URES_BINARY:  | 
1592  | 0  |         case URES_STRING:  | 
1593  | 0  |         case URES_STRING_V2:  | 
1594  | 0  |         case URES_INT_VECTOR:  | 
1595  | 0  |             return ures_copyResb(fillIn, resB, status);  | 
1596  | 0  |         case URES_TABLE:  | 
1597  | 0  |         case URES_TABLE16:  | 
1598  | 0  |         case URES_TABLE32:  | 
1599  | 0  |             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);  | 
1600  | 0  |             if(r == RES_BOGUS && resB->fHasFallback) { | 
1601  |  |                 /* TODO: do the fallback */  | 
1602  | 0  |             }  | 
1603  | 0  |             return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);  | 
1604  | 0  |         case URES_ARRAY:  | 
1605  | 0  |         case URES_ARRAY16:  | 
1606  | 0  |             r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);  | 
1607  | 0  |             if(r == RES_BOGUS && resB->fHasFallback) { | 
1608  |  |                 /* TODO: do the fallback */  | 
1609  | 0  |             }  | 
1610  | 0  |             return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);  | 
1611  | 0  |         default:  | 
1612  |  |             /*return NULL;*/  | 
1613  | 0  |             return fillIn;  | 
1614  | 0  |         }  | 
1615  | 0  |     }  | 
1616  |  |     /*return NULL;*/  | 
1617  | 0  |     return fillIn;  | 
1618  | 0  | }  | 
1619  |  |  | 
1620  | 0  | U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) { | 
1621  | 0  |     const char* key = NULL;  | 
1622  | 0  |     Resource r = RES_BOGUS;  | 
1623  |  | 
  | 
1624  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
1625  |  |         /*return NULL;*/  | 
1626  | 0  |         return fillIn;  | 
1627  | 0  |     }  | 
1628  | 0  |     if(resB == NULL) { | 
1629  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1630  |  |         /*return NULL;*/  | 
1631  | 0  |         return fillIn;  | 
1632  | 0  |     }  | 
1633  |  |  | 
1634  | 0  |     if(indexR >= 0 && resB->fSize > indexR) { | 
1635  | 0  |         switch(RES_GET_TYPE(resB->fRes)) { | 
1636  | 0  |         case URES_INT:  | 
1637  | 0  |         case URES_BINARY:  | 
1638  | 0  |         case URES_STRING:  | 
1639  | 0  |         case URES_STRING_V2:  | 
1640  | 0  |         case URES_INT_VECTOR:  | 
1641  | 0  |             return ures_copyResb(fillIn, resB, status);  | 
1642  | 0  |         case URES_TABLE:  | 
1643  | 0  |         case URES_TABLE16:  | 
1644  | 0  |         case URES_TABLE32:  | 
1645  | 0  |             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);  | 
1646  | 0  |             if(r == RES_BOGUS && resB->fHasFallback) { | 
1647  |  |                 /* TODO: do the fallback */  | 
1648  | 0  |             }  | 
1649  | 0  |             return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);  | 
1650  | 0  |         case URES_ARRAY:  | 
1651  | 0  |         case URES_ARRAY16:  | 
1652  | 0  |             r = res_getArrayItem(&(resB->fResData), resB->fRes, indexR);  | 
1653  | 0  |             if(r == RES_BOGUS && resB->fHasFallback) { | 
1654  |  |                 /* TODO: do the fallback */  | 
1655  | 0  |             }  | 
1656  | 0  |             return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);  | 
1657  | 0  |         default:  | 
1658  |  |             /*return NULL;*/  | 
1659  | 0  |             return fillIn;  | 
1660  | 0  |         }  | 
1661  | 0  |     } else { | 
1662  | 0  |         *status = U_MISSING_RESOURCE_ERROR;  | 
1663  | 0  |     }  | 
1664  |  |     /*return NULL;*/  | 
1665  | 0  |     return fillIn;  | 
1666  | 0  | }  | 
1667  |  |  | 
1668  | 0  | U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) { | 
1669  | 0  |     const char* key = NULL;  | 
1670  | 0  |     Resource r = RES_BOGUS;  | 
1671  |  | 
  | 
1672  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
1673  | 0  |         return NULL;  | 
1674  | 0  |     }  | 
1675  | 0  |     if(resB == NULL) { | 
1676  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1677  | 0  |         return NULL;  | 
1678  | 0  |     }  | 
1679  |  |  | 
1680  | 0  |     if(indexS >= 0 && resB->fSize > indexS) { | 
1681  | 0  |         switch(RES_GET_TYPE(resB->fRes)) { | 
1682  | 0  |         case URES_STRING:  | 
1683  | 0  |         case URES_STRING_V2:  | 
1684  | 0  |             return res_getString({resB}, &(resB->fResData), resB->fRes, len); | 
1685  | 0  |         case URES_TABLE:  | 
1686  | 0  |         case URES_TABLE16:  | 
1687  | 0  |         case URES_TABLE32:  | 
1688  | 0  |             r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);  | 
1689  | 0  |             if(r == RES_BOGUS && resB->fHasFallback) { | 
1690  |  |                 /* TODO: do the fallback */  | 
1691  | 0  |             }  | 
1692  | 0  |             return ures_getStringWithAlias(resB, r, indexS, len, status);  | 
1693  | 0  |         case URES_ARRAY:  | 
1694  | 0  |         case URES_ARRAY16:  | 
1695  | 0  |             r = res_getArrayItem(&(resB->fResData), resB->fRes, indexS);  | 
1696  | 0  |             if(r == RES_BOGUS && resB->fHasFallback) { | 
1697  |  |                 /* TODO: do the fallback */  | 
1698  | 0  |             }  | 
1699  | 0  |             return ures_getStringWithAlias(resB, r, indexS, len, status);  | 
1700  | 0  |         case URES_ALIAS:  | 
1701  | 0  |             return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);  | 
1702  | 0  |         case URES_INT:  | 
1703  | 0  |         case URES_BINARY:  | 
1704  | 0  |         case URES_INT_VECTOR:  | 
1705  | 0  |             *status = U_RESOURCE_TYPE_MISMATCH;  | 
1706  | 0  |             break;  | 
1707  | 0  |         default:  | 
1708  |  |           /* must not occur */  | 
1709  | 0  |           *status = U_INTERNAL_PROGRAM_ERROR;  | 
1710  | 0  |           break;  | 
1711  | 0  |         }  | 
1712  | 0  |     } else { | 
1713  | 0  |         *status = U_MISSING_RESOURCE_ERROR;  | 
1714  | 0  |     }  | 
1715  | 0  |     return NULL;  | 
1716  | 0  | }  | 
1717  |  |  | 
1718  |  | U_CAPI const char * U_EXPORT2  | 
1719  |  | ures_getUTF8StringByIndex(const UResourceBundle *resB,  | 
1720  |  |                           int32_t idx,  | 
1721  |  |                           char *dest, int32_t *pLength,  | 
1722  |  |                           UBool forceCopy,  | 
1723  | 0  |                           UErrorCode *status) { | 
1724  | 0  |     int32_t length16;  | 
1725  | 0  |     const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status);  | 
1726  | 0  |     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);  | 
1727  | 0  | }  | 
1728  |  |  | 
1729  |  | /*U_CAPI const char *ures_getResPath(UResourceBundle *resB) { | 
1730  |  |   return resB->fResPath;  | 
1731  |  | }*/  | 
1732  |  |  | 
1733  |  | U_CAPI UResourceBundle* U_EXPORT2  | 
1734  |  | ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status)   | 
1735  | 0  | { | 
1736  | 0  |   UResourceBundle *first = NULL;   | 
1737  | 0  |   UResourceBundle *result = fillIn;  | 
1738  | 0  |   char *packageName = NULL;  | 
1739  | 0  |   char *pathToResource = NULL, *save = NULL;  | 
1740  | 0  |   char *locale = NULL, *localeEnd = NULL;  | 
1741  | 0  |   int32_t length;  | 
1742  |  | 
  | 
1743  | 0  |   if(status == NULL || U_FAILURE(*status)) { | 
1744  | 0  |     return result;  | 
1745  | 0  |   }  | 
1746  |  |  | 
1747  | 0  |   length = (int32_t)(uprv_strlen(path)+1);  | 
1748  | 0  |   save = pathToResource = (char *)uprv_malloc(length*sizeof(char));  | 
1749  |  |   /* test for NULL */  | 
1750  | 0  |   if(pathToResource == NULL) { | 
1751  | 0  |     *status = U_MEMORY_ALLOCATION_ERROR;  | 
1752  | 0  |     return result;  | 
1753  | 0  |   }  | 
1754  | 0  |   uprv_memcpy(pathToResource, path, length);  | 
1755  |  | 
  | 
1756  | 0  |   locale = pathToResource;  | 
1757  | 0  |   if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */ | 
1758  | 0  |     pathToResource++;  | 
1759  | 0  |     packageName = pathToResource;  | 
1760  | 0  |     pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);  | 
1761  | 0  |     if(pathToResource == NULL) { | 
1762  | 0  |       *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1763  | 0  |     } else { | 
1764  | 0  |       *pathToResource = 0;  | 
1765  | 0  |       locale = pathToResource+1;  | 
1766  | 0  |     }  | 
1767  | 0  |   }  | 
1768  |  | 
  | 
1769  | 0  |   localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);  | 
1770  | 0  |   if(localeEnd != NULL) { | 
1771  | 0  |     *localeEnd = 0;  | 
1772  | 0  |   }  | 
1773  |  | 
  | 
1774  | 0  |   first = ures_open(packageName, locale, status);  | 
1775  |  | 
  | 
1776  | 0  |   if(U_SUCCESS(*status)) { | 
1777  | 0  |     if(localeEnd) { | 
1778  | 0  |       result = ures_findSubResource(first, localeEnd+1, fillIn, status);  | 
1779  | 0  |     } else { | 
1780  | 0  |       result = ures_copyResb(fillIn, first, status);  | 
1781  | 0  |     }  | 
1782  | 0  |     ures_close(first);  | 
1783  | 0  |   }  | 
1784  | 0  |   uprv_free(save);  | 
1785  | 0  |   return result;  | 
1786  | 0  | }  | 
1787  |  |  | 
1788  |  | U_CAPI UResourceBundle* U_EXPORT2  | 
1789  |  | ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status)   | 
1790  | 0  | { | 
1791  | 0  |   Resource res = RES_BOGUS;  | 
1792  | 0  |   UResourceBundle *result = fillIn;  | 
1793  | 0  |   const char *key;  | 
1794  |  | 
  | 
1795  | 0  |   if(status == NULL || U_FAILURE(*status)) { | 
1796  | 0  |     return result;  | 
1797  | 0  |   }  | 
1798  |  |  | 
1799  |  |   /* here we do looping and circular alias checking */  | 
1800  |  |   /* this loop is here because aliasing is resolved on this level, not on res level */  | 
1801  |  |   /* so, when we encounter an alias, it is not an aggregate resource, so we return */  | 
1802  | 0  |   do { | 
1803  | 0  |     res = res_findResource(&(resB->fResData), resB->fRes, &path, &key);   | 
1804  | 0  |     if(res != RES_BOGUS) { | 
1805  | 0  |         result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);  | 
1806  | 0  |         resB = result;  | 
1807  | 0  |     } else { | 
1808  | 0  |         *status = U_MISSING_RESOURCE_ERROR;  | 
1809  | 0  |         break;  | 
1810  | 0  |     }  | 
1811  | 0  |   } while(*path); /* there is more stuff in the path */  | 
1812  |  |  | 
1813  | 0  |   return result;  | 
1814  | 0  | }  | 
1815  |  | U_CAPI const UChar* U_EXPORT2   | 
1816  |  | ures_getStringByKeyWithFallback(const UResourceBundle *resB,   | 
1817  |  |                                 const char* inKey,   | 
1818  |  |                                 int32_t* len,  | 
1819  | 0  |                                 UErrorCode *status) { | 
1820  |  | 
  | 
1821  | 0  |     UResourceBundle stack;  | 
1822  | 0  |     const UChar* retVal = NULL;  | 
1823  | 0  |     ures_initStackObject(&stack);  | 
1824  | 0  |     ures_getByKeyWithFallback(resB, inKey, &stack, status);  | 
1825  | 0  |     int32_t length;  | 
1826  | 0  |     retVal = ures_getString(&stack, &length, status);  | 
1827  | 0  |     ures_close(&stack);  | 
1828  | 0  |     if (U_FAILURE(*status)) { | 
1829  | 0  |         return NULL;  | 
1830  | 0  |     }  | 
1831  | 0  |     if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) { | 
1832  | 0  |         retVal = NULL;  | 
1833  | 0  |         length = 0;  | 
1834  | 0  |         *status = U_MISSING_RESOURCE_ERROR;  | 
1835  | 0  |     }  | 
1836  | 0  |     if (len != NULL) { | 
1837  | 0  |         *len = length;  | 
1838  | 0  |     }  | 
1839  | 0  |     return retVal;  | 
1840  | 0  | }  | 
1841  |  |  | 
1842  |  | /*  | 
1843  |  |   Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".  | 
1844  |  | */    | 
1845  | 0  | static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) { | 
1846  | 0  |   Resource resource = table;  /* The current resource */  | 
1847  | 0  |   icu::CharString path;  | 
1848  | 0  |   UErrorCode errorCode = U_ZERO_ERROR;  | 
1849  | 0  |   path.append(key, errorCode);  | 
1850  | 0  |   if (U_FAILURE(errorCode)) { return RES_BOGUS; } | 
1851  | 0  |   char *pathPart = path.data();  /* Path from current resource to desired resource */  | 
1852  | 0  |   UResType type = (UResType)RES_GET_TYPE(resource);  /* the current resource type */  | 
1853  | 0  |   while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) { | 
1854  | 0  |     char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);  | 
1855  | 0  |     if (nextPathPart != NULL) { | 
1856  | 0  |       *nextPathPart = 0;  /* Terminating null for this part of path. */  | 
1857  | 0  |       nextPathPart++;  | 
1858  | 0  |     } else { | 
1859  | 0  |       nextPathPart = uprv_strchr(pathPart, 0);  | 
1860  | 0  |     }  | 
1861  | 0  |     int32_t t;  | 
1862  | 0  |     const char *pathP = pathPart;  | 
1863  | 0  |     resource = res_getTableItemByKey(pResData, resource, &t, &pathP);  | 
1864  | 0  |     type = (UResType)RES_GET_TYPE(resource);  | 
1865  | 0  |     pathPart = nextPathPart;   | 
1866  | 0  |   }  | 
1867  | 0  |   if (*pathPart) { | 
1868  | 0  |     return RES_BOGUS;  | 
1869  | 0  |   }  | 
1870  | 0  |   return resource;  | 
1871  | 0  | }  | 
1872  |  |  | 
1873  |  | U_CAPI UResourceBundle* U_EXPORT2   | 
1874  |  | ures_getByKeyWithFallback(const UResourceBundle *resB,   | 
1875  |  |                           const char* inKey,   | 
1876  |  |                           UResourceBundle *fillIn,   | 
1877  | 0  |                           UErrorCode *status) { | 
1878  | 0  |     Resource res = RES_BOGUS, rootRes = RES_BOGUS;  | 
1879  |  |     /*UResourceDataEntry *realData = NULL;*/  | 
1880  | 0  |     UResourceBundle *helper = NULL;  | 
1881  |  | 
  | 
1882  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
1883  | 0  |         return fillIn;  | 
1884  | 0  |     }  | 
1885  | 0  |     if(resB == NULL) { | 
1886  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
1887  | 0  |         return fillIn;  | 
1888  | 0  |     }  | 
1889  |  |  | 
1890  | 0  |     int32_t type = RES_GET_TYPE(resB->fRes);  | 
1891  | 0  |     if(URES_IS_TABLE(type)) { | 
1892  | 0  |         res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey);  | 
1893  | 0  |         const char* key = inKey;  | 
1894  | 0  |         if(res == RES_BOGUS) { | 
1895  | 0  |             UResourceDataEntry *dataEntry = resB->fData;  | 
1896  | 0  |             CharString path;  | 
1897  | 0  |             char *myPath = NULL;  | 
1898  | 0  |             const char* resPath = resB->fResPath;  | 
1899  | 0  |             int32_t len = resB->fResPathLen;  | 
1900  | 0  |             while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */ | 
1901  | 0  |                 dataEntry = dataEntry->fParent;  | 
1902  | 0  |                 rootRes = dataEntry->fData.rootRes;  | 
1903  |  | 
  | 
1904  | 0  |                 if(dataEntry->fBogus == U_ZERO_ERROR) { | 
1905  | 0  |                     path.clear();  | 
1906  | 0  |                     if (len > 0) { | 
1907  | 0  |                         path.append(resPath, len, *status);  | 
1908  | 0  |                     }  | 
1909  | 0  |                     path.append(inKey, *status);  | 
1910  | 0  |                     if (U_FAILURE(*status)) { | 
1911  | 0  |                         ures_close(helper);  | 
1912  | 0  |                         return fillIn;  | 
1913  | 0  |                     }  | 
1914  | 0  |                     myPath = path.data();  | 
1915  | 0  |                     key = inKey;  | 
1916  | 0  |                     do { | 
1917  | 0  |                         res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);  | 
1918  | 0  |                         if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) { | 
1919  |  |                             /* We hit an alias, but we didn't finish following the path. */  | 
1920  | 0  |                             helper = init_resb_result(&(dataEntry->fData), res, NULL, -1, dataEntry, resB, 0, helper, status);   | 
1921  |  |                             /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/  | 
1922  | 0  |                             if(helper) { | 
1923  | 0  |                               dataEntry = helper->fData;  | 
1924  | 0  |                               rootRes = helper->fRes;  | 
1925  | 0  |                               resPath = helper->fResPath;  | 
1926  | 0  |                               len = helper->fResPathLen;  | 
1927  |  | 
  | 
1928  | 0  |                             } else { | 
1929  | 0  |                               break;  | 
1930  | 0  |                             }  | 
1931  | 0  |                         } else if (res == RES_BOGUS) { | 
1932  | 0  |                             break;  | 
1933  | 0  |                         }  | 
1934  | 0  |                     } while(*myPath); /* Continue until the whole path is consumed */  | 
1935  | 0  |                 }  | 
1936  | 0  |             }  | 
1937  |  |             /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/  | 
1938  | 0  |             if(res != RES_BOGUS) { | 
1939  |  |               /* check if resB->fResPath gives the right name here */  | 
1940  | 0  |                 if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) { | 
1941  | 0  |                     *status = U_USING_DEFAULT_WARNING;  | 
1942  | 0  |                 } else { | 
1943  | 0  |                     *status = U_USING_FALLBACK_WARNING;  | 
1944  | 0  |                 }  | 
1945  |  | 
  | 
1946  | 0  |                 fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);  | 
1947  | 0  |             } else { | 
1948  | 0  |                 *status = U_MISSING_RESOURCE_ERROR;  | 
1949  | 0  |             }  | 
1950  | 0  |         } else { | 
1951  | 0  |             fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);  | 
1952  | 0  |         }  | 
1953  | 0  |     }   | 
1954  | 0  |     else { | 
1955  | 0  |         *status = U_RESOURCE_TYPE_MISMATCH;  | 
1956  | 0  |     }  | 
1957  | 0  |     ures_close(helper);  | 
1958  | 0  |     return fillIn;  | 
1959  | 0  | }  | 
1960  |  |  | 
1961  |  | namespace { | 
1962  |  |  | 
1963  |  | void getAllItemsWithFallback(  | 
1964  |  |         const UResourceBundle *bundle, ResourceDataValue &value,  | 
1965  |  |         ResourceSink &sink,  | 
1966  | 0  |         UErrorCode &errorCode) { | 
1967  | 0  |     if (U_FAILURE(errorCode)) { return; } | 
1968  |  |     // We recursively enumerate child-first,  | 
1969  |  |     // only storing parent items in the absence of child items.  | 
1970  |  |     // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker  | 
1971  |  |     // to prevent a parent item from being stored.  | 
1972  |  |     //  | 
1973  |  |     // It would be possible to recursively enumerate parent-first,  | 
1974  |  |     // overriding parent items with child items.  | 
1975  |  |     // When the sink sees the no-fallback/no-inheritance marker,  | 
1976  |  |     // then it would remove the parent's item.  | 
1977  |  |     // We would deserialize parent values even though they are overridden in a child bundle.  | 
1978  | 0  |     value.setData(&bundle->fResData);  | 
1979  | 0  |     UResourceDataEntry *parentEntry = bundle->fData->fParent;  | 
1980  | 0  |     UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus);  | 
1981  | 0  |     value.setResource(bundle->fRes, ResourceTracer(bundle));  | 
1982  | 0  |     sink.put(bundle->fKey, value, !hasParent, errorCode);  | 
1983  | 0  |     if (hasParent) { | 
1984  |  |         // We might try to query the sink whether  | 
1985  |  |         // any fallback from the parent bundle is still possible.  | 
1986  |  |  | 
1987  |  |         // Turn the parent UResourceDataEntry into a UResourceBundle,  | 
1988  |  |         // much like in ures_openWithType().  | 
1989  |  |         // TODO: See if we can refactor ures_getByKeyWithFallback()  | 
1990  |  |         // and pull out an inner function that takes and returns a UResourceDataEntry  | 
1991  |  |         // so that we need not create UResourceBundle objects.  | 
1992  | 0  |         UResourceBundle parentBundle;  | 
1993  | 0  |         ures_initStackObject(&parentBundle);  | 
1994  | 0  |         parentBundle.fTopLevelData = parentBundle.fData = parentEntry;  | 
1995  |  |         // TODO: What is the difference between bundle fData and fTopLevelData?  | 
1996  | 0  |         uprv_memcpy(&parentBundle.fResData, &parentEntry->fData, sizeof(ResourceData));  | 
1997  |  |         // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.  | 
1998  | 0  |         parentBundle.fHasFallback = !parentBundle.fResData.noFallback;  | 
1999  | 0  |         parentBundle.fIsTopLevel = TRUE;  | 
2000  | 0  |         parentBundle.fRes = parentBundle.fResData.rootRes;  | 
2001  | 0  |         parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes);  | 
2002  | 0  |         parentBundle.fIndex = -1;  | 
2003  | 0  |         entryIncrease(parentEntry);  | 
2004  |  |  | 
2005  |  |         // Look up the container item in the parent bundle.  | 
2006  | 0  |         UResourceBundle containerBundle;  | 
2007  | 0  |         ures_initStackObject(&containerBundle);  | 
2008  | 0  |         const UResourceBundle *rb;  | 
2009  | 0  |         UErrorCode pathErrorCode = U_ZERO_ERROR;  // Ignore if parents up to root do not have this path.  | 
2010  | 0  |         if (bundle->fResPath == NULL || *bundle->fResPath == 0) { | 
2011  | 0  |             rb = &parentBundle;  | 
2012  | 0  |         } else { | 
2013  | 0  |             rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,  | 
2014  | 0  |                                            &containerBundle, &pathErrorCode);  | 
2015  | 0  |         }  | 
2016  | 0  |         if (U_SUCCESS(pathErrorCode)) { | 
2017  | 0  |             getAllItemsWithFallback(rb, value, sink, errorCode);  | 
2018  | 0  |         }  | 
2019  | 0  |         ures_close(&containerBundle);  | 
2020  | 0  |         ures_close(&parentBundle);  | 
2021  | 0  |     }  | 
2022  | 0  | }  | 
2023  |  |  | 
2024  |  | }  // namespace  | 
2025  |  |  | 
2026  |  | // Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue.  | 
2027  |  | // Unfortunately, the caller must know which subclass to make and pass in.  | 
2028  |  | // Alternatively, we could make it as polymorphic as in Java by  | 
2029  |  | // returning a ResourceValue pointer (possibly wrapped into a LocalPointer)  | 
2030  |  | // that the caller then owns.  | 
2031  |  | //  | 
2032  |  | // Also requires a UResourceBundle fill-in, so that the value's ResourceTracer  | 
2033  |  | // can point to a non-local bundle.  | 
2034  |  | // Without tracing, the child bundle could be a function-local object.  | 
2035  |  | U_CAPI void U_EXPORT2  | 
2036  |  | ures_getValueWithFallback(const UResourceBundle *bundle, const char *path,  | 
2037  |  |                           UResourceBundle *tempFillIn,  | 
2038  | 0  |                           ResourceDataValue &value, UErrorCode &errorCode) { | 
2039  | 0  |     if (U_FAILURE(errorCode)) { return; } | 
2040  | 0  |     if (path == nullptr) { | 
2041  | 0  |         errorCode = U_ILLEGAL_ARGUMENT_ERROR;  | 
2042  | 0  |         return;  | 
2043  | 0  |     }  | 
2044  | 0  |     const UResourceBundle *rb;  | 
2045  | 0  |     if (*path == 0) { | 
2046  |  |         // empty path  | 
2047  | 0  |         rb = bundle;  | 
2048  | 0  |     } else { | 
2049  | 0  |         rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode);  | 
2050  | 0  |         if (U_FAILURE(errorCode)) { | 
2051  | 0  |             return;  | 
2052  | 0  |         }  | 
2053  | 0  |     }  | 
2054  | 0  |     value.setData(&rb->fResData);  | 
2055  | 0  |     value.setResource(rb->fRes, ResourceTracer(rb));  | 
2056  | 0  | }  | 
2057  |  |  | 
2058  |  | U_CAPI void U_EXPORT2  | 
2059  |  | ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,  | 
2060  | 0  |                              icu::ResourceSink &sink, UErrorCode &errorCode) { | 
2061  | 0  |     if (U_FAILURE(errorCode)) { return; } | 
2062  | 0  |     if (path == nullptr) { | 
2063  | 0  |         errorCode = U_ILLEGAL_ARGUMENT_ERROR;  | 
2064  | 0  |         return;  | 
2065  | 0  |     }  | 
2066  | 0  |     StackUResourceBundle stackBundle;  | 
2067  | 0  |     const UResourceBundle *rb;  | 
2068  | 0  |     if (*path == 0) { | 
2069  |  |         // empty path  | 
2070  | 0  |         rb = bundle;  | 
2071  | 0  |     } else { | 
2072  | 0  |         rb = ures_getByKeyWithFallback(bundle, path, stackBundle.getAlias(), &errorCode);  | 
2073  | 0  |         if (U_FAILURE(errorCode)) { | 
2074  | 0  |             return;  | 
2075  | 0  |         }  | 
2076  | 0  |     }  | 
2077  |  |     // Get all table items with fallback.  | 
2078  | 0  |     ResourceDataValue value;  | 
2079  | 0  |     getAllItemsWithFallback(rb, value, sink, errorCode);  | 
2080  | 0  | }  | 
2081  |  |  | 
2082  | 0  | U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) { | 
2083  | 0  |     Resource res = RES_BOGUS;  | 
2084  | 0  |     UResourceDataEntry *realData = NULL;  | 
2085  | 0  |     const char *key = inKey;  | 
2086  |  | 
  | 
2087  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
2088  | 0  |         return fillIn;  | 
2089  | 0  |     }  | 
2090  | 0  |     if(resB == NULL) { | 
2091  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2092  | 0  |         return fillIn;  | 
2093  | 0  |     }  | 
2094  |  |  | 
2095  | 0  |     int32_t type = RES_GET_TYPE(resB->fRes);  | 
2096  | 0  |     if(URES_IS_TABLE(type)) { | 
2097  | 0  |         int32_t t;  | 
2098  | 0  |         res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);  | 
2099  | 0  |         if(res == RES_BOGUS) { | 
2100  | 0  |             key = inKey;  | 
2101  | 0  |             if(resB->fHasFallback == TRUE) { | 
2102  | 0  |                 const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);  | 
2103  | 0  |                 if(U_SUCCESS(*status)) { | 
2104  |  |                   /* check if resB->fResPath gives the right name here */  | 
2105  | 0  |                     return init_resb_result(rd, res, key, -1, realData, resB, 0, fillIn, status);  | 
2106  | 0  |                 } else { | 
2107  | 0  |                     *status = U_MISSING_RESOURCE_ERROR;  | 
2108  | 0  |                 }  | 
2109  | 0  |             } else { | 
2110  | 0  |                 *status = U_MISSING_RESOURCE_ERROR;  | 
2111  | 0  |             }  | 
2112  | 0  |         } else { | 
2113  | 0  |             return init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);  | 
2114  | 0  |         }  | 
2115  | 0  |     }   | 
2116  |  | #if 0  | 
2117  |  |     /* this is a kind of TODO item. If we have an array with an index table, we could do this. */  | 
2118  |  |     /* not currently */  | 
2119  |  |     else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) { | 
2120  |  |         /* here should go a first attempt to locate the key using index table */  | 
2121  |  |         const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);  | 
2122  |  |         if(U_SUCCESS(*status)) { | 
2123  |  |             return init_resb_result(rd, res, key, realData, resB, fillIn, status);  | 
2124  |  |         } else { | 
2125  |  |             *status = U_MISSING_RESOURCE_ERROR;  | 
2126  |  |         }  | 
2127  |  |     }  | 
2128  |  | #endif      | 
2129  | 0  |     else { | 
2130  | 0  |         *status = U_RESOURCE_TYPE_MISMATCH;  | 
2131  | 0  |     }  | 
2132  | 0  |     return fillIn;  | 
2133  | 0  | }  | 
2134  |  |  | 
2135  | 0  | U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) { | 
2136  | 0  |     Resource res = RES_BOGUS;  | 
2137  | 0  |     UResourceDataEntry *realData = NULL;  | 
2138  | 0  |     const char* key = inKey;  | 
2139  |  | 
  | 
2140  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
2141  | 0  |         return NULL;  | 
2142  | 0  |     }  | 
2143  | 0  |     if(resB == NULL) { | 
2144  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2145  | 0  |         return NULL;  | 
2146  | 0  |     }  | 
2147  |  |  | 
2148  | 0  |     int32_t type = RES_GET_TYPE(resB->fRes);  | 
2149  | 0  |     if(URES_IS_TABLE(type)) { | 
2150  | 0  |         int32_t t=0;  | 
2151  |  | 
  | 
2152  | 0  |         res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);  | 
2153  |  | 
  | 
2154  | 0  |         if(res == RES_BOGUS) { | 
2155  | 0  |             key = inKey;  | 
2156  | 0  |             if(resB->fHasFallback == TRUE) { | 
2157  | 0  |                 const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);  | 
2158  | 0  |                 if(U_SUCCESS(*status)) { | 
2159  | 0  |                     switch (RES_GET_TYPE(res)) { | 
2160  | 0  |                     case URES_STRING:  | 
2161  | 0  |                     case URES_STRING_V2:  | 
2162  | 0  |                         return res_getString({resB, key}, rd, res, len); | 
2163  | 0  |                     case URES_ALIAS:  | 
2164  | 0  |                       { | 
2165  | 0  |                         const UChar* result = 0;  | 
2166  | 0  |                         UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);  | 
2167  | 0  |                         result = ures_getString(tempRes, len, status);  | 
2168  | 0  |                         ures_close(tempRes);  | 
2169  | 0  |                         return result;  | 
2170  | 0  |                       }  | 
2171  | 0  |                     default:  | 
2172  | 0  |                         *status = U_RESOURCE_TYPE_MISMATCH;  | 
2173  | 0  |                     }  | 
2174  | 0  |                 } else { | 
2175  | 0  |                     *status = U_MISSING_RESOURCE_ERROR;  | 
2176  | 0  |                 }  | 
2177  | 0  |             } else { | 
2178  | 0  |                 *status = U_MISSING_RESOURCE_ERROR;  | 
2179  | 0  |             }  | 
2180  | 0  |         } else { | 
2181  | 0  |             switch (RES_GET_TYPE(res)) { | 
2182  | 0  |             case URES_STRING:  | 
2183  | 0  |             case URES_STRING_V2:  | 
2184  | 0  |                 return res_getString({resB, key}, &(resB->fResData), res, len); | 
2185  | 0  |             case URES_ALIAS:  | 
2186  | 0  |               { | 
2187  | 0  |                 const UChar* result = 0;  | 
2188  | 0  |                 UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);  | 
2189  | 0  |                 result = ures_getString(tempRes, len, status);  | 
2190  | 0  |                 ures_close(tempRes);  | 
2191  | 0  |                 return result;  | 
2192  | 0  |               }  | 
2193  | 0  |             default:  | 
2194  | 0  |                 *status = U_RESOURCE_TYPE_MISMATCH;  | 
2195  | 0  |             }  | 
2196  | 0  |         }  | 
2197  | 0  |     }   | 
2198  |  | #if 0   | 
2199  |  |     /* this is a kind of TODO item. If we have an array with an index table, we could do this. */  | 
2200  |  |     /* not currently */     | 
2201  |  |     else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) { | 
2202  |  |         /* here should go a first attempt to locate the key using index table */  | 
2203  |  |         const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);  | 
2204  |  |         if(U_SUCCESS(*status)) { | 
2205  |  |             // TODO: Tracing  | 
2206  |  |             return res_getString(rd, res, len);  | 
2207  |  |         } else { | 
2208  |  |             *status = U_MISSING_RESOURCE_ERROR;  | 
2209  |  |         }  | 
2210  |  |     }   | 
2211  |  | #endif      | 
2212  | 0  |     else { | 
2213  | 0  |         *status = U_RESOURCE_TYPE_MISMATCH;  | 
2214  | 0  |     }  | 
2215  | 0  |     return NULL;  | 
2216  | 0  | }  | 
2217  |  |  | 
2218  |  | U_CAPI const char * U_EXPORT2  | 
2219  |  | ures_getUTF8StringByKey(const UResourceBundle *resB,  | 
2220  |  |                         const char *key,  | 
2221  |  |                         char *dest, int32_t *pLength,  | 
2222  |  |                         UBool forceCopy,  | 
2223  | 0  |                         UErrorCode *status) { | 
2224  | 0  |     int32_t length16;  | 
2225  | 0  |     const UChar *s16 = ures_getStringByKey(resB, key, &length16, status);  | 
2226  | 0  |     return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);  | 
2227  | 0  | }  | 
2228  |  |  | 
2229  |  | /* TODO: clean from here down */  | 
2230  |  |  | 
2231  |  | /**  | 
2232  |  |  *  INTERNAL: Get the name of the first real locale (not placeholder)   | 
2233  |  |  *  that has resource bundle data.  | 
2234  |  |  */  | 
2235  |  | U_CAPI const char*  U_EXPORT2  | 
2236  |  | ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)  | 
2237  | 0  | { | 
2238  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
2239  | 0  |         return NULL;  | 
2240  | 0  |     }  | 
2241  | 0  |     if (!resourceBundle) { | 
2242  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2243  | 0  |         return NULL;  | 
2244  | 0  |     } else { | 
2245  | 0  |       return resourceBundle->fData->fName;  | 
2246  | 0  |     }  | 
2247  | 0  | }  | 
2248  |  |  | 
2249  |  | U_CAPI const char* U_EXPORT2   | 
2250  |  | ures_getLocale(const UResourceBundle* resourceBundle,   | 
2251  |  |                UErrorCode* status)  | 
2252  | 0  | { | 
2253  | 0  |   return ures_getLocaleInternal(resourceBundle, status);  | 
2254  | 0  | }  | 
2255  |  |  | 
2256  |  |  | 
2257  |  | U_CAPI const char* U_EXPORT2   | 
2258  |  | ures_getLocaleByType(const UResourceBundle* resourceBundle,   | 
2259  |  |                      ULocDataLocaleType type,   | 
2260  | 0  |                      UErrorCode* status) { | 
2261  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
2262  | 0  |         return NULL;  | 
2263  | 0  |     }  | 
2264  | 0  |     if (!resourceBundle) { | 
2265  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2266  | 0  |         return NULL;  | 
2267  | 0  |     } else { | 
2268  | 0  |         switch(type) { | 
2269  | 0  |         case ULOC_ACTUAL_LOCALE:  | 
2270  | 0  |             return resourceBundle->fData->fName;  | 
2271  | 0  |         case ULOC_VALID_LOCALE:  | 
2272  | 0  |             return resourceBundle->fTopLevelData->fName;  | 
2273  | 0  |         case ULOC_REQUESTED_LOCALE:  | 
2274  | 0  |         default:  | 
2275  | 0  |             *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2276  | 0  |             return NULL;  | 
2277  | 0  |         }  | 
2278  | 0  |     }  | 
2279  | 0  | }  | 
2280  |  |  | 
2281  | 0  | U_CFUNC const char* ures_getName(const UResourceBundle* resB) { | 
2282  | 0  |   if(resB == NULL) { | 
2283  | 0  |     return NULL;  | 
2284  | 0  |   }  | 
2285  |  |  | 
2286  | 0  |   return resB->fData->fName;  | 
2287  | 0  | }  | 
2288  |  |  | 
2289  |  | #ifdef URES_DEBUG  | 
2290  |  | U_CFUNC const char* ures_getPath(const UResourceBundle* resB) { | 
2291  |  |   if(resB == NULL) { | 
2292  |  |     return NULL;  | 
2293  |  |   }  | 
2294  |  |  | 
2295  |  |   return resB->fData->fPath;  | 
2296  |  | }  | 
2297  |  | #endif  | 
2298  |  |  | 
2299  |  | static UResourceBundle*  | 
2300  |  | ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,  | 
2301  | 0  |                   UResOpenType openType, UErrorCode* status) { | 
2302  | 0  |     if(U_FAILURE(*status)) { | 
2303  | 0  |         return NULL;  | 
2304  | 0  |     }  | 
2305  |  |  | 
2306  | 0  |     UResourceDataEntry *entry;  | 
2307  | 0  |     if(openType != URES_OPEN_DIRECT) { | 
2308  |  |         /* first "canonicalize" the locale ID */  | 
2309  | 0  |         char canonLocaleID[ULOC_FULLNAME_CAPACITY];  | 
2310  | 0  |         uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);  | 
2311  | 0  |         if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) { | 
2312  | 0  |             *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2313  | 0  |             return NULL;  | 
2314  | 0  |         }  | 
2315  | 0  |         entry = entryOpen(path, canonLocaleID, openType, status);  | 
2316  | 0  |     } else { | 
2317  | 0  |         entry = entryOpenDirect(path, localeID, status);  | 
2318  | 0  |     }  | 
2319  | 0  |     if(U_FAILURE(*status)) { | 
2320  | 0  |         return NULL;  | 
2321  | 0  |     }  | 
2322  | 0  |     if(entry == NULL) { | 
2323  | 0  |         *status = U_MISSING_RESOURCE_ERROR;  | 
2324  | 0  |         return NULL;  | 
2325  | 0  |     }  | 
2326  |  |  | 
2327  | 0  |     UBool isStackObject;  | 
2328  | 0  |     if(r == NULL) { | 
2329  | 0  |         r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));  | 
2330  | 0  |         if(r == NULL) { | 
2331  | 0  |             entryClose(entry);  | 
2332  | 0  |             *status = U_MEMORY_ALLOCATION_ERROR;  | 
2333  | 0  |             return NULL;  | 
2334  | 0  |         }  | 
2335  | 0  |         isStackObject = FALSE;  | 
2336  | 0  |     } else {  // fill-in | 
2337  | 0  |         isStackObject = ures_isStackObject(r);  | 
2338  | 0  |         ures_closeBundle(r, FALSE);  | 
2339  | 0  |     }  | 
2340  | 0  |     uprv_memset(r, 0, sizeof(UResourceBundle));  | 
2341  | 0  |     ures_setIsStackObject(r, isStackObject);  | 
2342  |  | 
  | 
2343  | 0  |     r->fTopLevelData = r->fData = entry;  | 
2344  | 0  |     uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));  | 
2345  | 0  |     r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;  | 
2346  | 0  |     r->fIsTopLevel = TRUE;  | 
2347  | 0  |     r->fRes = r->fResData.rootRes;  | 
2348  | 0  |     r->fSize = res_countArrayItems(&(r->fResData), r->fRes);  | 
2349  | 0  |     r->fIndex = -1;  | 
2350  |  | 
  | 
2351  | 0  |     ResourceTracer(r).traceOpen();  | 
2352  |  | 
  | 
2353  | 0  |     return r;  | 
2354  | 0  | }  | 
2355  |  |  | 
2356  |  | U_CAPI UResourceBundle* U_EXPORT2  | 
2357  | 0  | ures_open(const char* path, const char* localeID, UErrorCode* status) { | 
2358  | 0  |     return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);  | 
2359  | 0  | }  | 
2360  |  |  | 
2361  |  | U_CAPI UResourceBundle* U_EXPORT2  | 
2362  | 0  | ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) { | 
2363  | 0  |     return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);  | 
2364  | 0  | }  | 
2365  |  |  | 
2366  |  | /**  | 
2367  |  |  *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed   | 
2368  |  |  *  or sought. However, alias substitution will happen!  | 
2369  |  |  */  | 
2370  |  | U_CAPI UResourceBundle*  U_EXPORT2  | 
2371  | 0  | ures_openDirect(const char* path, const char* localeID, UErrorCode* status) { | 
2372  | 0  |     return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);  | 
2373  | 0  | }  | 
2374  |  |  | 
2375  |  | /**  | 
2376  |  |  *  Internal API: This function is used to open a resource bundle   | 
2377  |  |  *  proper fallback chaining is executed while initialization.   | 
2378  |  |  *  The result is stored in cache for later fallback search.  | 
2379  |  |  *   | 
2380  |  |  * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle.  | 
2381  |  |  */  | 
2382  |  | U_CAPI void U_EXPORT2  | 
2383  |  | ures_openFillIn(UResourceBundle *r, const char* path,  | 
2384  | 0  |                 const char* localeID, UErrorCode* status) { | 
2385  | 0  |     if(U_SUCCESS(*status) && r == NULL) { | 
2386  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2387  | 0  |         return;  | 
2388  | 0  |     }  | 
2389  | 0  |     ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);  | 
2390  | 0  | }  | 
2391  |  |  | 
2392  |  | /**  | 
2393  |  |  * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle.  | 
2394  |  |  */  | 
2395  |  | U_CAPI void U_EXPORT2  | 
2396  | 0  | ures_openDirectFillIn(UResourceBundle *r, const char* path, const char* localeID, UErrorCode* status) { | 
2397  | 0  |     if(U_SUCCESS(*status) && r == NULL) { | 
2398  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2399  | 0  |         return;  | 
2400  | 0  |     }  | 
2401  | 0  |     ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status);  | 
2402  | 0  | }  | 
2403  |  |  | 
2404  |  | /**  | 
2405  |  |  *  API: Counts members. For arrays and tables, returns number of resources.  | 
2406  |  |  *  For strings, returns 1.  | 
2407  |  |  */  | 
2408  |  | U_CAPI int32_t  U_EXPORT2  | 
2409  |  | ures_countArrayItems(const UResourceBundle* resourceBundle,  | 
2410  |  |                   const char* resourceKey,  | 
2411  |  |                   UErrorCode* status)  | 
2412  | 0  | { | 
2413  | 0  |     UResourceBundle resData;  | 
2414  | 0  |     ures_initStackObject(&resData);  | 
2415  | 0  |     if (status==NULL || U_FAILURE(*status)) { | 
2416  | 0  |         return 0;  | 
2417  | 0  |     }  | 
2418  | 0  |     if(resourceBundle == NULL) { | 
2419  | 0  |         *status = U_ILLEGAL_ARGUMENT_ERROR;  | 
2420  | 0  |         return 0;  | 
2421  | 0  |     }  | 
2422  | 0  |     ures_getByKey(resourceBundle, resourceKey, &resData, status);  | 
2423  |  |       | 
2424  | 0  |     if(resData.fResData.data != NULL) { | 
2425  | 0  |         int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);  | 
2426  | 0  |         ures_close(&resData);  | 
2427  | 0  |         return result;  | 
2428  | 0  |     } else { | 
2429  | 0  |         *status = U_MISSING_RESOURCE_ERROR;  | 
2430  | 0  |         ures_close(&resData);  | 
2431  | 0  |         return 0;  | 
2432  | 0  |     }  | 
2433  | 0  | }  | 
2434  |  |  | 
2435  |  | /**  | 
2436  |  |  * Internal function.  | 
2437  |  |  * Return the version number associated with this ResourceBundle as a string.  | 
2438  |  |  *  | 
2439  |  |  * @param resourceBundle The resource bundle for which the version is checked.  | 
2440  |  |  * @return  A version number string as specified in the resource bundle or its parent.  | 
2441  |  |  *          The caller does not own this string.  | 
2442  |  |  * @see ures_getVersion  | 
2443  |  |  * @internal  | 
2444  |  |  */  | 
2445  |  | U_CAPI const char* U_EXPORT2   | 
2446  |  | ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)  | 
2447  | 0  | { | 
2448  | 0  |     if (!resourceBundle) return NULL;  | 
2449  |  |  | 
2450  | 0  |     if(resourceBundle->fVersion == NULL) { | 
2451  |  |  | 
2452  |  |         /* If the version ID has not been built yet, then do so.  Retrieve */  | 
2453  |  |         /* the minor version from the file. */  | 
2454  | 0  |         UErrorCode status = U_ZERO_ERROR;  | 
2455  | 0  |         int32_t minor_len = 0;  | 
2456  | 0  |         int32_t len;  | 
2457  |  | 
  | 
2458  | 0  |         const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);  | 
2459  |  |           | 
2460  |  |         /* Determine the length of of the final version string.  This is */  | 
2461  |  |         /* the length of the major part + the length of the separator */  | 
2462  |  |         /* (==1) + the length of the minor part (+ 1 for the zero byte at */  | 
2463  |  |         /* the end). */  | 
2464  |  | 
  | 
2465  | 0  |         len = (minor_len > 0) ? minor_len : 1;  | 
2466  |  |       | 
2467  |  |         /* Allocate the string, and build it up. */  | 
2468  |  |         /* + 1 for zero byte */  | 
2469  |  |  | 
2470  |  | 
  | 
2471  | 0  |         ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len);   | 
2472  |  |         /* Check for null pointer. */  | 
2473  | 0  |         if (((UResourceBundle *)resourceBundle)->fVersion == NULL) { | 
2474  | 0  |             return NULL;  | 
2475  | 0  |         }  | 
2476  |  |          | 
2477  | 0  |         if(minor_len > 0) { | 
2478  | 0  |             u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);  | 
2479  | 0  |             resourceBundle->fVersion[len] =  '\0';  | 
2480  | 0  |         }  | 
2481  | 0  |         else { | 
2482  | 0  |             uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);  | 
2483  | 0  |         }  | 
2484  | 0  |     }  | 
2485  |  |  | 
2486  | 0  |     return resourceBundle->fVersion;  | 
2487  | 0  | }  | 
2488  |  |  | 
2489  |  | U_CAPI const char*  U_EXPORT2  | 
2490  |  | ures_getVersionNumber(const UResourceBundle*   resourceBundle)  | 
2491  | 0  | { | 
2492  | 0  |     return ures_getVersionNumberInternal(resourceBundle);  | 
2493  | 0  | }  | 
2494  |  |  | 
2495  | 0  | U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) { | 
2496  | 0  |     if (!resB) return;  | 
2497  |  |  | 
2498  | 0  |     u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));  | 
2499  | 0  | }  | 
2500  |  |  | 
2501  |  | /** Tree support functions *******************************/  | 
2502  | 0  | #define INDEX_LOCALE_NAME "res_index"  | 
2503  | 0  | #define INDEX_TAG         "InstalledLocales"  | 
2504  | 0  | #define DEFAULT_TAG       "default"  | 
2505  |  |  | 
2506  |  | #if defined(URES_TREE_DEBUG)  | 
2507  |  | #include <stdio.h>  | 
2508  |  | #endif  | 
2509  |  |  | 
2510  |  | typedef struct ULocalesContext { | 
2511  |  |     UResourceBundle installed;  | 
2512  |  |     UResourceBundle curr;  | 
2513  |  | } ULocalesContext;  | 
2514  |  |  | 
2515  |  | static void U_CALLCONV  | 
2516  | 0  | ures_loc_closeLocales(UEnumeration *enumerator) { | 
2517  | 0  |     ULocalesContext *ctx = (ULocalesContext *)enumerator->context;  | 
2518  | 0  |     ures_close(&ctx->curr);  | 
2519  | 0  |     ures_close(&ctx->installed);  | 
2520  | 0  |     uprv_free(ctx);  | 
2521  | 0  |     uprv_free(enumerator);  | 
2522  | 0  | }  | 
2523  |  |  | 
2524  |  | static int32_t U_CALLCONV  | 
2525  | 0  | ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) { | 
2526  | 0  |     ULocalesContext *ctx = (ULocalesContext *)en->context;  | 
2527  | 0  |     return ures_getSize(&ctx->installed);  | 
2528  | 0  | }  | 
2529  |  |  | 
2530  |  | U_CDECL_BEGIN  | 
2531  |  |  | 
2532  |  |  | 
2533  |  | static const char * U_CALLCONV  | 
2534  |  | ures_loc_nextLocale(UEnumeration* en,  | 
2535  |  |                     int32_t* resultLength,  | 
2536  | 0  |                     UErrorCode* status) { | 
2537  | 0  |     ULocalesContext *ctx = (ULocalesContext *)en->context;  | 
2538  | 0  |     UResourceBundle *res = &(ctx->installed);  | 
2539  | 0  |     UResourceBundle *k = NULL;  | 
2540  | 0  |     const char *result = NULL;  | 
2541  | 0  |     int32_t len = 0;  | 
2542  | 0  |     if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status)) != 0) { | 
2543  | 0  |         result = ures_getKey(k);  | 
2544  | 0  |         len = (int32_t)uprv_strlen(result);  | 
2545  | 0  |     }  | 
2546  | 0  |     if (resultLength) { | 
2547  | 0  |         *resultLength = len;  | 
2548  | 0  |     }  | 
2549  | 0  |     return result;  | 
2550  | 0  | }  | 
2551  |  |  | 
2552  |  | static void U_CALLCONV   | 
2553  |  | ures_loc_resetLocales(UEnumeration* en,   | 
2554  | 0  |                       UErrorCode* /*status*/) { | 
2555  | 0  |     UResourceBundle *res = &((ULocalesContext *)en->context)->installed;  | 
2556  | 0  |     ures_resetIterator(res);  | 
2557  | 0  | }  | 
2558  |  |  | 
2559  |  | U_CDECL_END  | 
2560  |  |  | 
2561  |  | static const UEnumeration gLocalesEnum = { | 
2562  |  |     NULL,  | 
2563  |  |         NULL,  | 
2564  |  |         ures_loc_closeLocales,  | 
2565  |  |         ures_loc_countLocales,  | 
2566  |  |         uenum_unextDefault,  | 
2567  |  |         ures_loc_nextLocale,  | 
2568  |  |         ures_loc_resetLocales  | 
2569  |  | };  | 
2570  |  |  | 
2571  |  |  | 
2572  |  | U_CAPI UEnumeration* U_EXPORT2  | 
2573  |  | ures_openAvailableLocales(const char *path, UErrorCode *status)  | 
2574  | 0  | { | 
2575  | 0  |     UResourceBundle *idx = NULL;  | 
2576  | 0  |     UEnumeration *en = NULL;  | 
2577  | 0  |     ULocalesContext *myContext = NULL;  | 
2578  |  | 
  | 
2579  | 0  |     if(U_FAILURE(*status)) { | 
2580  | 0  |         return NULL;  | 
2581  | 0  |     }  | 
2582  | 0  |     myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));  | 
2583  | 0  |     en =  (UEnumeration *)uprv_malloc(sizeof(UEnumeration));  | 
2584  | 0  |     if(!en || !myContext) { | 
2585  | 0  |         *status = U_MEMORY_ALLOCATION_ERROR;  | 
2586  | 0  |         uprv_free(en);  | 
2587  | 0  |         uprv_free(myContext);  | 
2588  | 0  |         return NULL;  | 
2589  | 0  |     }  | 
2590  | 0  |     uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));  | 
2591  |  | 
  | 
2592  | 0  |     ures_initStackObject(&myContext->installed);  | 
2593  | 0  |     ures_initStackObject(&myContext->curr);  | 
2594  | 0  |     idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);  | 
2595  | 0  |     ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);  | 
2596  | 0  |     if(U_SUCCESS(*status)) { | 
2597  |  | #if defined(URES_TREE_DEBUG)  | 
2598  |  |         fprintf(stderr, "Got %s::%s::[%s] : %s\n",   | 
2599  |  |             path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));  | 
2600  |  | #endif  | 
2601  | 0  |         en->context = myContext;  | 
2602  | 0  |     } else { | 
2603  |  | #if defined(URES_TREE_DEBUG)  | 
2604  |  |         fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));  | 
2605  |  | #endif  | 
2606  | 0  |         ures_close(&myContext->installed);  | 
2607  | 0  |         uprv_free(myContext);  | 
2608  | 0  |         uprv_free(en);  | 
2609  | 0  |         en = NULL;  | 
2610  | 0  |     }  | 
2611  |  |       | 
2612  | 0  |     ures_close(idx);  | 
2613  |  |       | 
2614  | 0  |     return en;  | 
2615  | 0  | }  | 
2616  |  |  | 
2617  | 0  | static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) { | 
2618  | 0  |     const char *loc;  | 
2619  | 0  |     while ((loc = uenum_next(locEnum, NULL, status)) != NULL) { | 
2620  | 0  |         if (uprv_strcmp(loc, locToSearch) == 0) { | 
2621  | 0  |             return TRUE;  | 
2622  | 0  |         }  | 
2623  | 0  |     }  | 
2624  | 0  |     return FALSE;  | 
2625  | 0  | }  | 
2626  |  |  | 
2627  |  | U_CAPI int32_t U_EXPORT2  | 
2628  |  | ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,  | 
2629  |  |                              const char *path, const char *resName, const char *keyword, const char *locid,  | 
2630  |  |                              UBool *isAvailable, UBool omitDefault, UErrorCode *status)  | 
2631  | 0  | { | 
2632  | 0  |     char kwVal[1024] = ""; /* value of keyword 'keyword' */  | 
2633  | 0  |     char defVal[1024] = ""; /* default value for given locale */  | 
2634  | 0  |     char defLoc[1024] = ""; /* default value for given locale */  | 
2635  | 0  |     char base[1024] = ""; /* base locale */  | 
2636  | 0  |     char found[1024] = "";  | 
2637  | 0  |     char parent[1024] = "";  | 
2638  | 0  |     char full[1024] = "";  | 
2639  | 0  |     UResourceBundle bund1, bund2;  | 
2640  | 0  |     UResourceBundle *res = NULL;  | 
2641  | 0  |     UErrorCode subStatus = U_ZERO_ERROR;  | 
2642  | 0  |     int32_t length = 0;  | 
2643  | 0  |     if(U_FAILURE(*status)) return 0;  | 
2644  | 0  |     uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);  | 
2645  | 0  |     if(!uprv_strcmp(kwVal, DEFAULT_TAG)) { | 
2646  | 0  |         kwVal[0]=0;  | 
2647  | 0  |     }  | 
2648  | 0  |     uloc_getBaseName(locid, base, 1024-1,&subStatus);  | 
2649  |  | #if defined(URES_TREE_DEBUG)  | 
2650  |  |     fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n",   | 
2651  |  |             locid, keyword, kwVal, base, u_errorName(subStatus));  | 
2652  |  | #endif  | 
2653  | 0  |     ures_initStackObject(&bund1);  | 
2654  | 0  |     ures_initStackObject(&bund2);  | 
2655  |  |       | 
2656  |  |       | 
2657  | 0  |     uprv_strcpy(parent, base);  | 
2658  | 0  |     uprv_strcpy(found, base);  | 
2659  |  | 
  | 
2660  | 0  |     if(isAvailable) {  | 
2661  | 0  |         UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);  | 
2662  | 0  |         *isAvailable = TRUE;  | 
2663  | 0  |         if (U_SUCCESS(subStatus)) { | 
2664  | 0  |             *isAvailable = isLocaleInList(locEnum, parent, &subStatus);  | 
2665  | 0  |         }  | 
2666  | 0  |         uenum_close(locEnum);  | 
2667  | 0  |     }  | 
2668  |  | 
  | 
2669  | 0  |     if(U_FAILURE(subStatus)) { | 
2670  | 0  |         *status = subStatus;  | 
2671  | 0  |         return 0;  | 
2672  | 0  |     }  | 
2673  |  |       | 
2674  | 0  |     do { | 
2675  | 0  |         subStatus = U_ZERO_ERROR;  | 
2676  | 0  |         res = ures_open(path, parent, &subStatus);  | 
2677  | 0  |         if(((subStatus == U_USING_FALLBACK_WARNING) ||  | 
2678  | 0  |             (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)  | 
2679  | 0  |         { | 
2680  | 0  |             *isAvailable = FALSE;  | 
2681  | 0  |         }  | 
2682  | 0  |         isAvailable = NULL; /* only want to set this the first time around */  | 
2683  |  |           | 
2684  |  | #if defined(URES_TREE_DEBUG)  | 
2685  |  |         fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));  | 
2686  |  | #endif  | 
2687  | 0  |         if(U_FAILURE(subStatus)) { | 
2688  | 0  |             *status = subStatus;  | 
2689  | 0  |         } else if(subStatus == U_ZERO_ERROR) { | 
2690  | 0  |             ures_getByKey(res,resName,&bund1, &subStatus);  | 
2691  | 0  |             if(subStatus == U_ZERO_ERROR) { | 
2692  | 0  |                 const UChar *defUstr;  | 
2693  | 0  |                 int32_t defLen;  | 
2694  |  |                 /* look for default item */  | 
2695  |  | #if defined(URES_TREE_DEBUG)  | 
2696  |  |                 fprintf(stderr, "%s;%s : loaded default -> %s\n",  | 
2697  |  |                     path?path:"ICUDATA", parent, u_errorName(subStatus));  | 
2698  |  | #endif  | 
2699  | 0  |                 defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);  | 
2700  | 0  |                 if(U_SUCCESS(subStatus) && defLen) { | 
2701  | 0  |                     u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));  | 
2702  |  | #if defined(URES_TREE_DEBUG)  | 
2703  |  |                     fprintf(stderr, "%s;%s -> default %s=%s,  %s\n",   | 
2704  |  |                         path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));  | 
2705  |  | #endif  | 
2706  | 0  |                     uprv_strcpy(defLoc, parent);  | 
2707  | 0  |                     if(kwVal[0]==0) { | 
2708  | 0  |                         uprv_strcpy(kwVal, defVal);  | 
2709  |  | #if defined(URES_TREE_DEBUG)  | 
2710  |  |                         fprintf(stderr, "%s;%s -> kwVal =  %s\n",   | 
2711  |  |                             path?path:"ICUDATA", parent, keyword, kwVal);  | 
2712  |  | #endif  | 
2713  | 0  |                     }  | 
2714  | 0  |                 }  | 
2715  | 0  |             }  | 
2716  | 0  |         }  | 
2717  |  |           | 
2718  | 0  |         subStatus = U_ZERO_ERROR;  | 
2719  |  | 
  | 
2720  | 0  |         if (res != NULL) { | 
2721  | 0  |             uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));  | 
2722  | 0  |         }  | 
2723  |  | 
  | 
2724  | 0  |         uloc_getParent(found,parent,sizeof(parent),&subStatus);  | 
2725  | 0  |         ures_close(res);  | 
2726  | 0  |     } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));  | 
2727  |  |       | 
2728  |  |     /* Now, see if we can find the kwVal collator.. start the search over.. */  | 
2729  | 0  |     uprv_strcpy(parent, base);  | 
2730  | 0  |     uprv_strcpy(found, base);  | 
2731  |  |       | 
2732  | 0  |     do { | 
2733  | 0  |         subStatus = U_ZERO_ERROR;  | 
2734  | 0  |         res = ures_open(path, parent, &subStatus);  | 
2735  | 0  |         if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) { | 
2736  | 0  |             *isAvailable = FALSE;  | 
2737  | 0  |         }  | 
2738  | 0  |         isAvailable = NULL; /* only want to set this the first time around */  | 
2739  |  |           | 
2740  |  | #if defined(URES_TREE_DEBUG)  | 
2741  |  |         fprintf(stderr, "%s;%s -> %s (looking for %s)\n",   | 
2742  |  |             path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);  | 
2743  |  | #endif  | 
2744  | 0  |         if(U_FAILURE(subStatus)) { | 
2745  | 0  |             *status = subStatus;  | 
2746  | 0  |         } else if(subStatus == U_ZERO_ERROR) { | 
2747  | 0  |             ures_getByKey(res,resName,&bund1, &subStatus);  | 
2748  |  | #if defined(URES_TREE_DEBUG)  | 
2749  |  | /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));  | 
2750  |  | #endif  | 
2751  | 0  |             if(subStatus == U_ZERO_ERROR) { | 
2752  | 0  |                 ures_getByKey(&bund1, kwVal, &bund2, &subStatus);  | 
2753  |  | #if defined(URES_TREE_DEBUG)  | 
2754  |  | /**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));  | 
2755  |  | #endif  | 
2756  | 0  |                 if(subStatus == U_ZERO_ERROR) { | 
2757  |  | #if defined(URES_TREE_DEBUG)  | 
2758  |  |                     fprintf(stderr, "%s;%s -> full0 %s=%s,  %s\n",   | 
2759  |  |                         path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));  | 
2760  |  | #endif  | 
2761  | 0  |                     uprv_strcpy(full, parent);  | 
2762  | 0  |                     if(*full == 0) { | 
2763  | 0  |                         uprv_strcpy(full, "root");  | 
2764  | 0  |                     }  | 
2765  |  |                         /* now, recalculate default kw if need be */  | 
2766  | 0  |                         if(uprv_strlen(defLoc) > uprv_strlen(full)) { | 
2767  | 0  |                           const UChar *defUstr;  | 
2768  | 0  |                           int32_t defLen;  | 
2769  |  |                           /* look for default item */  | 
2770  |  | #if defined(URES_TREE_DEBUG)  | 
2771  |  |                             fprintf(stderr, "%s;%s -> recalculating Default0\n",   | 
2772  |  |                                     path?path:"ICUDATA", full);  | 
2773  |  | #endif  | 
2774  | 0  |                           defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);  | 
2775  | 0  |                           if(U_SUCCESS(subStatus) && defLen) { | 
2776  | 0  |                             u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));  | 
2777  |  | #if defined(URES_TREE_DEBUG)  | 
2778  |  |                             fprintf(stderr, "%s;%s -> default0 %s=%s,  %s\n",   | 
2779  |  |                                     path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));  | 
2780  |  | #endif  | 
2781  | 0  |                             uprv_strcpy(defLoc, full);  | 
2782  | 0  |                           }  | 
2783  | 0  |                         } /* end of recalculate default KW */  | 
2784  |  | #if defined(URES_TREE_DEBUG)  | 
2785  |  |                         else { | 
2786  |  |                           fprintf(stderr, "No trim0,  %s <= %s\n", defLoc, full);  | 
2787  |  |                         }  | 
2788  |  | #endif  | 
2789  | 0  |                 } else { | 
2790  |  | #if defined(URES_TREE_DEBUG)  | 
2791  |  |                     fprintf(stderr, "err=%s in %s looking for %s\n",   | 
2792  |  |                         u_errorName(subStatus), parent, kwVal);  | 
2793  |  | #endif  | 
2794  | 0  |                 }  | 
2795  | 0  |             }  | 
2796  | 0  |         }  | 
2797  |  |           | 
2798  | 0  |         subStatus = U_ZERO_ERROR;  | 
2799  |  |           | 
2800  | 0  |         uprv_strcpy(found, parent);  | 
2801  | 0  |         uloc_getParent(found,parent,1023,&subStatus);  | 
2802  | 0  |         ures_close(res);  | 
2803  | 0  |     } while(!full[0] && *found && U_SUCCESS(*status));  | 
2804  |  |       | 
2805  | 0  |     if((full[0]==0) && uprv_strcmp(kwVal, defVal)) { | 
2806  |  | #if defined(URES_TREE_DEBUG)  | 
2807  |  |         fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);  | 
2808  |  | #endif  | 
2809  | 0  |         uprv_strcpy(kwVal, defVal);  | 
2810  | 0  |         uprv_strcpy(parent, base);  | 
2811  | 0  |         uprv_strcpy(found, base);  | 
2812  |  |           | 
2813  | 0  |         do { /* search for 'default' named item */ | 
2814  | 0  |             subStatus = U_ZERO_ERROR;  | 
2815  | 0  |             res = ures_open(path, parent, &subStatus);  | 
2816  | 0  |             if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) { | 
2817  | 0  |                 *isAvailable = FALSE;  | 
2818  | 0  |             }  | 
2819  | 0  |             isAvailable = NULL; /* only want to set this the first time around */  | 
2820  |  |               | 
2821  |  | #if defined(URES_TREE_DEBUG)  | 
2822  |  |             fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",  | 
2823  |  |                 path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);  | 
2824  |  | #endif  | 
2825  | 0  |             if(U_FAILURE(subStatus)) { | 
2826  | 0  |                 *status = subStatus;  | 
2827  | 0  |             } else if(subStatus == U_ZERO_ERROR) { | 
2828  | 0  |                 ures_getByKey(res,resName,&bund1, &subStatus);  | 
2829  | 0  |                 if(subStatus == U_ZERO_ERROR) { | 
2830  | 0  |                     ures_getByKey(&bund1, kwVal, &bund2, &subStatus);  | 
2831  | 0  |                     if(subStatus == U_ZERO_ERROR) { | 
2832  |  | #if defined(URES_TREE_DEBUG)  | 
2833  |  |                         fprintf(stderr, "%s;%s -> full1 %s=%s,  %s\n", path?path:"ICUDATA",  | 
2834  |  |                             parent, keyword, kwVal, u_errorName(subStatus));  | 
2835  |  | #endif  | 
2836  | 0  |                         uprv_strcpy(full, parent);  | 
2837  | 0  |                         if(*full == 0) { | 
2838  | 0  |                             uprv_strcpy(full, "root");  | 
2839  | 0  |                         }  | 
2840  |  |                           | 
2841  |  |                         /* now, recalculate default kw if need be */  | 
2842  | 0  |                         if(uprv_strlen(defLoc) > uprv_strlen(full)) { | 
2843  | 0  |                           const UChar *defUstr;  | 
2844  | 0  |                           int32_t defLen;  | 
2845  |  |                           /* look for default item */  | 
2846  |  | #if defined(URES_TREE_DEBUG)  | 
2847  |  |                             fprintf(stderr, "%s;%s -> recalculating Default1\n",   | 
2848  |  |                                     path?path:"ICUDATA", full);  | 
2849  |  | #endif  | 
2850  | 0  |                           defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);  | 
2851  | 0  |                           if(U_SUCCESS(subStatus) && defLen) { | 
2852  | 0  |                             u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));  | 
2853  |  | #if defined(URES_TREE_DEBUG)  | 
2854  |  |                             fprintf(stderr, "%s;%s -> default %s=%s,  %s\n",   | 
2855  |  |                                     path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));  | 
2856  |  | #endif  | 
2857  | 0  |                             uprv_strcpy(defLoc, full);  | 
2858  | 0  |                           }  | 
2859  | 0  |                         } /* end of recalculate default KW */  | 
2860  |  | #if defined(URES_TREE_DEBUG)  | 
2861  |  |                         else { | 
2862  |  |                           fprintf(stderr, "No trim1,  %s <= %s\n", defLoc, full);  | 
2863  |  |                         }  | 
2864  |  | #endif  | 
2865  | 0  |                     }  | 
2866  | 0  |                 }  | 
2867  | 0  |             }  | 
2868  | 0  |             subStatus = U_ZERO_ERROR;  | 
2869  |  |               | 
2870  | 0  |             uprv_strcpy(found, parent);  | 
2871  | 0  |             uloc_getParent(found,parent,1023,&subStatus);  | 
2872  | 0  |             ures_close(res);  | 
2873  | 0  |         } while(!full[0] && *found && U_SUCCESS(*status));  | 
2874  | 0  |     }  | 
2875  |  |       | 
2876  | 0  |     if(U_SUCCESS(*status)) { | 
2877  | 0  |         if(!full[0]) { | 
2878  |  | #if defined(URES_TREE_DEBUG)  | 
2879  |  |           fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);  | 
2880  |  | #endif  | 
2881  | 0  |           *status = U_MISSING_RESOURCE_ERROR;  | 
2882  | 0  |         } else if(omitDefault) { | 
2883  |  | #if defined(URES_TREE_DEBUG)  | 
2884  |  |           fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);  | 
2885  |  | #endif          | 
2886  | 0  |           if(uprv_strlen(defLoc) <= uprv_strlen(full)) { | 
2887  |  |             /* found the keyword in a *child* of where the default tag was present. */  | 
2888  | 0  |             if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */ | 
2889  |  |               /* and the default is in or in an ancestor of the current locale */  | 
2890  |  | #if defined(URES_TREE_DEBUG)  | 
2891  |  |               fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);  | 
2892  |  | #endif  | 
2893  | 0  |               kwVal[0]=0;  | 
2894  | 0  |             }  | 
2895  | 0  |           }  | 
2896  | 0  |         }  | 
2897  | 0  |         uprv_strcpy(found, full);  | 
2898  | 0  |         if(kwVal[0]) { | 
2899  | 0  |             uprv_strcat(found, "@");  | 
2900  | 0  |             uprv_strcat(found, keyword);  | 
2901  | 0  |             uprv_strcat(found, "=");  | 
2902  | 0  |             uprv_strcat(found, kwVal);  | 
2903  | 0  |         } else if(!omitDefault) { | 
2904  | 0  |             uprv_strcat(found, "@");  | 
2905  | 0  |             uprv_strcat(found, keyword);  | 
2906  | 0  |             uprv_strcat(found, "=");  | 
2907  | 0  |             uprv_strcat(found, defVal);  | 
2908  | 0  |         }  | 
2909  | 0  |     }  | 
2910  |  |     /* we found the default locale - no need to repeat it.*/  | 
2911  |  |       | 
2912  | 0  |     ures_close(&bund1);  | 
2913  | 0  |     ures_close(&bund2);  | 
2914  |  |       | 
2915  | 0  |     length = (int32_t)uprv_strlen(found);  | 
2916  |  | 
  | 
2917  | 0  |     if(U_SUCCESS(*status)) { | 
2918  | 0  |         int32_t copyLength = uprv_min(length, resultCapacity);  | 
2919  | 0  |         if(copyLength>0) { | 
2920  | 0  |             uprv_strncpy(result, found, copyLength);  | 
2921  | 0  |         }  | 
2922  | 0  |         if(length == 0) { | 
2923  | 0  |           *status = U_MISSING_RESOURCE_ERROR;   | 
2924  | 0  |         }  | 
2925  | 0  |     } else { | 
2926  | 0  |         length = 0;  | 
2927  | 0  |         result[0]=0;  | 
2928  | 0  |     }  | 
2929  | 0  |     return u_terminateChars(result, resultCapacity, length, status);  | 
2930  | 0  | }  | 
2931  |  |  | 
2932  |  | U_CAPI UEnumeration* U_EXPORT2  | 
2933  |  | ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)  | 
2934  | 0  | { | 
2935  | 0  | #define VALUES_BUF_SIZE 2048  | 
2936  | 0  | #define VALUES_LIST_SIZE 512  | 
2937  |  |       | 
2938  | 0  |     char       valuesBuf[VALUES_BUF_SIZE];  | 
2939  | 0  |     int32_t    valuesIndex = 0;  | 
2940  | 0  |     const char *valuesList[VALUES_LIST_SIZE];  | 
2941  | 0  |     int32_t    valuesCount = 0;  | 
2942  |  |       | 
2943  | 0  |     const char *locale;  | 
2944  | 0  |     int32_t     locLen;  | 
2945  |  |       | 
2946  | 0  |     UEnumeration *locs = NULL;  | 
2947  |  |       | 
2948  | 0  |     UResourceBundle    item;  | 
2949  | 0  |     UResourceBundle    subItem;  | 
2950  |  |       | 
2951  | 0  |     ures_initStackObject(&item);  | 
2952  | 0  |     ures_initStackObject(&subItem);  | 
2953  | 0  |     locs = ures_openAvailableLocales(path, status);  | 
2954  |  |       | 
2955  | 0  |     if(U_FAILURE(*status)) { | 
2956  | 0  |         ures_close(&item);  | 
2957  | 0  |         ures_close(&subItem);  | 
2958  | 0  |         return NULL;  | 
2959  | 0  |     }  | 
2960  |  |       | 
2961  | 0  |     valuesBuf[0]=0;  | 
2962  | 0  |     valuesBuf[1]=0;  | 
2963  |  |       | 
2964  | 0  |     while((locale = uenum_next(locs, &locLen, status)) != 0) { | 
2965  | 0  |         UResourceBundle   *bund = NULL;  | 
2966  | 0  |         UResourceBundle   *subPtr = NULL;  | 
2967  | 0  |         UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */  | 
2968  | 0  |         bund = ures_open(path, locale, &subStatus);  | 
2969  |  |           | 
2970  |  | #if defined(URES_TREE_DEBUG)  | 
2971  |  |         if(!bund || U_FAILURE(subStatus)) { | 
2972  |  |             fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n",   | 
2973  |  |                 path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));  | 
2974  |  |         }  | 
2975  |  | #endif  | 
2976  |  |           | 
2977  | 0  |         ures_getByKey(bund, keyword, &item, &subStatus);  | 
2978  |  |           | 
2979  | 0  |         if(!bund || U_FAILURE(subStatus)) { | 
2980  |  | #if defined(URES_TREE_DEBUG)  | 
2981  |  |             fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n",   | 
2982  |  |                 path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));  | 
2983  |  | #endif  | 
2984  | 0  |             ures_close(bund);  | 
2985  | 0  |             bund = NULL;  | 
2986  | 0  |             continue;  | 
2987  | 0  |         }  | 
2988  |  |           | 
2989  | 0  |         while((subPtr = ures_getNextResource(&item,&subItem,&subStatus)) != 0  | 
2990  | 0  |             && U_SUCCESS(subStatus)) { | 
2991  | 0  |             const char *k;  | 
2992  | 0  |             int32_t i;  | 
2993  | 0  |             k = ures_getKey(subPtr);  | 
2994  |  | 
  | 
2995  |  | #if defined(URES_TREE_DEBUG)  | 
2996  |  |             /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */  | 
2997  |  | #endif  | 
2998  | 0  |             if(k == NULL || *k == 0 ||  | 
2999  | 0  |                     uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) { | 
3000  |  |                 // empty or "default" or unlisted type  | 
3001  | 0  |                 continue;  | 
3002  | 0  |             }  | 
3003  | 0  |             for(i=0; i<valuesCount; i++) { | 
3004  | 0  |                 if(!uprv_strcmp(valuesList[i],k)) { | 
3005  | 0  |                     k = NULL; /* found duplicate */  | 
3006  | 0  |                     break;  | 
3007  | 0  |                 }  | 
3008  | 0  |             }  | 
3009  | 0  |             if(k != NULL) { | 
3010  | 0  |                 int32_t kLen = (int32_t)uprv_strlen(k);  | 
3011  | 0  |                 if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */  | 
3012  | 0  |                     ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */ | 
3013  | 0  |                     *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */  | 
3014  | 0  |                 } else { | 
3015  | 0  |                     uprv_strcpy(valuesBuf+valuesIndex, k);  | 
3016  | 0  |                     valuesList[valuesCount++] = valuesBuf+valuesIndex;  | 
3017  | 0  |                     valuesIndex += kLen;  | 
3018  |  | #if defined(URES_TREE_DEBUG)  | 
3019  |  |                     fprintf(stderr, "%s | %s | %s | [%s]   (UNIQUE)\n",  | 
3020  |  |                         path?path:"<ICUDATA>", keyword, locale, k);  | 
3021  |  | #endif  | 
3022  | 0  |                     valuesBuf[valuesIndex++] = 0; /* terminate */  | 
3023  | 0  |                 }  | 
3024  | 0  |             }  | 
3025  | 0  |         }  | 
3026  | 0  |         ures_close(bund);  | 
3027  | 0  |     }  | 
3028  | 0  |     valuesBuf[valuesIndex++] = 0; /* terminate */  | 
3029  |  |       | 
3030  | 0  |     ures_close(&item);  | 
3031  | 0  |     ures_close(&subItem);  | 
3032  | 0  |     uenum_close(locs);  | 
3033  |  | #if defined(URES_TREE_DEBUG)  | 
3034  |  |     fprintf(stderr, "%s:  size %d, #%d\n", u_errorName(*status),   | 
3035  |  |         valuesIndex, valuesCount);  | 
3036  |  | #endif  | 
3037  | 0  |     return uloc_openKeywordList(valuesBuf, valuesIndex, status);  | 
3038  | 0  | }  | 
3039  |  | #if 0  | 
3040  |  | /* This code isn't needed, and given the documentation warnings the implementation is suspect */  | 
3041  |  | U_CAPI UBool U_EXPORT2  | 
3042  |  | ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){ | 
3043  |  |     if(res1==NULL || res2==NULL){ | 
3044  |  |         return res1==res2; /* pointer comparison */  | 
3045  |  |     }  | 
3046  |  |     if(res1->fKey==NULL||  res2->fKey==NULL){ | 
3047  |  |         return (res1->fKey==res2->fKey);  | 
3048  |  |     }else{ | 
3049  |  |         if(uprv_strcmp(res1->fKey, res2->fKey)!=0){ | 
3050  |  |             return FALSE;  | 
3051  |  |         }  | 
3052  |  |     }  | 
3053  |  |     if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){ | 
3054  |  |         return FALSE;  | 
3055  |  |     }  | 
3056  |  |     if(res1->fData->fPath == NULL||  res2->fData->fPath==NULL){ | 
3057  |  |         return (res1->fData->fPath == res2->fData->fPath);  | 
3058  |  |     }else{ | 
3059  |  |         if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){ | 
3060  |  |             return FALSE;  | 
3061  |  |         }  | 
3062  |  |     }  | 
3063  |  |     if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){ | 
3064  |  |         return FALSE;  | 
3065  |  |     }  | 
3066  |  |     if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){ | 
3067  |  |         return FALSE;  | 
3068  |  |     }  | 
3069  |  |     if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){ | 
3070  |  |         return FALSE;  | 
3071  |  |     }  | 
3072  |  |     if(res1->fRes != res2->fRes){ | 
3073  |  |         return FALSE;  | 
3074  |  |     }  | 
3075  |  |     return TRUE;  | 
3076  |  | }  | 
3077  |  | U_CAPI UResourceBundle* U_EXPORT2  | 
3078  |  | ures_clone(const UResourceBundle* res, UErrorCode* status){ | 
3079  |  |     UResourceBundle* bundle = NULL;  | 
3080  |  |     UResourceBundle* ret = NULL;  | 
3081  |  |     if(U_FAILURE(*status) || res == NULL){ | 
3082  |  |         return NULL;  | 
3083  |  |     }  | 
3084  |  |     bundle = ures_open(res->fData->fPath, res->fData->fName, status);  | 
3085  |  |     if(res->fResPath!=NULL){ | 
3086  |  |         ret = ures_findSubResource(bundle, res->fResPath, NULL, status);  | 
3087  |  |         ures_close(bundle);  | 
3088  |  |     }else{ | 
3089  |  |         ret = bundle;  | 
3090  |  |     }  | 
3091  |  |     return ret;  | 
3092  |  | }  | 
3093  |  | U_CAPI const UResourceBundle* U_EXPORT2  | 
3094  |  | ures_getParentBundle(const UResourceBundle* res){ | 
3095  |  |     if(res==NULL){ | 
3096  |  |         return NULL;  | 
3097  |  |     }  | 
3098  |  |     return res->fParentRes;  | 
3099  |  | }  | 
3100  |  | #endif  | 
3101  |  |  | 
3102  |  | U_CAPI void U_EXPORT2  | 
3103  | 0  | ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) { | 
3104  | 0  |   const UChar *str;  | 
3105  | 0  |   int32_t len;  | 
3106  | 0  |   str = ures_getStringByKey(res, key, &len, status);  | 
3107  | 0  |   if(U_SUCCESS(*status)) { | 
3108  | 0  |     u_versionFromUString(ver, str);  | 
3109  | 0  |   }   | 
3110  | 0  | }  | 
3111  |  |  | 
3112  |  | /* eof */  |