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