Coverage Report

Created: 2026-06-13 06:44

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