Coverage Report

Created: 2026-02-05 06:34

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
12.6M
static int32_t U_CALLCONV hashEntry(const UHashTok parm) {
59
12.6M
    UResourceDataEntry* b = static_cast<UResourceDataEntry*>(parm.pointer);
60
12.6M
    UHashTok namekey, pathkey;
61
12.6M
    namekey.pointer = b->fName;
62
12.6M
    pathkey.pointer = b->fPath;
63
12.6M
    return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);
64
12.6M
}
65
66
/* INTERNAL: compares two entries */
67
12.5M
static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) {
68
12.5M
    UResourceDataEntry* b1 = static_cast<UResourceDataEntry*>(p1.pointer);
69
12.5M
    UResourceDataEntry* b2 = static_cast<UResourceDataEntry*>(p2.pointer);
70
12.5M
    UHashTok name1, name2, path1, path2;
71
12.5M
    name1.pointer = b1->fName;
72
12.5M
    name2.pointer = b2->fName;
73
12.5M
    path1.pointer = b1->fPath;
74
12.5M
    path2.pointer = b2->fPath;
75
12.5M
    return uhash_compareChars(name1, name2) && uhash_compareChars(path1, path2);
76
12.5M
}
77
78
79
/**
80
 *  Internal function, gets parts of locale name according 
81
 *  to the position of '_' character
82
 */
83
6.32M
static UBool chopLocale(char *name) {
84
6.32M
    char *i = uprv_strrchr(name, '_');
85
86
6.32M
    if(i != nullptr) {
87
4.24M
        *i = '\0';
88
4.24M
        return true;
89
4.24M
    }
90
91
2.08M
    return false;
92
6.32M
}
93
94
4.36M
static UBool hasVariant(const char* localeID) {
95
4.36M
    UErrorCode err = U_ZERO_ERROR;
96
4.36M
    CheckedArrayByteSink sink(nullptr, 0);
97
4.36M
    ulocimp_getSubtags(
98
4.36M
            localeID,
99
4.36M
            nullptr,
100
4.36M
            nullptr,
101
4.36M
            nullptr,
102
4.36M
            &sink,
103
4.36M
            nullptr,
104
4.36M
            err);
105
4.36M
    return sink.NumberOfBytesAppended() != 0;
106
4.36M
}
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
7.54M
                                         int32_t lookupTableLength) {
119
7.54M
    const int32_t* bottom = lookupTable;
120
7.54M
    const int32_t* top = lookupTable + lookupTableLength;
121
122
74.7M
    while (bottom < top) {
123
        // Effectively, divide by 2 and round down to an even index
124
67.6M
        const int32_t* middle = bottom + (((top - bottom) / 4) * 2);
125
67.6M
        const char* entryKey = &(keyStrs[*middle]);
126
67.6M
        int32_t strcmpResult = uprv_strcmp(key, entryKey);
127
67.6M
        if (strcmpResult == 0) {
128
444k
            return &(valueStrs[middle[1]]);
129
67.2M
        } else if (strcmpResult < 0) {
130
39.6M
            top = middle;
131
39.6M
        } else {
132
27.5M
            bottom = middle + 2;
133
27.5M
        }
134
67.6M
    }
135
7.10M
    return nullptr;
136
7.54M
}
137
138
2.91M
static CharString getDefaultScript(const CharString& language, const CharString& region) {
139
2.91M
    const char* defaultScript = nullptr;
140
2.91M
    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
2.91M
    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
2.91M
    if (!region.isEmpty()) {
148
1.51M
        CharString localeID;
149
1.51M
        localeID.append(language, err).append("_", err).append(region, err);
150
1.51M
        if (U_FAILURE(err)) {
151
0
            return result;
152
0
        }
153
1.51M
        defaultScript = performFallbackLookup(localeID.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
154
1.51M
    }
155
    
156
    // if we didn't find anything, look up just the language in the default script table
157
2.91M
    if (defaultScript == nullptr) {
158
2.91M
        defaultScript = performFallbackLookup(language.data(), dsLocaleIDChars, scriptCodeChars, defaultScriptTable, UPRV_LENGTHOF(defaultScriptTable));
159
2.91M
    }
160
    
161
    // if either lookup above succeeded, copy the result from "defaultScript" into "result"; otherwise, return "Latn"
162
2.91M
    if (defaultScript != nullptr) {
163
440k
        result.clear();
164
440k
        result.append(defaultScript, err);
165
440k
    }
166
2.91M
    return result;
167
2.91M
}
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
5.68M
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
5.68M
    size_t nameLen = uprv_strlen(name);
214
5.68M
    if (!nameLen || name[nameLen - 1] == '_' || hasVariant(name)) {
215
2.52M
        return chopLocale(name);
216
2.52M
    }
217
    
218
3.15M
    UErrorCode err = U_ZERO_ERROR;
219
3.15M
    CharString language;
220
3.15M
    CharString script;
221
3.15M
    CharString region;
222
3.15M
    ulocimp_getSubtags(name, &language, &script, &region, nullptr, nullptr, err);
223
224
3.15M
    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
3.15M
    if (openType == URES_OPEN_LOCALE_DEFAULT_ROOT) {
235
3.11M
        const char* parentID = performFallbackLookup(name, parentLocaleChars, parentLocaleChars, parentLocaleTable, UPRV_LENGTHOF(parentLocaleTable));
236
3.11M
        if (parentID != nullptr) {
237
4.74k
            uprv_strcpy(name, parentID);
238
4.74k
            return true;
239
4.74k
        }
240
3.11M
    }
241
242
3.15M
    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
3.15M
    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
100k
        if (getDefaultScript(language, region) == script) {
251
30.4k
            workingLocale.append(language, err).append("_", err).append(region, err);
252
70.4k
        } else {
253
70.4k
            workingLocale.append(language, err).append("_", err).append(script, err);
254
70.4k
        }
255
3.05M
    } 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
1.43M
        UErrorCode err = U_ZERO_ERROR;
260
1.43M
        CharString origNameLanguage;
261
1.43M
        CharString origNameScript;
262
1.43M
        ulocimp_getSubtags(origName, &origNameLanguage, &origNameScript, nullptr, nullptr, nullptr, err);
263
1.43M
        if (!origNameScript.isEmpty()) {
264
26.4k
            workingLocale.append(language, err).append("_", err).append(origNameScript, err);
265
1.40M
        } else {
266
1.40M
            workingLocale.append(language, err).append("_", err).append(getDefaultScript(language, region), err);
267
1.40M
        }
268
1.61M
    } 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
1.42M
        if (openType != URES_OPEN_LOCALE_DEFAULT_ROOT || getDefaultScript(language, CharString()) == script) {
276
1.41M
            workingLocale.append(language, err);
277
1.41M
        } else {
278
9.41k
            return false;
279
9.41k
        }
280
1.42M
    } else {
281
        // if "name" just contains a language code, return false so the calling code falls back to "root"
282
195k
        return false;
283
195k
    }
284
2.94M
    if (U_SUCCESS(err) && !workingLocale.isEmpty()) {
285
2.93M
        uprv_strcpy(name, workingLocale.data());
286
2.93M
        return true;
287
2.93M
    } else {
288
17.0k
        return false;
289
17.0k
    }
290
2.94M
}
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
2.06M
static UBool mayHaveParent(char *name) {
298
2.06M
    return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr);
299
2.06M
}
300
301
/**
302
 *  Internal function
303
 */
304
16.1M
static void entryIncrease(UResourceDataEntry *entry) {
305
16.1M
    Mutex lock(&resbMutex);
306
16.1M
    entry->fCountExisting++;
307
26.0M
    while(entry->fParent != nullptr) {
308
9.89M
      entry = entry->fParent;
309
9.89M
      entry->fCountExisting++;
310
9.89M
    }
311
16.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
869k
        const char **resTag, Resource *res, UErrorCode *status) {
320
869k
    UResourceDataEntry *dataEntry = resBundle->fData;
321
869k
    int32_t indexR = -1;
322
869k
    int32_t i = 0;
323
869k
    *res = RES_BOGUS;
324
869k
    if(dataEntry == nullptr) {
325
0
        *status = U_MISSING_RESOURCE_ERROR;
326
0
        return nullptr;
327
0
    }
328
869k
    if(dataEntry->fBogus == U_ZERO_ERROR) { /* if this resource is real, */
329
869k
        *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag); /* try to get data from there */
330
869k
        i++;
331
869k
    }
332
869k
    if(resBundle->fHasFallback) {
333
        // Otherwise, we'll look in parents.
334
2.16M
        while(*res == RES_BOGUS && dataEntry->fParent != nullptr) {
335
1.29M
            dataEntry = dataEntry->fParent;
336
1.29M
            if(dataEntry->fBogus == U_ZERO_ERROR) {
337
1.29M
                i++;
338
1.29M
                *res = res_getTableItemByKey(&(dataEntry->fData), dataEntry->fData.rootRes, &indexR, resTag);
339
1.29M
            }
340
1.29M
        }
341
869k
    }
342
343
869k
    if(*res == RES_BOGUS) {
344
        // If the resource is not found, we need to give an error.
345
429k
        *status = U_MISSING_RESOURCE_ERROR;
346
429k
        return nullptr;
347
429k
    }
348
    // If the resource is found in parents, we need to adjust the error.
349
440k
    if(i>1) {
350
440k
        if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
351
30.8k
            *status = U_USING_DEFAULT_WARNING;
352
409k
        } else {
353
409k
            *status = U_USING_FALLBACK_WARNING;
354
409k
        }
355
440k
    }
356
440k
    return dataEntry;
357
869k
}
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
25
static void U_CALLCONV createCache(UErrorCode &status) {
478
25
    U_ASSERT(cache == nullptr);
479
25
    cache = uhash_open(hashEntry, compareEntries, nullptr, &status);
480
25
    ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
481
25
}
482
     
483
6.75M
static void initCache(UErrorCode *status) {
484
6.75M
    umtx_initOnce(gCacheInitOnce, &createCache, *status);
485
6.75M
}
486
487
/** INTERNAL: sets the name (locale) of the resource bundle to given name */
488
489
118k
static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) {
490
118k
    int32_t len = static_cast<int32_t>(uprv_strlen(name));
491
118k
    if(res->fName != nullptr && res->fName != res->fNameBuffer) {
492
0
        uprv_free(res->fName);
493
0
    }
494
118k
    if (len < static_cast<int32_t>(sizeof(res->fNameBuffer))) {
495
6.36k
        res->fName = res->fNameBuffer;
496
6.36k
    }
497
112k
    else {
498
112k
        res->fName = static_cast<char*>(uprv_malloc(len + 1));
499
112k
    }
500
118k
    if(res->fName == nullptr) {
501
0
        *status = U_MEMORY_ALLOCATION_ERROR;
502
118k
    } else {
503
118k
        uprv_strcpy(res->fName, name);
504
118k
    }
505
118k
}
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
12.4M
static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) {
515
12.4M
    UResourceDataEntry *r = nullptr;
516
12.4M
    UResourceDataEntry find;
517
    /*int32_t hashValue;*/
518
12.4M
    const char *name;
519
12.4M
    char aliasName[100] = { 0 };
520
12.4M
    int32_t aliasLen = 0;
521
    /*UBool isAlias = false;*/
522
    /*UHashTok hashkey; */
523
524
12.4M
    if(U_FAILURE(*status)) {
525
1.43k
        return nullptr;
526
1.43k
    }
527
528
    /* here we try to deduce the right locale name */
529
12.4M
    if(localeID == nullptr) { /* if localeID is nullptr, we're trying to open default locale */
530
0
        name = uloc_getDefault();
531
12.4M
    } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */
532
277k
        name = kRootLocaleName;
533
12.1M
    } else { /* otherwise, we'll open what we're given */
534
12.1M
        name = localeID;
535
12.1M
    }
