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