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