536
537
12.4M
    find.fName = const_cast<char*>(name);
538
12.4M
    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
12.4M
    r = static_cast<UResourceDataEntry*>(uhash_get(cache, &find));
546
12.4M
    if(r == nullptr) {
547
        /* if the entry is not yet in the hash table, we'll try to construct a new one */
548
118k
        r = static_cast<UResourceDataEntry*>(uprv_malloc(sizeof(UResourceDataEntry)));
549
118k
        if(r == nullptr) {
550
0
            *status = U_MEMORY_ALLOCATION_ERROR;
551
0
            return nullptr;
552
0
        }
553
554
118k
        uprv_memset(r, 0, sizeof(UResourceDataEntry));
555
        /*r->fHashKey = hashValue;*/
556
557
118k
        setEntryName(r, name, status);
558
118k
        if (U_FAILURE(*status)) {
559
0
            uprv_free(r);
560
0
            return nullptr;
561
0
        }
562
563
118k
        if(path != nullptr) {
564
71.0k
            r->fPath = uprv_strdup(path);
565
71.0k
            if(r->fPath == nullptr) {
566
0
                *status = U_MEMORY_ALLOCATION_ERROR;
567
0
                uprv_free(r);
568
0
                return nullptr;
569
0
            }
570
71.0k
        }
571
572
        /* this is the actual loading */
573
118k
        res_load(&(r->fData), r->fPath, r->fName, status);
574
575
118k
        if (U_FAILURE(*status)) {
576
            /* if we failed to load due to an out-of-memory error, exit early. */
577
107k
            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
107k
            *status = U_USING_FALLBACK_WARNING;
583
107k
            r->fBogus = U_USING_FALLBACK_WARNING;
584
107k
        } else { /* if we have a regular entry */
585
10.9k
            Resource aliasres;
586
10.9k
            if (r->fData.usesPoolBundle) {
587
10.5k
                r->fPool = getPoolEntry(r->fPath, status);
588
10.5k
                if (U_SUCCESS(*status)) {
589
10.5k
                    const int32_t *poolIndexes = r->fPool->fData.pRoot + 1;
590
10.5k
                    if(r->fData.pRoot[1 + URES_INDEX_POOL_CHECKSUM] == poolIndexes[URES_INDEX_POOL_CHECKSUM]) {
591
10.5k
                        r->fData.poolBundleKeys = reinterpret_cast<const char*>(poolIndexes + (poolIndexes[URES_INDEX_LENGTH] & 0xff));
592
10.5k
                        r->fData.poolBundleStrings = r->fPool->fData.p16BitUnits;
593
10.5k
                    } else {
594
16
                        r->fBogus = *status = U_INVALID_FORMAT_ERROR;
595
16
                    }
596
10.5k
                } else {
597
0
                    r->fBogus = *status;
598
0
                }
599
10.5k
            }
600
10.9k
            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
10.9k
                aliasres = res_getResource(&(r->fData), "%%ALIAS");
604
10.9k
                if (aliasres != RES_BOGUS) {
605
                    // No tracing: called during initial data loading
606
184
                    const char16_t *alias = res_getStringNoTrace(&(r->fData), aliasres, &aliasLen);
607
184
                    if(alias != nullptr && aliasLen > 0) { /* if there is actual alias - unload and load new data */
608
184
                        u_UCharsToChars(alias, aliasName, aliasLen+1);
609
184
                        r->fAlias = init_entry(aliasName, path, status);
610
184
                    }
611
184
                }
612
10.9k
            }
613
10.9k
        }
614
615
118k
        {
616
118k
            UResourceDataEntry *oldR = nullptr;
617
118k
            if ((oldR = static_cast<UResourceDataEntry*>(uhash_get(cache, r))) == nullptr) { /* if the data is not cached */
618
                /* just insert it in the cache */
619
118k
                UErrorCode cacheStatus = U_ZERO_ERROR;
620
118k
                uhash_put(cache, (void *)r, r, &cacheStatus);
621
118k
                if (U_FAILURE(cacheStatus)) {
622
0
                    *status = cacheStatus;
623
0
                    free_entry(r);
624
0
                    r = nullptr;
625
0
                }
626
118k
            } 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
118k
        }
633
634
118k
    }
635
12.4M
    if(r != nullptr) {
636
        /* return the real bundle */
637
12.4M
        while(r->fAlias != nullptr) {
638
12.3k
            r = r->fAlias;
639
12.3k
        }
640
12.4M
        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
12.4M
        if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
644
5.68M
             *status = r->fBogus; /* set the returning status */
645
5.68M
        }
646
12.4M
    }
647
12.4M
    return r;
648
12.4M
}
649
650
static UResourceDataEntry *
651
10.5k
getPoolEntry(const char *path, UErrorCode *status) {
652
10.5k
    UResourceDataEntry *poolBundle = init_entry(kPoolBundleName, path, status);
653
10.5k
    if( U_SUCCESS(*status) &&
654
10.5k
        (poolBundle == nullptr || poolBundle->fBogus != U_ZERO_ERROR || !poolBundle->fData.isPoolBundle)
655
10.5k
    ) {
656
0
        *status = U_INVALID_FORMAT_ERROR;
657
0
    }
658
10.5k
    return poolBundle;
659
10.5k
}
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
4.02M
                  UBool *isRoot, UBool *foundParent, UBool *isDefault, UErrorCode* status) {
666
4.02M
    UResourceDataEntry *r = nullptr;
667
4.02M
    UBool hasRealData = false;
668
4.02M
    *foundParent = true; /* we're starting with a fresh name */
669
4.02M
    char origName[ULOC_FULLNAME_CAPACITY];
670
671
4.02M
    uprv_strcpy(origName, name);
672
13.5M
    while(*foundParent && !hasRealData) {
673
9.48M
        r = init_entry(name, path, status);
674
        /* Null pointer test */
675
9.48M
        if (U_FAILURE(*status)) {
676
2.87k
            return nullptr;
677
2.87k
        }
678
9.48M
        *isDefault = static_cast<UBool>(uprv_strncmp(name, defaultLocale, uprv_strlen(name)) == 0);
679
9.48M
        hasRealData = static_cast<UBool>(r->fBogus == U_ZERO_ERROR);
680
9.48M
        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
5.68M
            r->fCountExisting--;
687
            /*entryCloseInt(r);*/
688
5.68M
            r = nullptr;
689
5.68M
            *status = U_USING_FALLBACK_WARNING;
690
5.68M
        } else {
691
3.80M
            uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */
692
3.80M
        }
693
694
9.48M
        *isRoot = static_cast<UBool>(uprv_strcmp(name, kRootLocaleName) == 0);
695
696
        /*Fallback data stuff*/
697
9.48M
        if (!hasRealData) {
698
5.68M
            *foundParent = getParentLocaleID(name, origName, openType);
699
5.68M
        } 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
3.80M
            *foundParent = chopLocale(name);
703
3.80M
        }
704
9.48M
        if (*foundParent && *name == '\0') {
705
43.2k
            uprv_strcpy(name, "und");
706
43.2k
        }
707
9.48M
    }
708
4.02M
    return r;
709
4.02M
}
710
711
15.8M
static void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
712
15.8M
    if(state) {
713
5.81M
        resB->fMagic1 = 0;
714
5.81M
        resB->fMagic2 = 0;
715
10.0M
    } else {
716
10.0M
        resB->fMagic1 = MAGIC1;
717
10.0M
        resB->fMagic2 = MAGIC2;
718
10.0M
    }
719
15.8M
}
720
721
15.8M
static UBool ures_isStackObject(const UResourceBundle* resB) {
722
15.8M
  return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?false:true);
723
15.8M
}
724
725
726
5.81M
U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
727
5.81M
  uprv_memset(resB, 0, sizeof(UResourceBundle));
728
5.81M
  ures_setIsStackObject(resB, true);
729
5.81M
}
730
731
U_NAMESPACE_BEGIN
732
733
3.58M
StackUResourceBundle::StackUResourceBundle() {
734
3.58M
    ures_initStackObject(&bundle);
735
3.58M
}
736
737
3.58M
StackUResourceBundle::~StackUResourceBundle() {
738
3.58M
    ures_close(&bundle);
739
3.58M
}
740
741
U_NAMESPACE_END
742
743
static UBool  // returns U_SUCCESS(*status)
744
loadParentsExceptRoot(UResourceDataEntry *&t1,
745
                      char name[], int32_t nameCapacity,
746
1.72M
                      UBool usingUSRData, char usrDataPath[], UErrorCode *status) {
747
1.72M
    if (U_FAILURE(*status)) { return false; }
748
1.72M
    UBool checkParent = true;
749
1.73M
    while (checkParent && t1->fParent == nullptr && !t1->fData.noFallback &&
750
6.44k
            res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) {
751
6.44k
        Resource parentRes = res_getResource(&t1->fData, "%%Parent");
752
6.44k
        if (parentRes != RES_BOGUS) {  // An explicit parent was found.
753
2.19k
            int32_t parentLocaleLen = 0;
754
            // No tracing: called during initial data loading
755
2.19k
            const char16_t *parentLocaleName = res_getStringNoTrace(&(t1->fData), parentRes, &parentLocaleLen);
756
2.19k
            if(parentLocaleName != nullptr && 0 < parentLocaleLen && parentLocaleLen < nameCapacity) {
757
2.19k
                u_UCharsToChars(parentLocaleName, name, parentLocaleLen + 1);
758
2.19k
                if (uprv_strcmp(name, kRootLocaleName) == 0) {
759
318
                    return true;
760
318
                }
761
2.19k
            }
762
2.19k
        }
763
        // Insert regular parents.
764
6.12k
        UErrorCode parentStatus = U_ZERO_ERROR;
765
6.12k
        UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus);
766
6.12k
        if (U_FAILURE(parentStatus)) {
767
0
            *status = parentStatus;
768
0
            return false;
769
0
        }
770
6.12k
        UResourceDataEntry *u2 = nullptr;
771
6.12k
        UErrorCode usrStatus = U_ZERO_ERROR;
772
6.12k
        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
6.12k
        if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) {
782
0
            t1->fParent = u2;
783
0
            u2->fParent = t2;
784
6.12k
        } else {
785
6.12k
            t1->fParent = t2;
786
6.12k
            if (usingUSRData) {
787
                // The USR override data wasn't found, set it to be deleted.
788
0
                u2->fCountExisting = 0;
789
0
            }
790
6.12k
        }
