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