Coverage Report

Created: 2023-03-04 07:00

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