791
6.12k
        t1 = t2;
792
6.12k
        checkParent = chopLocale(name) || mayHaveParent(name);
793
6.12k
    }
794
1.72M
    return true;
795
1.72M
}
796
797
static UBool  // returns U_SUCCESS(*status)
798
4.50k
insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) {
799
4.50k
    if (U_FAILURE(*status)) { return false; }
800
4.50k
    UErrorCode parentStatus = U_ZERO_ERROR;
801
4.50k
    UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);
802
4.50k
    if (U_FAILURE(parentStatus)) {
803
0
        *status = parentStatus;
804
0
        return false;
805
0
    }
806
4.50k
    t1->fParent = t2;
807
4.50k
    t1 = t2;
808
4.50k
    return true;
809
4.50k
}
810
811
static UResourceDataEntry *entryOpen(const char* path, const char* localeID,
812
3.80M
                                     UResOpenType openType, UErrorCode* status) {
813
3.80M
    U_ASSERT(openType != URES_OPEN_DIRECT);
814
3.80M
    UErrorCode intStatus = U_ZERO_ERROR;
815
3.80M
    UResourceDataEntry *r = nullptr;
816
3.80M
    UResourceDataEntry *t1 = nullptr;
817
3.80M
    UBool isDefault = false;
818
3.80M
    UBool isRoot = false;
819
3.80M
    UBool hasRealData = false;
820
3.80M
    UBool hasChopped = true;
821
3.80M
    UBool usingUSRData = U_USE_USRDATA && ( path == nullptr || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0);
822
823
3.80M
    char name[ULOC_FULLNAME_CAPACITY];
824
3.80M
    char usrDataPath[96];
825
826
3.80M
    initCache(status);
827
828
3.80M
    if(U_FAILURE(*status)) {
829
0
        return nullptr;
830
0
    }
831
832
3.80M
    uprv_strncpy(name, localeID, sizeof(name) - 1);
833
3.80M
    name[sizeof(name) - 1] = 0;
834
835
3.80M
    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
3.80M
    const char *defaultLocale = uloc_getDefault();
849
850
3.80M
    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
3.80M
    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
3.80M
    if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
857
0
        *status = intStatus;
858
0
        goto finish;
859
0
    }
860
861
3.80M
    if(r != nullptr) { /* if there is one real locale, we can look for parents. */
862
3.57M
        t1 = r;
863
3.57M
        hasRealData = true;
864
3.57M
        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
3.57M
        if ((hasChopped || mayHaveParent(name)) && !isRoot) {
883
1.53M
            if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
884
0
                goto finish;
885
0
            }
886
1.53M
        }
887
3.57M
    }
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
3.80M
    if(r==nullptr && openType == URES_OPEN_LOCALE_DEFAULT_ROOT && !isDefault && !isRoot) {
892
        /* insert default locale */
893
204k
        uprv_strcpy(name, defaultLocale);
894
204k
        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
204k
        if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
897
0
            *status = intStatus;
898
0
            goto finish;
899
0
        }
900
204k
        intStatus = U_USING_DEFAULT_WARNING;
901
204k
        if(r != nullptr) { /* the default locale exists */
902
202k
            t1 = r;
903
202k
            hasRealData = true;
904
202k
            isDefault = true;
905
            // TODO: Why not if (usingUSRData) { ... } like in the non-default-locale code path?
906
202k
            if ((hasChopped || mayHaveParent(name)) && !isRoot) {
907
191k
                if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
908
0
                    goto finish;
909
0
                }
910
191k
            }
911
202k
        }
912
204k
    }
913
914
    /* we could still have r == nullptr at this point - maybe even default locale is not */
915
    /* present */
916
3.80M
    if(r == nullptr) {
917
20.8k
        uprv_strcpy(name, kRootLocaleName);
918
20.8k
        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
20.8k
        if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
921
0
            *status = intStatus;
922
0
            goto finish;
923
0
        }
924
20.8k
        if(r != nullptr) {
925
20.8k
            t1 = r;
926
20.8k
            intStatus = U_USING_DEFAULT_WARNING;
927
20.8k
            hasRealData = true;
928
20.8k
        } else { /* we don't even have the root locale */
929
0
            *status = U_MISSING_RESOURCE_ERROR;
930
0
            goto finish;
931
0
        }
932
3.78M
    } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
933
3.50M
            t1->fParent == nullptr && !r->fData.noFallback) {
934
4.49k
        if (!insertRootBundle(t1, status)) {
935
0
            goto finish;
936
0
        }
937
4.49k
        if(!hasRealData) {
938
0
            r->fBogus = U_USING_DEFAULT_WARNING;
939
0
        }
940
4.49k
    }
941
942
    // TODO: Does this ever loop?
943
10.0M
    while(r != nullptr && !isRoot && t1->fParent != nullptr) {
944
6.24M
        t1->fParent->fCountExisting++;
945
6.24M
        t1 = t1->fParent;
946
6.24M
    }
947
948
3.80M
finish:
949
3.80M
    if(U_SUCCESS(*status)) {
950
3.80M
        if(intStatus != U_ZERO_ERROR) {
951
1.68M
            *status = intStatus;  
952
1.68M
        }
953
3.80M
        return r;
954
3.80M
    } else {
955
0
        return nullptr;
956
0
    }
957
3.80M
}
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.95M
entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) {
967
2.95M
    initCache(status);
968
2.95M
    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.95M
    if (localeID == nullptr) {
975
0
        localeID = uloc_getDefault();
976
2.95M
    } 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.95M
    Mutex lock(&resbMutex);
982
983
    // findFirstExisting() without fallbacks.
984
2.95M
    UResourceDataEntry *r = init_entry(localeID, path, status);
985
2.95M
    if(U_SUCCESS(*status)) {
986
2.95M
        if(r->fBogus != U_ZERO_ERROR) {
987
6
            r->fCountExisting--;
988
6
            r = nullptr;
989
6
        }
990
2.95M
    } 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.95M
    UResourceDataEntry *t1 = r;
997
2.95M
    if(r != nullptr && uprv_strcmp(localeID, kRootLocaleName) != 0 &&  // not root
998
2.95M
            r->fParent == nullptr && !r->fData.noFallback &&
999
2.95M
            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.95M
    if(r != nullptr) {
1014
        // TODO: Does this ever loop?
1015
2.95M
        while(t1->fParent != nullptr) {
1016
466
            t1->fParent->fCountExisting++;
1017
466
            t1 = t1->fParent;
1018
466
        }
1019
2.95M
    }
1020
2.95M
    return r;
1021
2.95M
}
1022
1023
/**
1024
 * Functions to create and destroy resource bundles.
1025
 *     CAUTION:  resbMutex must be locked when calling this function.
1026
 */
1027
/* INTERNAL: */
1028
22.9M
static void entryCloseInt(UResourceDataEntry *resB) {
1029
22.9M
    UResourceDataEntry *p = resB;
1030
1031
61.9M
    while(resB != nullptr) {
1032
39.0M
        p = resB->fParent;
1033
39.0M
        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
39.0M
        resB = p;
1054
39.0M
    }
1055
22.9M
}
1056
1057
/** 
1058
 *  API: closes a resource bundle and cleans up.
1059
 */
1060
1061
22.9M
static void entryClose(UResourceDataEntry *resB) {
1062
22.9M
  Mutex lock(&resbMutex);
1063
22.9M
  entryCloseInt(resB);
1064
22.9M
}
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
41.7M
static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) {
1084
41.7M
    int32_t resPathLenOrig = resB->fResPathLen;
1085
41.7M
    if(resB->fResPath == nullptr) {
1086
17.3M
        resB->fResPath = resB->fResBuf;
1087
17.3M
        *(resB->fResPath) = 0;
1088
17.3M
        resB->fResPathLen = 0;
1089
17.3M
    } 
1090
41.7M
    resB->fResPathLen += lenToAdd;
1091
41.7M
    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
41.7M
    uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd);
1111
41.7M
}
1112
1113
23.0M
static void ures_freeResPath(UResourceBundle *resB) {
1114
23.0M
    if (resB->fResPath && resB->fResPath != resB->fResBuf) {
1115
0
        uprv_free(resB->fResPath);
1116
0
    }
1117
23.0M
    resB->fResPath = nullptr;
1118
23.0M
    resB->fResPathLen = 0;
1119
23.0M
}
1120
1121
static void
1122
ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj)
1123
22.1M
{
1124
22.1M
    if(resB != nullptr) {
1125
15.8M
        if(resB->fData != nullptr) {
1126
14.7M
            entryClose(resB->fData);
1127
14.7M
        }
1128
15.8M
        if(resB->fVersion != nullptr) {
1129
0
            uprv_free(resB->fVersion);
1130
0
        }
1131
15.8M
        ures_freeResPath(resB);
1132
1133
15.8M
        if(ures_isStackObject(resB) == false && freeBundleObj) {
1134
10.0M
            uprv_free(resB);
1135
10.0M
        }
1136
#if 0 /*U_DEBUG*/
1137
        else {
1138
            /* poison the data */
1139
            uprv_memset(resB, -1, sizeof(UResourceBundle));
1140
        }
1141
#endif
1142
15.8M
    }
1143
22.1M
}
1144
1145
U_CAPI void  U_EXPORT2
1146
ures_close(UResourceBundle* resB)
1147
22.1M
{
1148
22.1M
    ures_closeBundle(resB, true);
1149
22.1M
}
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
1.98M
        UResourceBundle *resB, UErrorCode *status) {
1167
    // TODO: When an error occurs: Should we return nullptr vs. resB?
1168
1.98M
    if (U_FAILURE(*status)) { return resB; }
1169
1.98M
    U_ASSERT(RES_GET_TYPE(r) == URES_ALIAS);
1170
1.98M
    int32_t len = 0;
1171
1.98M
    const char16_t *alias = res_getAlias(&resData, r, &len);
1172
1.98M
    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
1.98M
    CharString chAlias;
1189
1.98M
    chAlias.appendInvariantChars(alias, len, *status);
1190
1.98M
    if (U_FAILURE(*status)) {
1191
0
        return nullptr;
1192
0
    }
1193
1194
    // We have an alias, now let's cut it up.
1195
1.98M
    const char *path = nullptr, *locale = nullptr, *keyPath = nullptr;
1196
1.98M
    if(chAlias[0] == RES_PATH_SEPARATOR) {
1197
        // There is a path included.
1198
1.98M
        char *chAliasData = chAlias.data();
1199
1.98M
        char *sep = chAliasData + 1;
1200
1.98M
        path = sep;
1201
1.98M
        sep = uprv_strchr(sep, RES_PATH_SEPARATOR);
1202
1.98M
        if(sep != nullptr) {
1203
1.98M
            *sep++ = 0;
1204
1.98M
        }
1205
1.98M
        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
1.97M
            keyPath = sep;
1213
            // Read from the valid locale which we already have.
1214
1.97M
            path = locale = nullptr;
1215
1.97M
        } else {
1216
10.1k
            if(uprv_strcmp(path, "ICUDATA") == 0) { /* want ICU data */
1217
10.1k
                path = nullptr;
1218
10.1k
            }
1219
10.1k
            if (sep == nullptr) {
1220
                // TODO: This ends up using the root bundle. Can/should we forbid this?
1221
0
                locale = "";
1222
10.1k
            } else {
1223
10.1k
                locale = sep;
1224
10.1k
                sep = uprv_strchr(sep, RES_PATH_SEPARATOR);
1225
10.1k
                if(sep != nullptr) {
1226
10.1k
                    *sep++ = 0;
1227
10.1k
                }
1228
10.1k
                keyPath = sep;
1229
10.1k
            }
1230
10.1k
        }
1231
1.98M
    } 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
