Coverage Report

Created: 2026-06-07 06:21

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