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