1.98M
    LocalUResourceBundlePointer mainRes;
1246
1.98M
    UResourceDataEntry *dataEntry;
1247
1.98M
    if (locale == nullptr) {
1248
        // alias = /LOCALE/keyPath
1249
        // Read from the valid locale which we already have.
1250
1.97M
        dataEntry = validLocaleDataEntry;
1251
1.97M
    } else {
1252
10.1k
        UErrorCode intStatus = U_ZERO_ERROR;
1253
        // TODO: Shouldn't we use ures_open() for locale data bundles (!noFallback)?
1254
10.1k
        mainRes.adoptInstead(ures_openDirect(path, locale, &intStatus));
1255
10.1k
        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.1k
        dataEntry = mainRes->fData;
1261
10.1k
    }
1262
1263
1.98M
    const char* temp = nullptr;
1264
1.98M
    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
1.98M
    } 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
1.98M
        CharString pathBuf(keyPath, *status);
1317
1.98M
        if (U_FAILURE(*status)) {
1318
0
            return nullptr;
1319
0
        }
1320
1.98M
        char *myPath = pathBuf.data();
1321
1.98M
        containerResPath = nullptr;
1322
        // Now we have fallback following here.
1323
4.78M
        for(;;) {
1324
4.78M
            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
6.76M
            while(*myPath && U_SUCCESS(*status)) {
1330
4.78M
                r = res_findResource(&(dataEntry->fData), r, &myPath, &temp);
1331
4.78M
                if(r == RES_BOGUS) {
1332
                    // No resource found, we don't really want to look anymore on this level.
1333
2.80M
                    break;
1334
2.80M
                }
1335
                // Found a resource, but it might be an indirection.
1336
1.98M
                resB = init_resb_result(
1337
1.98M
                    dataEntry, r, temp, -1,
1338
1.98M
                    validLocaleDataEntry, containerResPath, recursionDepth+1,
1339
1.98M
                    resB, status);
1340
1.98M
                if (U_FAILURE(*status)) {
1341
0
                    break;
1342
0
                }
1343
1.98M
                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
1.98M
                    ures_freeResPath(resB);
1350
1.98M
                    ures_appendResPath(resB, keyPath, static_cast<int32_t>(uprv_strlen(keyPath)), status);
1351
1.98M
                    if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1352
1.98M
                        ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1353
1.98M
                    }
1354
1.98M
                    if (U_FAILURE(*status)) {
1355
0
                        break;
1356
0
                    }
1357
1.98M
                }
1358
1.98M
                r = resB->fRes; /* switch to a new resource, possibly a new tree */
1359
1.98M
                dataEntry = resB->fData;
1360
1.98M
                containerResPath = resB->fResPath;
1361
1.98M
            }
1362
4.78M
            if (U_FAILURE(*status) || r != RES_BOGUS) {
1363
1.98M
                break;
1364
1.98M
            }
1365
            // Fall back to the parent bundle, if there is one.
1366
2.80M
            dataEntry = dataEntry->fParent;
1367
2.80M
            if (dataEntry == nullptr) {
1368
0
                *status = U_MISSING_RESOURCE_ERROR;
1369
0
                break;
1370
0
            }
1371
            // Copy the same keyPath again.
1372
2.80M
            myPath = pathBuf.data();
1373
2.80M
            uprv_strcpy(myPath, keyPath);
1374
2.80M
        }
1375
1.98M
    }
1376
1.98M
    if(mainRes.getAlias() == resB) {
1377
0
        mainRes.orphan();
1378
0
    }
1379
1.98M
    ResourceTracer(resB).maybeTrace("getalias");
1380
1.98M
    return resB;
1381
1.98M
}
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
15.7M
        UResourceBundle *resB, UErrorCode *status) {
1390
    // TODO: When an error occurs: Should we return nullptr vs. resB?
1391
15.7M
    if(status == nullptr || U_FAILURE(*status)) {
1392
0
        return resB;
1393
0
    }
1394
15.7M
    if (validLocaleDataEntry == nullptr) {
1395
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1396
0
        return nullptr;
1397
0
    }
1398
15.7M
    if(RES_GET_TYPE(r) == URES_ALIAS) {
1399
        // This is an alias, need to exchange with real data.
1400
218k
        if(recursionDepth >= URES_MAX_ALIAS_LEVEL) {
1401
0
            *status = U_TOO_MANY_ALIASES_ERROR;
1402
0
            return resB;
1403
0
        }
1404
218k
        return getAliasTargetAsResourceBundle(
1405
218k
            dataEntry->fData, r, key, idx,
1406
218k
            validLocaleDataEntry, containerResPath, recursionDepth, resB, status);
1407
218k
    }
1408
15.4M
    if(resB == nullptr) {
1409
3.31M
        resB = static_cast<UResourceBundle*>(uprv_malloc(sizeof(UResourceBundle)));
1410
3.31M
        if (resB == nullptr) {
1411
0
            *status = U_MEMORY_ALLOCATION_ERROR;
1412
0
            return nullptr;
1413
0
        }
1414
3.31M
        ures_setIsStackObject(resB, false);
1415
3.31M
        resB->fResPath = nullptr;
1416
3.31M
        resB->fResPathLen = 0;
1417
12.1M
    } else {
1418
12.1M
        if(resB->fData != nullptr) {
1419
8.14M
            entryClose(resB->fData);
1420
8.14M
        }
1421
12.1M
        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
12.1M
        if(containerResPath != resB->fResPath) {
1436
3.61M
            ures_freeResPath(resB);
1437
3.61M
        }
1438
12.1M
    }
1439
15.4M
    resB->fData = dataEntry;
1440
15.4M
    entryIncrease(resB->fData);
1441
15.4M
    resB->fHasFallback = false;
1442
15.4M
    resB->fIsTopLevel = false;
1443
15.4M
    resB->fIndex = -1;
1444
15.4M
    resB->fKey = key; 
1445
15.4M
    resB->fValidLocaleDataEntry = validLocaleDataEntry;
1446
15.4M
    if(containerResPath != resB->fResPath) {
1447
4.29M
        ures_appendResPath(
1448
4.29M
            resB, containerResPath, static_cast<int32_t>(uprv_strlen(containerResPath)), status);
1449
4.29M
    }
1450
15.4M
    if(key != nullptr) {
1451
14.8M
        ures_appendResPath(resB, key, static_cast<int32_t>(uprv_strlen(key)), status);
1452
14.8M
        if(resB->fResPathLen > 0 && resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1453
14.3M
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1454
14.3M
        }
1455
14.8M
    } else if(idx >= 0) {
1456
663k
        char buf[256];
1457
663k
        int32_t len = T_CString_integerToString(buf, idx, 10);
1458
663k
        ures_appendResPath(resB, buf, len, status);
1459
663k
        if(resB->fResPathLen > 0 && resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1460
663k
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1461
663k
        }
1462
663k
    }
1463
    /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1464
15.4M
    {
1465
15.4M
        int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
1466
15.4M
        uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
1467
15.4M
    }
1468
1469
15.4M
    resB->fVersion = nullptr;
1470
15.4M
    resB->fRes = r;
1471
15.4M
    resB->fSize = res_countArrayItems(&resB->getResData(), resB->fRes);
1472
15.4M
    ResourceTracer(resB).trace("get");
1473
15.4M
    return resB;
1474
15.4M
}
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
13.7M
        UResourceBundle *resB, UErrorCode *status) {
1481
13.7M
    return init_resb_result(
1482
13.7M
        dataEntry, r, key, idx,
1483
13.7M
        container->fValidLocaleDataEntry, container->fResPath, 0, resB, status);
1484
13.7M
}
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
3.52M
U_CAPI const char16_t* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) {
1525
3.52M
    const char16_t *s;
1526
3.52M
    if (status==nullptr || U_FAILURE(*status)) {
1527
950k
        return nullptr;
1528
950k
    }
1529
2.57M
    if(resB == nullptr) {
1530
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1531
0
        return nullptr;
1532
0
    }
1533
2.57M
    s = res_getString({resB}, &resB->getResData(), resB->fRes, len);
1534
2.57M
    if (s == nullptr) {
1535
2.25k
        *status = U_RESOURCE_TYPE_MISMATCH;
1536
2.25k
    }
1537
2.57M
    return s;
1538
2.57M
}
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.49k
                                               UErrorCode*               status) {
1614
4.49k
  const uint8_t *p;
1615
4.49k
  if (status==nullptr || U_FAILURE(*status)) {
1616
4
    return nullptr;
1617
4
  }
1618
4.49k
  if(resB == nullptr) {
1619
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1620
0
    return nullptr;
1621
0
  }
1622
4.49k
  p = res_getBinary({resB}, &resB->getResData(), resB->fRes, len);
1623
4.49k
  if (p == nullptr) {
1624
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1625
0
  }
1626
4.49k
  return p;
1627
4.49k
}
1628
1629
U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len, 
1630
166k
                                                   UErrorCode*               status) {
1631
166k
  const int32_t *p;
1632
166k
  if (status==nullptr || U_FAILURE(*status)) {
1633
7.61k
    return nullptr;
1634
7.61k
  }
1635
158k
  if(resB == nullptr) {
1636
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1637
0
    return nullptr;
1638
0
  }
1639
158k
  p = res_getIntVector({resB}, &resB->getResData(), resB->fRes, len);
1640
158k
  if (p == nullptr) {
1641
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1642
0
  }
1643
158k
  return p;
1644
158k
}
1645
1646
/* this function returns a signed integer */ 
1647
/* it performs sign extension */
1648
758k
U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) {
1649
758k
  if (status==nullptr || U_FAILURE(*status)) {
1650
7.15k
    return 0xffffffff;
1651
7.15k
  }
1652
751k
  if(resB == nullptr) {
1653
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1654
0
    return 0xffffffff;
1655
0
  }
1656
751k
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1657
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1658
0
    return 0xffffffff;
1659
0
  }
1660
751k
  return res_getInt({resB}, resB->fRes);
1661
751k
}
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
330k
U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
1679
330k
  if(resB == nullptr) {
1680
71
    return URES_NONE;
1681
71
  }
1682
330k
  return res_getPublicType(resB->fRes);
