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