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