1683
330k
}
1684
1685
110k
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
110k
  if(resB == nullptr) {
1696
0
    return nullptr;
1697
0
  }
1698
110k
  return(resB->fKey);
1699
110k
}
1700
1701
1.07M
U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
1702
1.07M
  if(resB == nullptr) {
1703
0
    return 0;
1704
0
  }
1705
  
1706
1.07M
  return resB->fSize;
1707
1.07M
}
1708
1709
4.71M
static const char16_t* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) {
1710
4.71M
  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.71M
  } else {
1717
4.71M
    return res_getString({resB, sIndex}, &resB->getResData(), r, len); 
1718
4.71M
  }
1719
4.71M
}
1720
1721
12.8k
U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){
1722
12.8k
  if(resB == nullptr) {
1723
0
    return;
1724
0
  }
1725
12.8k
  resB->fIndex = -1;
1726
12.8k
}
1727
1728
1.51M
U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
1729
1.51M
  if(resB == nullptr) {
1730
0
    return false;
1731
0
  }
1732
1.51M
  return resB->fIndex < resB->fSize-1;
1733
1.51M
}
1734
1735
296k
U_CAPI const char16_t* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) {
1736
296k
  Resource r = RES_BOGUS;
1737
  
1738
296k
  if (status==nullptr || U_FAILURE(*status)) {
1739
0
    return nullptr;
1740
0
  }
1741
296k
  if(resB == nullptr) {
1742
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1743
0
    return nullptr;
1744
0
  }
1745
  
1746
296k
  if(resB->fIndex == resB->fSize-1) {
1747
0
    *status = U_INDEX_OUTOFBOUNDS_ERROR;
1748
296k
  } else {
1749
296k
    resB->fIndex++;
1750
296k
    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
98.3k
    case URES_TABLE16:
1756
98.3k
    case URES_TABLE32:
1757
98.3k
      r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, key);
1758
98.3k
      if(r == RES_BOGUS && resB->fHasFallback) {
1759
        /* TODO: do the fallback */
1760
0
      }
1761
98.3k
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1762
0
    case URES_ARRAY:
1763
198k
    case URES_ARRAY16:
1764
198k
      r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex);
1765
198k
      if(r == RES_BOGUS && resB->fHasFallback) {
1766
        /* TODO: do the fallback */
1767
0
      }
1768
198k
      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
296k
    }
1779
296k
  }
1780
1781
0
  return nullptr;
1782
296k
}
1783
1784
1.27M
U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
1785
1.27M
    const char *key = nullptr;
1786
1.27M
    Resource r = RES_BOGUS;
1787
1788
1.27M
    if (status==nullptr || U_FAILURE(*status)) {
1789
            /*return nullptr;*/
1790
0
            return fillIn;
1791
0
    }
1792
1.27M
    if(resB == nullptr) {
1793
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
1794
            /*return nullptr;*/
1795
0
            return fillIn;
1796
0
    }
1797
1798
1.27M
    if(resB->fIndex == resB->fSize-1) {
1799
5.62k
      *status = U_INDEX_OUTOFBOUNDS_ERROR;
1800
      /*return nullptr;*/
1801
1.26M
    } else {
1802
1.26M
        resB->fIndex++;
1803
1.26M
        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
104k
        case URES_TABLE:
1811
1.26M
        case URES_TABLE16:
1812
1.26M
        case URES_TABLE32:
1813
1.26M
            r = res_getTableItemByIndex(&resB->getResData(), resB->fRes, resB->fIndex, &key);
1814
1.26M
            if(r == RES_BOGUS && resB->fHasFallback) {
1815
                /* TODO: do the fallback */
1816
0
            }
1817
1.26M
            return init_resb_result(resB->fData, r, key, resB->fIndex, resB, fillIn, status);
1818
1.64k
        case URES_ARRAY:
1819
7.83k
        case URES_ARRAY16:
1820
7.83k
            r = res_getArrayItem(&resB->getResData(), resB->fRes, resB->fIndex);
1821
7.83k
            if(r == RES_BOGUS && resB->fHasFallback) {
1822
                /* TODO: do the fallback */
1823
0
            }
1824
7.83k
            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.26M
        }
1829
1.26M
    }
1830
    /*return nullptr;*/
1831
5.62k
    return fillIn;
1832
1.27M
}
1833
1834
675k
U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
1835
675k
    const char* key = nullptr;
1836
675k
    Resource r = RES_BOGUS;
1837
1838
675k
    if (status==nullptr || U_FAILURE(*status)) {
1839
        /*return nullptr;*/
1840
3.81k
        return fillIn;
1841
3.81k
    }
1842
671k
    if(resB == nullptr) {
1843
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1844
        /*return nullptr;*/
1845
0
        return fillIn;
1846
0
    }
1847
1848
671k
    if(indexR >= 0 && resB->fSize > indexR) {
1849
655k
        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
455k
        case URES_ARRAY:
1865
655k
        case URES_ARRAY16:
1866
655k
            r = res_getArrayItem(&resB->getResData(), resB->fRes, indexR);
1867
655k
            if(r == RES_BOGUS && resB->fHasFallback) {
1868
                /* TODO: do the fallback */
1869
0
            }
1870
655k
            return init_resb_result(resB->fData, r, key, indexR, resB, fillIn, status);
1871
0
        default:
1872
            /*return nullptr;*/
1873
0
            return fillIn;
1874
655k
        }
1875
655k
    } else {
1876
16.3k
        *status = U_MISSING_RESOURCE_ERROR;
1877
16.3k
    }
1878
    /*return nullptr;*/
1879
16.3k
    return fillIn;
1880
671k
}
1881
1882
4.43M
U_CAPI const char16_t* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
1883
4.43M
    const char* key = nullptr;
1884
4.43M
    Resource r = RES_BOGUS;
1885
1886
4.43M
    if (status==nullptr || U_FAILURE(*status)) {
1887
14.4k
        return nullptr;
1888
14.4k
    }
1889
4.42M
    if(resB == nullptr) {
1890
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1891
0
        return nullptr;
1892
0
    }
1893
1894
4.42M
    if(indexS >= 0 && resB->fSize > indexS) {
1895
4.41M
        switch(RES_GET_TYPE(resB->fRes)) {
1896
0
        case URES_STRING:
1897
6
        case URES_STRING_V2:
1898
6
            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
79.6k
        case URES_ARRAY:
1908
4.41M
        case URES_ARRAY16:
1909
4.41M
            r = res_getArrayItem(&resB->getResData(), resB->fRes, indexS);
1910
4.41M
            if(r == RES_BOGUS && resB->fHasFallback) {
1911
                /* TODO: do the fallback */
1912
0
            }
1913
4.41M
            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.41M
        }
1926
4.41M
    } else {
1927
8.36k
        *status = U_MISSING_RESOURCE_ERROR;
1928
8.36k
    }
1929
8.36k
    return nullptr;
1930
4.42M
}
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
2.07M
                                UErrorCode *status) {
2034
2035
2.07M
    UResourceBundle stack;
2036
2.07M
    const char16_t* retVal = nullptr;
2037
2.07M
    ures_initStackObject(&stack);
2038
2.07M
    ures_getByKeyWithFallback(resB, inKey, &stack, status);
2039
2.07M
    int32_t length;
2040
2.07M
    retVal = ures_getString(&stack, &length, status);
2041
2.07M
    ures_close(&stack);
2042
2.07M
    if (U_FAILURE(*status)) {
2043
952k
        return nullptr;
2044
952k
    }
2045
1.12M
    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
1.12M
    if (len != nullptr) {
2051
959k
        *len = length;
2052
959k
    }
2053
1.12M
    return retVal;
2054
2.07M
}
2055
2056
/*
2057
  Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
2058
*/  
2059
6.20M
static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
2060
6.20M
  Resource resource = table;  /* The current resource */
2061
6.20M
  icu::CharString path;
2062
6.20M
  UErrorCode errorCode = U_ZERO_ERROR;
2063
6.20M
  path.append(key, errorCode);
2064
6.20M
  if (U_FAILURE(errorCode)) { return RES_BOGUS; }
2065
6.20M
  char *pathPart = path.data();  /* Path from current resource to desired resource */
2066
6.20M
  UResType type = static_cast<UResType>(RES_GET_TYPE(resource)); /* the current resource type */
2067
14.3M
  while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
2068
8.19M
    char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
2069
8.19M
    if (nextPathPart != nullptr) {
2070
2.94M
      *nextPathPart = 0;  /* Terminating null for this part of path. */
2071
2.94M
      nextPathPart++;
2072
5.25M
    } else {
2073
5.25M
      nextPathPart = uprv_strchr(pathPart, 0);
2074
5.25M
    }
2075
8.19M
    int32_t t;
2076
8.19M
    const char *pathP = pathPart;
2077
8.19M
    resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
2078
8.19M
    type = static_cast<UResType>(RES_GET_TYPE(resource));
2079
8.19M
    pathPart = nextPathPart; 
2080
8.19M
  }
2081
6.20M
  if (*pathPart) {
2082
509k
    return RES_BOGUS;
2083
509k
  }
2084
5.69M
  return resource;
2085
6.20M
}
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
6.06M
                       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
6.06M
    path.clear();
2107
6.06M
    const char* key = inKey;
2108
6.06M
    if (resPathLen > 0) {
2109
3.16M
        path.append(resPath, resPathLen, *status);
2110
3.16M
        if (U_SUCCESS(*status)) {
2111
3.16M
            const char* resPathLimit = resPath + resPathLen;
2112
3.16M
            const char* origResPathLimit = origResPath + origResPathLen;
2113
3.16M
            const char* resPathPtr = resPath;
2114
3.16M
            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
6.96M
            while (origResPathPtr < origResPathLimit && resPathPtr < resPathLimit) {
2119
48.8M
                while (origResPathPtr < origResPathLimit && *origResPathPtr != RES_PATH_SEPARATOR) {
2120
45.0M
                    ++origResPathPtr;
2121
45.0M
                }
2122
3.80M
                if (origResPathPtr < origResPathLimit && *origResPathPtr == RES_PATH_SEPARATOR) {
2123
3.80M
                    ++origResPathPtr;
2124
3.80M
                }
2125
48.8M
                while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
2126
45.0M
                    ++resPathPtr;
2127
45.0M
                }
2128
3.80M
                if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
2129
3.80M
                    ++resPathPtr;
2130
3.80M
                }
2131
3.80M
            }
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
3.23M
            while (resPathPtr < resPathLimit && *key != '\0') {
2136
737k
                while (resPathPtr < resPathLimit && *resPathPtr != RES_PATH_SEPARATOR) {
2137
667k
                    ++resPathPtr;
2138
667k
                }
2139
69.9k
                if (resPathPtr < resPathLimit && *resPathPtr == RES_PATH_SEPARATOR) {
2140
69.9k
                    ++resPathPtr;
2141
69.9k
                }
2142
725k
                while (*key != '\0' && *key != RES_PATH_SEPARATOR) {
2143
655k
                    ++key;
2144
655k
                }
2145
69.9k
                if (*key == RES_PATH_SEPARATOR) {
2146
51.4k
                    ++key;
2147
51.4k
                }
2148
69.9k
            }
