Coverage Report

Created: 2024-04-24 06:23

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