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