2149
3.16M
        }
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
3.16M
        path.append(key, *status);
2155
3.16M
    } else {
2156
2.89M
        path.append(inKey, *status);
2157
2.89M
    }
2158
6.06M
}
2159
2160
U_CAPI UResourceBundle* U_EXPORT2
2161
ures_getByKeyWithFallback(const UResourceBundle *resB,
2162
                          const char* inKey,
2163
                          UResourceBundle *fillIn,
2164
6.54M
                          UErrorCode *status) {
2165
6.54M
    Resource res = RES_BOGUS, rootRes = RES_BOGUS;
2166
6.54M
    UResourceBundle *helper = nullptr;
2167
2168
6.54M
    if (status==nullptr || U_FAILURE(*status)) {
2169
346k
        return fillIn;
2170
346k
    }
2171
6.20M
    if(resB == nullptr) {
2172
88
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2173
88
        return fillIn;
2174
88
    }
2175
2176
6.20M
    int32_t type = RES_GET_TYPE(resB->fRes);
2177
6.20M
    if(URES_IS_TABLE(type)) {
2178
6.20M
        const char* origResPath = resB->fResPath;
2179
6.20M
        int32_t origResPathLen = resB->fResPathLen;
2180
6.20M
        res = getTableItemByKeyPath(&resB->getResData(), resB->fRes, inKey);
2181
6.20M
        const char* key = inKey;
2182
6.20M
        bool didRootOnce = false;
2183
6.20M
        if(res == RES_BOGUS) {
2184
2.61M
            UResourceDataEntry *dataEntry = resB->fData;
2185
2.61M
            CharString path;
2186
2.61M
            char *myPath = nullptr;
2187
2.61M
            const char* resPath = resB->fResPath;
2188
2.61M
            int32_t len = resB->fResPathLen;
2189
7.07M
            while(res == RES_BOGUS && (dataEntry->fParent != nullptr || !didRootOnce)) { /* Otherwise, we'll look in parents */
2190
4.46M
                if (dataEntry->fParent != nullptr) {
2191
3.44M
                    dataEntry = dataEntry->fParent;
2192
3.44M
                } 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
1.01M
                    didRootOnce = true;
2199
1.01M
                }
2200
4.46M
                rootRes = dataEntry->fData.rootRes;
2201
2202
4.46M
                if(dataEntry->fBogus == U_ZERO_ERROR) {
2203
4.46M
                    createPath(origResPath, origResPathLen, resPath, len, inKey, path, status);
2204
4.46M
                    if (U_FAILURE(*status)) {
2205
0
                        ures_close(helper);
2206
0
                        return fillIn;
2207
0
                    }
2208
4.46M
                    myPath = path.data();
2209
4.46M
                    key = inKey;
2210
4.46M
                    do {
2211
4.46M
                        res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
2212
4.46M
                        if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
2213
                            /* We hit an alias, but we didn't finish following the path. */
2214
2.49k
                            helper = init_resb_result(dataEntry, res, nullptr, -1, resB, helper, status);
2215
                            /*helper = init_resb_result(dataEntry, res, inKey, -1, resB, helper, status);*/
2216
2.49k
                            if(helper) {
2217
2.49k
                              dataEntry = helper->fData;
2218
2.49k
                              rootRes = helper->fRes;
2219
2.49k
                              resPath = helper->fResPath;
2220
2.49k
                              len = helper->fResPathLen;
2221
2222
2.49k
                            } else {
2223
0
                              break;
2224
0
                            }
2225
4.46M
                        } else if (res == RES_BOGUS) {
2226
2.85M
                            break;
2227
2.85M
                        }
2228
4.46M
                    } while(*myPath); /* Continue until the whole path is consumed */
2229
4.46M
                }
2230
4.46M
            }
2231
            /*dataEntry = getFallbackData(resB, &key, &res, status);*/
2232
2.61M
            if(res != RES_BOGUS) {
2233
              /* check if resB->fResPath gives the right name here */
2234
1.60M
                if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
2235
988k
                    *status = U_USING_DEFAULT_WARNING;
2236
988k
                } else {
2237
612k
                    *status = U_USING_FALLBACK_WARNING;
2238
612k
                }
2239
2240
1.60M
                fillIn = init_resb_result(dataEntry, res, key, -1, resB, fillIn, status);
2241
1.60M
                if (resPath != nullptr) {
2242
499k
                    createPath(origResPath, origResPathLen, resPath, len, inKey, path, status);
2243
1.10M
                } else {
2244
1.10M
                    const char* separator = nullptr;
2245
1.10M
                    if (fillIn->fResPath != nullptr) {
2246
1.10M
                        separator = uprv_strchr(fillIn->fResPath, RES_PATH_SEPARATOR);
2247
1.10M
                    }
2248
1.10M
                    if (separator != nullptr && separator[1] != '\0') {
2249
18.5k
                        createPath(origResPath, origResPathLen, fillIn->fResPath,
2250
18.5k
                                   static_cast<int32_t>(uprv_strlen(fillIn->fResPath)), inKey, path, status);
2251
1.08M
                    } else {
2252
1.08M
                        createPath(origResPath, origResPathLen, "", 0, inKey, path, status);
2253
1.08M
                    }
2254
1.10M
                }
2255
1.60M
                ures_freeResPath(fillIn);
2256
1.60M
                ures_appendResPath(fillIn, path.data(), path.length(), status);
2257
1.60M
                if(fillIn->fResPath[fillIn->fResPathLen-1] != RES_PATH_SEPARATOR) {
2258
1.33M
                    ures_appendResPath(fillIn, RES_PATH_SEPARATOR_S, 1, status);
2259
1.33M
                }
2260
1.60M
            } else {
2261
1.01M
                *status = U_MISSING_RESOURCE_ERROR;
2262
1.01M
            }
2263
3.58M
        } else {
2264
3.58M
            fillIn = init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
2265
3.58M
        }
2266
6.20M
    } 
2267
0
    else {
2268
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2269
0
    }
2270
6.20M
    ures_close(helper);
2271
6.20M
    return fillIn;
2272
6.20M
}
2273
2274
namespace {
2275
2276
void getAllItemsWithFallback(
2277
        const UResourceBundle *bundle, ResourceDataValue &value,
2278
1.51M
        ResourceSink &sink, UErrorCode &errorCode) {
2279
1.51M
    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
1.51M
    value.setData(bundle->getResData());
2291
1.51M
    value.setValidLocaleDataEntry(bundle->fValidLocaleDataEntry);
2292
1.51M
    UResourceDataEntry *parentEntry = bundle->fData->fParent;
2293
1.51M
    UBool hasParent = parentEntry != nullptr && U_SUCCESS(parentEntry->fBogus);
2294
1.51M
    value.setResource(bundle->fRes, ResourceTracer(bundle));
2295
1.51M
    sink.put(bundle->fKey, value, !hasParent, errorCode);
2296
1.51M
    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
676k
        StackUResourceBundle parentBundle;
2306
676k
        UResourceBundle &parentRef = parentBundle.ref();
2307
676k
        parentRef.fData = parentEntry;
2308
676k
        parentRef.fValidLocaleDataEntry = bundle->fValidLocaleDataEntry;
2309
676k
        parentRef.fHasFallback = !parentRef.getResData().noFallback;
2310
676k
        parentRef.fIsTopLevel = true;
2311
676k
        parentRef.fRes = parentRef.getResData().rootRes;
2312
676k
        parentRef.fSize = res_countArrayItems(&parentRef.getResData(), parentRef.fRes);
2313
676k
        parentRef.fIndex = -1;
2314
676k
        entryIncrease(parentEntry);
2315
2316
        // Look up the container item in the parent bundle.
2317
676k
        StackUResourceBundle containerBundle;
2318
676k
        const UResourceBundle *rb;
2319
676k
        UErrorCode pathErrorCode = U_ZERO_ERROR;  // Ignore if parents up to root do not have this path.
2320
676k
        if (bundle->fResPath == nullptr || *bundle->fResPath == 0) {
2321
0
            rb = parentBundle.getAlias();
2322
676k
        } else {
2323
676k
            rb = ures_getByKeyWithFallback(parentBundle.getAlias(), bundle->fResPath,
2324
676k
                                           containerBundle.getAlias(), &pathErrorCode);
2325
676k
        }
2326
676k
        if (U_SUCCESS(pathErrorCode)) {
2327
676k
            getAllItemsWithFallback(rb, value, sink, errorCode);
2328
676k
        }
2329
676k
    }
2330
1.51M
}
2331
2332
struct GetAllChildrenSink : public ResourceSink {
2333
    // Destination sink
2334
    ResourceSink& dest;
2335
2336
    GetAllChildrenSink(ResourceSink& dest)
2337
85.4k
        : dest(dest) {}
2338
    virtual ~GetAllChildrenSink() override;
2339
    virtual void put(const char *key, ResourceValue &value, UBool isRoot,
2340
160k
           UErrorCode &errorCode) override {
2341
160k
        ResourceTable itemsTable = value.getTable(errorCode);
2342
160k
        if (U_FAILURE(errorCode)) { return; }
2343
6.52M
        for (int32_t i = 0; itemsTable.getKeyAndValue(i, key, value); ++i) {
2344
6.36M
            if (value.getType() == URES_ALIAS) {
2345
1.22M
                ResourceDataValue& rdv = static_cast<ResourceDataValue&>(value);
2346
1.22M
                StackUResourceBundle stackTempBundle;
2347
1.22M
                UResourceBundle* aliasRB = getAliasTargetAsResourceBundle(rdv.getData(), rdv.getResource(), nullptr, -1,
2348
1.22M
                                                                          rdv.getValidLocaleDataEntry(), nullptr, 0,
2349
1.22M
                                                                          stackTempBundle.getAlias(), &errorCode);
2350
1.22M
                if (U_SUCCESS(errorCode)) {
2351
1.22M
                    ResourceDataValue aliasedValue;
2352
1.22M
                    aliasedValue.setData(aliasRB->getResData());
2353
1.22M
                    aliasedValue.setValidLocaleDataEntry(aliasRB->fValidLocaleDataEntry);
2354
1.22M
                    aliasedValue.setResource(aliasRB->fRes, ResourceTracer(aliasRB));
2355
                    
2356
1.22M
                    if (aliasedValue.getType() != URES_TABLE) {
2357
0
                        dest.put(key, aliasedValue, isRoot, errorCode);
2358
1.22M
                    } 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.22M
                        UResType aliasedValueType = URES_TABLE;
2366
1.22M
                        CharString tablePath;
2367
1.22M
                        tablePath.append(aliasRB->fResPath, errorCode);
2368
1.22M
                        const char* parentKey = key; // dest.put() changes the key
2369
1.22M
                        dest.put(parentKey, aliasedValue, isRoot, errorCode);
2370
1.22M
                        UResourceDataEntry* entry = aliasRB->fData;
2371
1.22M
                        Resource res = aliasRB->fRes;
2372
2.90M
                        while (aliasedValueType == URES_TABLE && entry->fParent != nullptr) {
2373
1.67M
                            CharString localPath;
2374
1.67M
                            localPath.copyFrom(tablePath, errorCode);
2375
1.67M
                            char* localPathAsCharPtr = localPath.data();
2376
1.67M
                            const char* childKey;
2377
1.67M
                            entry = entry->fParent;
2378
1.67M
                            res = entry->fData.rootRes;
2379
1.67M
                            Resource newRes = res_findResource(&entry->fData, res, &localPathAsCharPtr, &childKey);
2380
1.67M
                            if (newRes != RES_BOGUS) {
2381
1.66M
                                aliasedValue.setData(entry->fData);
2382
                                // TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
2383
1.66M
                                aliasedValue.setResource(newRes, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
2384
1.66M
                                aliasedValueType = aliasedValue.getType();
2385
1.66M
                                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
536k
                                    ResourceDataValue& rdv2 = static_cast<ResourceDataValue&>(aliasedValue);
2392
536k
                                    aliasRB = getAliasTargetAsResourceBundle(rdv2.getData(), rdv2.getResource(), nullptr, -1,
2393
536k
                                                                             rdv2.getValidLocaleDataEntry(), nullptr, 0,
2394
536k
                                                                             stackTempBundle.getAlias(), &errorCode);
2395
536k
                                    tablePath.clear();
2396
536k
                                    tablePath.append(aliasRB->fResPath, errorCode);
2397
536k
                                    entry = aliasRB->fData;
2398
536k
                                    res = aliasRB->fRes;
2399
536k
                                    aliasedValue.setData(entry->fData);
2400
                                    // TODO: do I also need to call aliasedValue.setValueLocaleDataEntry() ?
2401
536k
                                    aliasedValue.setResource(res, ResourceTracer(aliasRB)); // probably wrong to use aliasRB here
2402
536k
                                    aliasedValueType = aliasedValue.getType();
2403
536k
                                }
2404
1.66M
                                if (aliasedValueType == URES_TABLE) {
2405
1.66M
                                    dest.put(parentKey, aliasedValue, isRoot, errorCode);
2406
1.66M
                                } 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.66M
                            }
2413
1.67M
                        }
2414
1.22M
                    }
2415
1.22M
                }
2416
5.13M
            } else {
2417
5.13M
                dest.put(key, value, isRoot, errorCode);
2418
5.13M
            }
2419
6.36M
            if (U_FAILURE(errorCode)) { return; }
2420
6.36M
        }
2421
160k
    }
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
85.4k
                                icu::ResourceSink &sink, UErrorCode &errorCode) {
2430
85.4k
    GetAllChildrenSink allChildrenSink(sink);
2431
85.4k
    ures_getAllItemsWithFallback(bundle, path, allChildrenSink, errorCode);
2432
85.4k
}
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
18
                          ResourceDataValue &value, UErrorCode &errorCode) {
2449
18
    if (U_FAILURE(errorCode)) { return; }
2450
18
    if (path == nullptr) {
2451
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
2452
0
        return;
2453
0
    }
2454
18
    const UResourceBundle *rb;
2455
18
    if (*path == 0) {
2456
        // empty path
2457
0
        rb = bundle;
2458
18
    } else {
2459
18
        rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode);
2460
18
        if (U_FAILURE(errorCode)) {
2461
0
            return;
2462
0
        }
2463
18
    }
