tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

uresbund.cpp (133939B)


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