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