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