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