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