Coverage Report

Created: 2022-11-20 06:20

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