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