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