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