2464
18
    value.setData(rb->getResData());
2465
18
    value.setValidLocaleDataEntry(rb->fValidLocaleDataEntry);
2466
18
    value.setResource(rb->fRes, ResourceTracer(rb));
2467
18
}
2468
2469
U_CAPI void U_EXPORT2
2470
ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,
2471
983k
                             icu::ResourceSink &sink, UErrorCode &errorCode) {
2472
983k
    if (U_FAILURE(errorCode)) { return; }
2473
983k
    if (path == nullptr) {
2474
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
2475
0
        return;
2476
0
    }
2477
983k
    StackUResourceBundle stackBundle;
2478
983k
    const UResourceBundle *rb;
2479
983k
    if (*path == 0) {
2480
        // empty path
2481
12.5k
        rb = bundle;
2482
970k
    } else {
2483
970k
        rb = ures_getByKeyWithFallback(bundle, path, stackBundle.getAlias(), &errorCode);
2484
970k
        if (U_FAILURE(errorCode)) {
2485
141k
            return;
2486
141k
        }
2487
970k
    }
2488
    // Get all table items with fallback.
2489
842k
    ResourceDataValue value;
2490
842k
    getAllItemsWithFallback(rb, value, sink, errorCode);
2491
842k
}
2492
2493
7.22M
U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
2494
7.22M
    Resource res = RES_BOGUS;
2495
7.22M
    UResourceDataEntry *dataEntry = nullptr;
2496
7.22M
    const char *key = inKey;
2497
2498
7.22M
    if (status==nullptr || U_FAILURE(*status)) {
2499
14.0k
        return fillIn;
2500
14.0k
    }
2501
7.21M
    if(resB == nullptr) {
2502
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2503
0
        return fillIn;
2504
0
    }
2505
2506
7.21M
    int32_t type = RES_GET_TYPE(resB->fRes);
2507
7.21M
    if(URES_IS_TABLE(type)) {
2508
7.21M
        int32_t t;
2509
7.21M
        res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key);
2510
7.21M
        if(res == RES_BOGUS) {
2511
1.04M
            key = inKey;
2512
1.04M
            if(resB->fHasFallback) {
2513
869k
                dataEntry = getFallbackData(resB, &key, &res, status);
2514
869k
                if(U_SUCCESS(*status)) {
2515
                    /* check if resB->fResPath gives the right name here */
2516
440k
                    return init_resb_result(dataEntry, res, key, -1, resB, fillIn, status);
2517
440k
                } else {
2518
429k
                    *status = U_MISSING_RESOURCE_ERROR;
2519
429k
                }
2520
869k
            } else {
2521
170k
                *status = U_MISSING_RESOURCE_ERROR;
2522
170k
            }
2523
6.17M
        } else {
2524
6.17M
            return init_resb_result(resB->fData, res, key, -1, resB, fillIn, status);
2525
6.17M
        }
2526
7.21M
    } 
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
600k
    return fillIn;
2544
7.21M
}
2545
2546
1.44M
U_CAPI const char16_t* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
2547
1.44M
    Resource res = RES_BOGUS;
2548
1.44M
    UResourceDataEntry *dataEntry = nullptr;
2549
1.44M
    const char* key = inKey;
2550
2551
1.44M
    if (status==nullptr || U_FAILURE(*status)) {
2552
782
        return nullptr;
2553
782
    }
2554
1.44M
    if(resB == nullptr) {
2555
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2556
0
        return nullptr;
2557
0
    }
2558
2559
1.44M
    int32_t type = RES_GET_TYPE(resB->fRes);
2560
1.44M
    if(URES_IS_TABLE(type)) {
2561
1.44M
        int32_t t=0;
2562
2563
1.44M
        res = res_getTableItemByKey(&resB->getResData(), resB->fRes, &t, &key);
2564
2565
1.44M
        if(res == RES_BOGUS) {
2566
554k
            key = inKey;
2567
554k
            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
554k
            } else {
2589
554k
                *status = U_MISSING_RESOURCE_ERROR;
2590
554k
            }
2591
885k
        } else {
2592
885k
            switch (RES_GET_TYPE(res)) {
2593
249
            case URES_STRING:
2594
885k
            case URES_STRING_V2:
2595
885k
                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
249
              }
2604
0
            default:
2605
0
                *status = U_RESOURCE_TYPE_MISMATCH;
2606
885k
            }
2607
885k
        }
2608
1.44M
    } 
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
554k
    return nullptr;
2627
1.44M
}
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
22.2k
{
2649
22.2k
    if (status==nullptr || U_FAILURE(*status)) {
2650
0
        return nullptr;
2651
0
    }
2652
22.2k
    if (!resourceBundle) {
2653
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2654
0
        return nullptr;
2655
22.2k
    } else {
2656
22.2k
      return resourceBundle->fData->fName;
2657
22.2k
    }
2658
22.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
1.22M
                     UErrorCode* status) {
2672
1.22M
    if (status==nullptr || U_FAILURE(*status)) {
2673
4
        return nullptr;
2674
4
    }
2675
1.22M
    if (!resourceBundle) {
2676
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2677
0
        return nullptr;
2678
1.22M
    } else {
2679
1.22M
        switch(type) {
2680
511k
        case ULOC_ACTUAL_LOCALE:
2681
511k
            return resourceBundle->fData->fName;
2682
710k
        case ULOC_VALID_LOCALE:
2683
710k
            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
1.22M
        }
2689
1.22M
    }
2690
1.22M
}
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
6.75M
                  UResOpenType openType, UErrorCode* status) {
2713
6.75M
    if(U_FAILURE(*status)) {
2714
1.82k
        return nullptr;
2715
1.82k
    }
2716
2717
6.75M
    UResourceDataEntry *entry;
2718
6.75M
    if(openType != URES_OPEN_DIRECT) {
2719
3.80M
        if (localeID == nullptr) {
2720
0
            localeID = uloc_getDefault();
2721
0
        }
2722
        /* first "canonicalize" the locale ID */
2723
3.80M
        CharString canonLocaleID = ulocimp_getBaseName(localeID, *status);
2724
3.80M
        if(U_FAILURE(*status)) {
2725
293
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2726
293
            return nullptr;
2727
293
        }
2728
3.80M
        entry = entryOpen(path, canonLocaleID.data(), openType, status);
2729
3.80M
    } else {
2730
2.95M
        entry = entryOpenDirect(path, localeID, status);
2731
2.95M
    }
2732
6.75M
    if(U_FAILURE(*status)) {
2733
0
        return nullptr;
2734
0
    }
2735
6.75M
    if(entry == nullptr) {
2736
6
        *status = U_MISSING_RESOURCE_ERROR;
2737
6
        return nullptr;
2738
6
    }
2739
2740
6.75M
    UBool isStackObject;
2741
6.75M
    if(r == nullptr) {
2742
6.75M
        r = static_cast<UResourceBundle*>(uprv_malloc(sizeof(UResourceBundle)));
2743
6.75M
        if(r == nullptr) {
2744
0
            entryClose(entry);
2745
0
            *status = U_MEMORY_ALLOCATION_ERROR;
2746
0
            return nullptr;
2747
0
        }
2748
6.75M
        isStackObject = false;
2749
6.75M
    } else {  // fill-in
2750
168
        isStackObject = ures_isStackObject(r);
2751
168
        ures_closeBundle(r, false);
2752
168
    }
2753
6.75M
    uprv_memset(r, 0, sizeof(UResourceBundle));
2754
6.75M
    ures_setIsStackObject(r, isStackObject);
2755
2756
6.75M
    r->fValidLocaleDataEntry = r->fData = entry;
2757
6.75M
    r->fHasFallback = openType != URES_OPEN_DIRECT && !r->getResData().noFallback;
2758
6.75M
    r->fIsTopLevel = true;
2759
6.75M
    r->fRes = r->getResData().rootRes;
2760
6.75M
    r->fSize = res_countArrayItems(&r->getResData(), r->fRes);
2761
6.75M
    r->fIndex = -1;
2762
2763
6.75M
    ResourceTracer(r).traceOpen();
2764
2765
6.75M
    return r;
2766
6.75M
}
2767
2768
U_CAPI UResourceBundle* U_EXPORT2
2769
3.77M
ures_open(const char* path, const char* localeID, UErrorCode* status) {
2770
3.77M
    return ures_openWithType(nullptr, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2771
3.77M
}
2772
2773
U_CAPI UResourceBundle* U_EXPORT2
2774
27.9k
ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
2775
27.9k
    return ures_openWithType(nullptr, path, localeID, URES_OPEN_LOCALE_ROOT, status);
2776
27.9k
}
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.95M
ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
2784
2.95M
    return ures_openWithType(nullptr, path, localeID, URES_OPEN_DIRECT, status);
