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