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