2785
2.95M
}
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
168
ures_openDirectFillIn(UResourceBundle *r, const char* path, const char* localeID, UErrorCode* status) {
2809
168
    if(U_SUCCESS(*status) && r == nullptr) {
2810
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2811
0
        return;
2812
0
    }
2813
168
    ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status);
2814
168
}
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
165k
#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
332k
                                             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
332k
    UErrorCode subStatus = U_ZERO_ERROR;
3047
332k
    parent.clear();
3048
332k
    if (res != nullptr) {
3049
332k
        ures_getByKey(res, "%%Parent", bund1, &subStatus);
3050
332k
        if (U_SUCCESS(subStatus)) {
3051
14.1k
            int32_t length16;
3052
14.1k
            const char16_t* s16 = ures_getString(bund1, &length16, &subStatus);
3053
14.1k
            parent.appendInvariantChars(s16, length16, subStatus);
3054
14.1k
        }
3055
332k
    }
3056
    
3057
    // If none there, use normal truncation parent
3058
332k
    if (U_FAILURE(subStatus) || parent.isEmpty()) {
3059
318k
        subStatus = U_ZERO_ERROR;
3060
318k
        parent = ulocimp_getParent(localeID, subStatus);
3061
318k
    }
3062
332k
}
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
53.7k
{
3069
53.7k
    CharString defVal; /* default value for given locale */
3070
53.7k
    CharString defLoc; /* default value for given locale */
3071
53.7k
    CharString found;
3072
53.7k
    CharString parent;
3073
53.7k
    CharString full;
3074
53.7k
    UResourceBundle bund1, bund2;
3075
53.7k
    UResourceBundle *res = nullptr;
3076
53.7k
    UErrorCode subStatus = U_ZERO_ERROR;
3077
53.7k
    int32_t length = 0;
3078
53.7k
    if(U_FAILURE(*status)) return 0;
3079
53.7k
    CharString kwVal;
3080
53.7k
    if (keyword != nullptr && *keyword != '\0') {
3081
53.7k
        kwVal = ulocimp_getKeywordValue(locid, keyword, subStatus);
3082
53.7k
        if (kwVal == DEFAULT_TAG) {
3083
6
            kwVal.clear();
3084
6
        }
3085
53.7k
    }
3086
53.7k
    if (locid == nullptr) {
3087
0
        locid = uloc_getDefault();
3088
0
    }
3089
53.7k
    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
53.7k
    ures_initStackObject(&bund1);
3095
53.7k
    ures_initStackObject(&bund2);
3096
3097
53.7k
    parent.copyFrom(base, subStatus);
3098
53.7k
    found.copyFrom(base, subStatus);
3099
3100
53.7k
    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
53.7k
    if(U_FAILURE(subStatus)) {
3110
1.02k
        *status = subStatus;
3111
1.02k
        return 0;
3112
1.02k
    }
3113
    
3114
183k
    do {
3115
183k
        subStatus = U_ZERO_ERROR;
3116
183k
        res = ures_open(path, parent.data(), &subStatus);
3117
183k
        if(((subStatus == U_USING_FALLBACK_WARNING) ||
3118
182k
            (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
3119
0
        {
3120
0
            *isAvailable = false;
3121
0
        }
3122
183k
        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
183k
        if(U_FAILURE(subStatus)) {
3128
0
            *status = subStatus;
3129
183k
        } else if(subStatus == U_ZERO_ERROR) {
3130
171k
            ures_getByKey(res,resName,&bund1, &subStatus);
3131
171k
            if(subStatus == U_ZERO_ERROR) {
3132
111k
                const char16_t *defUstr;
3133
111k
                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
111k
                defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
3140
111k
                if(U_SUCCESS(subStatus) && defLen) {
3141
52.4k
                    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
52.4k
                    defLoc.copyFrom(parent, subStatus);
3147
52.4k
                    if(kwVal.isEmpty()) {
3148
50.9k
                        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
50.9k
                    }
3154
52.4k
                }
3155
111k
            }
3156
171k
        }
3157
        
3158
183k
        subStatus = U_ZERO_ERROR;
3159
3160
183k
        if (res != nullptr) {
3161
183k
            found.clear().append(ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus), subStatus);
3162
183k
        }
3163
3164
183k
        if (found != parent) {
3165
61.9k
            parent.copyFrom(found, subStatus);
3166
121k
        } else {
3167
121k
            getParentForFunctionalEquivalent(found.data(),res,&bund1,parent);
3168
121k
        }
3169
183k
        ures_close(res);
3170
183k
    } 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
52.7k
    parent.copyFrom(base, subStatus);
3174
52.7k
    found.copyFrom(base, subStatus);
3175
3176
177k
    do {
3177
177k
        res = ures_open(path, parent.data(), &subStatus);
3178
177k
        if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
3179
0
            *isAvailable = false;
3180
0
        }
3181
177k
        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
177k
        if(U_FAILURE(subStatus)) {
3188
18
            *status = subStatus;
3189
177k
        } else if(subStatus == U_ZERO_ERROR) {
3190
99.7k
            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
99.7k
            if(subStatus == U_ZERO_ERROR) {
3195
59.0k
                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
59.0k
                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
50.9k
                    if (parent.isEmpty()) {
3205
9.18k
                        full.clear().append("root", subStatus);
3206
41.8k
                    } else {
3207
41.8k
                        full.copyFrom(parent, subStatus);
3208
41.8k
                    }
3209
                        /* now, recalculate default kw if need be */
3210
50.9k
                        if(defLoc.length() > full.length()) {
3211
219
                          const char16_t *defUstr;
3212
219
                          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
219
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
3219
219
                          if(U_SUCCESS(subStatus) && defLen) {
3220
94
                            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
94
                            defLoc.copyFrom(full, subStatus);
3226
94
                          }
3227
219
                        } /* 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
50.9k
                } 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
8.02k
                }
3239
59.0k
            }
3240
99.7k
        }
3241
        
3242
177k
        subStatus = U_ZERO_ERROR;
3243
177k
        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
177k
        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
177k
        if (!haveFound) {
3263
177k
            found.copyFrom(parent, subStatus);
3264
177k
        }
3265
3266
177k
        getParentForFunctionalEquivalent(found.data(),res,&bund1,parent);
3267
177k
        ures_close(res);
3268
177k
        subStatus = U_ZERO_ERROR;
3269
177k
    } while(full.isEmpty() && !found.isEmpty() && U_SUCCESS(*status));
3270
3271
52.7k
    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
33.5k
        do { /* search for 'default' named item */
3280
33.5k
            res = ures_open(path, parent.data(), &subStatus);
3281
33.5k
            if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
3282
0
                *isAvailable = false;
3283
0
            }
3284
33.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
33.5k
            if(U_FAILURE(subStatus)) {
3291
0
                *status = subStatus;
3292
33.5k
            } else if(subStatus == U_ZERO_ERROR) {
3293
7.28k
                ures_getByKey(res,resName,&bund1, &subStatus);
3294
7.28k
                if(subStatus == U_ZERO_ERROR) {
3295
4.60k
                    ures_getByKey(&bund1, kwVal.data(), &bund2, &subStatus);
3296
4.60k
                    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
950
                            full.clear().append("root", subStatus);
3303
950
                        } else {
3304
471
                            full.copyFrom(parent, subStatus);
3305
471
                        }
3306
                        
3307
                        /* now, recalculate default kw if need be */
3308
1.42k
                        if(defLoc.length() > full.length()) {
3309
138
                          const char16_t *defUstr;
3310
138
                          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
138
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
3317
138
                          if(U_SUCCESS(subStatus) && defLen) {
3318
91
                            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
91
                            defLoc.copyFrom(full, subStatus);
3324
91
                          }
3325
138
                        } /* 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.60k
                }
3333
7.28k
            }
3334
            
3335
33.5k
            subStatus = U_ZERO_ERROR;
3336
33.5k
            found.copyFrom(parent, subStatus);
3337
33.5k
            getParentForFunctionalEquivalent(found.data(),res,&bund1,parent);
3338
33.5k
            ures_close(res);
3339
33.5k
            subStatus = U_ZERO_ERROR;
3340
33.5k
        } while(full.isEmpty() && !found.isEmpty() && U_SUCCESS(*status));
3341
1.49k
    }
3342
    
3343
52.7k
    if(U_SUCCESS(*status)) {
3344
52.7k
        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
317
          *status = U_MISSING_RESOURCE_ERROR;
3349
52.4k
        } 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
52.7k
        found.copyFrom(full, subStatus);
3365
52.7k
        if(!kwVal.isEmpty()) {
3366
52.4k
            found
3367
52.4k
                .append("@", subStatus)
3368
52.4k
                .append(keyword, subStatus)
3369
52.4k
                .append("=", subStatus)
3370
52.4k
                .append(kwVal, subStatus);
3371
52.4k
        } else if(!omitDefault) {
3372
317
            found
3373
317
                .append("@", subStatus)
3374
317
                .append(keyword, subStatus)
3375
317
                .append("=", subStatus)
3376
317
                .append(defVal, subStatus);
3377
317
        }
3378
52.7k
    }
3379
    /* we found the default locale - no need to repeat it.*/
3380
    
3381
52.7k
    ures_close(&bund1);
3382
52.7k
    ures_close(&bund2);
3383
    
3384
52.7k
    length = found.length();
3385
3386
52.7k
    if(U_SUCCESS(*status)) {
3387
52.4k
        int32_t copyLength = uprv_min(length, resultCapacity);
3388
52.4k
        if(copyLength>0) {
3389
52.4k
            found.extract(result, copyLength, subStatus);
3390
52.4k
        }
3391
52.4k
        if(length == 0) {
3392
0
          *status = U_MISSING_RESOURCE_ERROR; 
3393
0
        }
3394
52.4k
    } else {
3395
335
        length = 0;
3396
335
        result[0]=0;
3397
335
    }
3398
52.7k
    return u_terminateChars(result, resultCapacity, length, status);
3399
53.7k
}
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 */