Coverage Report

Created: 2021-08-22 09:07

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