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