Coverage Report

Created: 2025-06-24 06:43

/src/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
0
static int32_t U_CALLCONV hashEntry(const UHashTok parm) {
58
0
    UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer;
59
0
    UHashTok namekey, pathkey;
60
0
    namekey.pointer = b->fName;
61
0
    pathkey.pointer = b->fPath;
62
0
    return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);
63
0
}
64
65
/* INTERNAL: compares two entries */
66
0
static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) {
67
0
    UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer;
68
0
    UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer;
69
0
    UHashTok name1, name2, path1, path2;
70
0
    name1.pointer = b1->fName;
71
0
    name2.pointer = b2->fName;
72
0
    path1.pointer = b1->fPath;
73
0
    path2.pointer = b2->fPath;
74
0
    return (UBool)(uhash_compareChars(name1, name2) &&
75
0
        uhash_compareChars(path1, path2));
76
0
}
77
78
79
/**
80
 *  Internal function, gets parts of locale name according 
81
 *  to the position of '_' character
82
 */
83
0
static UBool chopLocale(char *name) {
84
0
    char *i = uprv_strrchr(name, '_');
85
86
0
    if(i != NULL) {
87
0
        *i = '\0';
88
0
        return TRUE;
89
0
    }
90
91
0
    return FALSE;
92
0
}
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
0
static UBool mayHaveParent(char *name) {
100
0
    return (name[0] != 0 && uprv_strstr("nb nn",name) != nullptr);
101
0
}
102
103
/**
104
 *  Internal function
105
 */
106
0
static void entryIncrease(UResourceDataEntry *entry) {
107
0
    Mutex lock(&resbMutex);
108
0
    entry->fCountExisting++;
109
0
    while(entry->fParent != NULL) {
110
0
      entry = entry->fParent;
111
0
      entry->fCountExisting++;
112
0
    }
113
0
}
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
0
static void U_CALLCONV createCache(UErrorCode &status) {
278
0
    U_ASSERT(cache == NULL);
279
0
    cache = uhash_open(hashEntry, compareEntries, NULL, &status);
280
0
    ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
281
0
}
282
     
283
0
static void initCache(UErrorCode *status) {
284
0
    umtx_initOnce(gCacheInitOnce, &createCache, *status);
285
0
}
286
287
/** INTERNAL: sets the name (locale) of the resource bundle to given name */
288
289
0
static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) {
290
0
    int32_t len = (int32_t)uprv_strlen(name);
291
0
    if(res->fName != NULL && res->fName != res->fNameBuffer) {
292
0
        uprv_free(res->fName);
293
0
    }
294
0
    if (len < (int32_t)sizeof(res->fNameBuffer)) {
295
0
        res->fName = res->fNameBuffer;
296
0
    }
297
0
    else {
298
0
        res->fName = (char *)uprv_malloc(len+1);
299
0
    }
300
0
    if(res->fName == NULL) {
301
0
        *status = U_MEMORY_ALLOCATION_ERROR;
302
0
    } else {
303
0
        uprv_strcpy(res->fName, name);
304
0
    }
305
0
}
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
0
static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) {
315
0
    UResourceDataEntry *r = NULL;
316
0
    UResourceDataEntry find;
317
    /*int32_t hashValue;*/
318
0
    const char *name;
319
0
    char aliasName[100] = { 0 };
320
0
    int32_t aliasLen = 0;
321
    /*UBool isAlias = FALSE;*/
322
    /*UHashTok hashkey; */
323
324
0
    if(U_FAILURE(*status)) {
325
0
        return NULL;
326
0
    }
327
328
    /* here we try to deduce the right locale name */
329
0
    if(localeID == NULL) { /* if localeID is NULL, we're trying to open default locale */
330
0
        name = uloc_getDefault();
331
0
    } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */
332
0
        name = kRootLocaleName;
333
0
    } else { /* otherwise, we'll open what we're given */
334
0
        name = localeID;
335
0
    }
336
337
0
    find.fName = (char *)name;
338
0
    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
0
    r = (UResourceDataEntry *)uhash_get(cache, &find);
346
0
    if(r == NULL) {
347
        /* if the entry is not yet in the hash table, we'll try to construct a new one */
348
0
        r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry));
349
0
        if(r == NULL) {
350
0
            *status = U_MEMORY_ALLOCATION_ERROR;
351
0
            return NULL;
352
0
        }
353
354
0
        uprv_memset(r, 0, sizeof(UResourceDataEntry));
355
        /*r->fHashKey = hashValue;*/
356
357
0
        setEntryName(r, name, status);
358
0
        if (U_FAILURE(*status)) {
359
0
            uprv_free(r);
360
0
            return NULL;
361
0
        }
362
363
0
        if(path != NULL) {
364
0
            r->fPath = (char *)uprv_strdup(path);
365
0
            if(r->fPath == NULL) {
366
0
                *status = U_MEMORY_ALLOCATION_ERROR;
367
0
                uprv_free(r);
368
0
                return NULL;
369
0
            }
370
0
        }
371
372
        /* this is the actual loading */
373
0
        res_load(&(r->fData), r->fPath, r->fName, status);
374
375
0
        if (U_FAILURE(*status)) {
376
            /* if we failed to load due to an out-of-memory error, exit early. */
377
0
            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
0
            *status = U_USING_FALLBACK_WARNING;
383
0
            r->fBogus = U_USING_FALLBACK_WARNING;
384
0
        } else { /* if we have a regular entry */
385
0
            Resource aliasres;
386
0
            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
0
            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
0
                aliasres = res_getResource(&(r->fData), "%%ALIAS");
404
0
                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
0
            }
413
0
        }
414
415
0
        {
416
0
            UResourceDataEntry *oldR = NULL;
417
0
            if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == NULL) { /* if the data is not cached */
418
                /* just insert it in the cache */
419
0
                UErrorCode cacheStatus = U_ZERO_ERROR;
420
0
                uhash_put(cache, (void *)r, r, &cacheStatus);
421
0
                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
0
        }
433
434
0
    }
435
0
    if(r != NULL) {
436
        /* return the real bundle */
437
0
        while(r->fAlias != NULL) {
438
0
            r = r->fAlias;
439
0
        }
440
0
        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
0
        if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
444
0
             *status = r->fBogus; /* set the returning status */
445
0
        }
446
0
    }
447
0
    return r;
448
0
}
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
0
                  UBool *isRoot, UBool *hasChopped, UBool *isDefault, UErrorCode* status) {
466
0
    UResourceDataEntry *r = NULL;
467
0
    UBool hasRealData = FALSE;
468
0
    const char *defaultLoc = uloc_getDefault();
469
0
    *hasChopped = TRUE; /* we're starting with a fresh name */
470
471
0
    while(*hasChopped && !hasRealData) {
472
0
        r = init_entry(name, path, status);
473
        /* Null pointer test */
474
0
        if (U_FAILURE(*status)) {
475
0
            return NULL;
476
0
        }
477
0
        *isDefault = (UBool)(uprv_strncmp(name, defaultLoc, uprv_strlen(name)) == 0);
478
0
        hasRealData = (UBool)(r->fBogus == U_ZERO_ERROR);
479
0
        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
0
            r->fCountExisting--;
486
            /*entryCloseInt(r);*/
487
0
            r = NULL;
488
0
            *status = U_USING_FALLBACK_WARNING;
489
0
        } else {
490
0
            uprv_strcpy(name, r->fName); /* this is needed for supporting aliases */
491
0
        }
492
493
0
        *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0);
494
495
        /*Fallback data stuff*/
496
0
        *hasChopped = chopLocale(name);
497
0
        if (*hasChopped && *name == '\0') {
498
0
            uprv_strcpy(name, "und");
499
0
        }
500
0
    }
501
0
    return r;
502
0
}
503
504
0
static void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
505
0
    if(state) {
506
0
        resB->fMagic1 = 0;
507
0
        resB->fMagic2 = 0;
508
0
    } else {
509
0
        resB->fMagic1 = MAGIC1;
510
0
        resB->fMagic2 = MAGIC2;
511
0
    }
512
0
}
513
514
0
static UBool ures_isStackObject(const UResourceBundle* resB) {
515
0
  return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?FALSE:TRUE);
516
0
}
517
518
519
0
U_CFUNC void ures_initStackObject(UResourceBundle* resB) {
520
0
  uprv_memset(resB, 0, sizeof(UResourceBundle));
521
0
  ures_setIsStackObject(resB, TRUE);
522
0
}
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
0
                      UBool usingUSRData, char usrDataPath[], UErrorCode *status) {
540
0
    if (U_FAILURE(*status)) { return FALSE; }
541
0
    UBool checkParent = TRUE;
542
0
    while (checkParent && t1->fParent == NULL && !t1->fData.noFallback &&
543
0
            res_getResource(&t1->fData,"%%ParentIsRoot") == RES_BOGUS) {
544
0
        Resource parentRes = res_getResource(&t1->fData, "%%Parent");
545
0
        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
0
            }
555
0
        }
556
        // Insert regular parents.
557
0
        UErrorCode parentStatus = U_ZERO_ERROR;
558
0
        UResourceDataEntry *t2 = init_entry(name, t1->fPath, &parentStatus);
559
0
        if (U_FAILURE(parentStatus)) {
560
0
            *status = parentStatus;
561
0
            return FALSE;
562
0
        }
563
0
        UResourceDataEntry *u2 = NULL;
564
0
        UErrorCode usrStatus = U_ZERO_ERROR;
565
0
        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
0
        }
573
574
0
        if (usingUSRData && U_SUCCESS(usrStatus) && u2->fBogus == U_ZERO_ERROR) {
575
0
            t1->fParent = u2;
576
0
            u2->fParent = t2;
577
0
        } else {
578
0
            t1->fParent = t2;
579
0
            if (usingUSRData) {
580
                // The USR override data wasn't found, set it to be deleted.
581
0
                u2->fCountExisting = 0;
582
0
            }
583
0
        }
584
0
        t1 = t2;
585
0
        checkParent = chopLocale(name) || mayHaveParent(name);
586
0
    }
587
0
    return TRUE;
588
0
}
589
590
static UBool  // returns U_SUCCESS(*status)
591
0
insertRootBundle(UResourceDataEntry *&t1, UErrorCode *status) {
592
0
    if (U_FAILURE(*status)) { return FALSE; }
593
0
    UErrorCode parentStatus = U_ZERO_ERROR;
594
0
    UResourceDataEntry *t2 = init_entry(kRootLocaleName, t1->fPath, &parentStatus);
595
0
    if (U_FAILURE(parentStatus)) {
596
0
        *status = parentStatus;
597
0
        return FALSE;
598
0
    }
599
0
    t1->fParent = t2;
600
0
    t1 = t2;
601
0
    return TRUE;
602
0
}
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
0
                                     UResOpenType openType, UErrorCode* status) {
638
0
    U_ASSERT(openType != URES_OPEN_DIRECT);
639
0
    UErrorCode intStatus = U_ZERO_ERROR;
640
0
    UResourceDataEntry *r = NULL;
641
0
    UResourceDataEntry *t1 = NULL;
642
0
    UBool isDefault = FALSE;
643
0
    UBool isRoot = FALSE;
644
0
    UBool hasRealData = FALSE;
645
0
    UBool hasChopped = TRUE;
646
0
    UBool usingUSRData = U_USE_USRDATA && ( path == NULL || uprv_strncmp(path,U_ICUDATA_NAME,8) == 0);
647
648
0
    char name[ULOC_FULLNAME_CAPACITY];
649
0
    char usrDataPath[96];
650
651
0
    initCache(status);
652
653
0
    if(U_FAILURE(*status)) {
654
0
        return NULL;
655
0
    }
656
657
0
    uprv_strncpy(name, localeID, sizeof(name) - 1);
658
0
    name[sizeof(name) - 1] = 0;
659
660
0
    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
0
    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
0
    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
0
    if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
679
0
        *status = intStatus;
680
0
        goto finish;
681
0
    }
682
683
0
    if(r != NULL) { /* if there is one real locale, we can look for parents. */
684
0
        t1 = r;
685
0
        hasRealData = TRUE;
686
0
        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
0
        if ((hasChopped || mayHaveParent(name)) && !isRoot) {
705
0
            if (!loadParentsExceptRoot(t1, name, UPRV_LENGTHOF(name), usingUSRData, usrDataPath, status)) {
706
0
                goto finish;
707
0
            }
708
0
        }
709
0
    }
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
0
    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
0
            }
733
0
        }
734
0
    }
735
736
    /* we could still have r == NULL at this point - maybe even default locale is not */
737
    /* present */
738
0
    if(r == NULL) {
739
0
        uprv_strcpy(name, kRootLocaleName);
740
0
        r = findFirstExisting(path, name, &isRoot, &hasChopped, &isDefault, &intStatus);
741
        // If we failed due to out-of-memory, report the failure and exit early.
742
0
        if (intStatus == U_MEMORY_ALLOCATION_ERROR) {
743
0
            *status = intStatus;
744
0
            goto finish;
745
0
        }
746
0
        if(r != NULL) {
747
0
            t1 = r;
748
0
            intStatus = U_USING_DEFAULT_WARNING;
749
0
            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
0
    } else if(!isRoot && uprv_strcmp(t1->fName, kRootLocaleName) != 0 &&
755
0
            t1->fParent == NULL && !r->fData.noFallback) {
756
0
        if (!insertRootBundle(t1, status)) {
757
0
            goto finish;
758
0
        }
759
0
        if(!hasRealData) {
760
0
            r->fBogus = U_USING_DEFAULT_WARNING;
761
0
        }
762
0
    }
763
764
    // TODO: Does this ever loop?
765
0
    while(r != NULL && !isRoot && t1->fParent != NULL) {
766
0
        t1->fParent->fCountExisting++;
767
0
        t1 = t1->fParent;
768
0
    }
769
770
0
finish:
771
0
    if(U_SUCCESS(*status)) {
772
0
        if(intStatus != U_ZERO_ERROR) {
773
0
            *status = intStatus;  
774
0
        }
775
0
        return r;
776
0
    } else {
777
0
        return NULL;
778
0
    }
779
0
}
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
0
static void entryCloseInt(UResourceDataEntry *resB) {
841
0
    UResourceDataEntry *p = resB;
842
843
0
    while(resB != NULL) {
844
0
        p = resB->fParent;
845
0
        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
0
        resB = p;
866
0
    }
867
0
}
868
869
/** 
870
 *  API: closes a resource bundle and cleans up.
871
 */
872
873
0
static void entryClose(UResourceDataEntry *resB) {
874
0
  Mutex lock(&resbMutex);
875
0
  entryCloseInt(resB);
876
0
}
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
0
static void ures_appendResPath(UResourceBundle *resB, const char* toAdd, int32_t lenToAdd, UErrorCode *status) {
896
0
    int32_t resPathLenOrig = resB->fResPathLen;
897
0
    if(resB->fResPath == NULL) {
898
0
        resB->fResPath = resB->fResBuf;
899
0
        *(resB->fResPath) = 0;
900
0
        resB->fResPathLen = 0;
901
0
    } 
902
0
    resB->fResPathLen += lenToAdd;
903
0
    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
0
    uprv_strcpy(resB->fResPath + resPathLenOrig, toAdd);
923
0
}
924
925
0
static void ures_freeResPath(UResourceBundle *resB) {
926
0
    if (resB->fResPath && resB->fResPath != resB->fResBuf) {
927
0
        uprv_free(resB->fResPath);
928
0
    }
929
0
    resB->fResPath = NULL;
930
0
    resB->fResPathLen = 0;
931
0
}
932
933
static void
934
ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj)
935
0
{
936
0
    if(resB != NULL) {
937
0
        if(resB->fData != NULL) {
938
0
            entryClose(resB->fData);
939
0
        }
940
0
        if(resB->fVersion != NULL) {
941
0
            uprv_free(resB->fVersion);
942
0
        }
943
0
        ures_freeResPath(resB);
944
945
0
        if(ures_isStackObject(resB) == FALSE && freeBundleObj) {
946
0
            uprv_free(resB);
947
0
        }
948
#if 0 /*U_DEBUG*/
949
        else {
950
            /* poison the data */
951
            uprv_memset(resB, -1, sizeof(UResourceBundle));
952
        }
953
#endif
954
0
    }
955
0
}
956
957
U_CAPI void  U_EXPORT2
958
ures_close(UResourceBundle* resB)
959
0
{
960
0
    ures_closeBundle(resB, TRUE);
961
0
}
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
0
{
968
0
    if(status == NULL || U_FAILURE(*status)) {
969
0
        return resB;
970
0
    }
971
0
    if (parent == NULL) {
972
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
973
0
        return NULL;
974
0
    }
975
0
    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
                                        if (temp == NULL || uprv_strcmp(keyPath, temp) != 0) {
1150
                                            // the call to init_resb_result() above will set resB->fKeyPath to be
1151
                                            // the same as resB->fKey, throwing away any additional path elements if we
1152
                                            // had them-- if the key path wasn't just a single resource ID, clear out
1153
                                            // the bundle's key path and re-set it to be equal to keyPath
1154
0
                                            ures_freeResPath(resB);
1155
0
                                            ures_appendResPath(resB, keyPath, (int32_t)uprv_strlen(keyPath), status);
1156
0
                                            if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1157
0
                                                ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1158
0
                                            }
1159
0
                                        }
1160
0
                                        result = resB;
1161
0
                                        if(result) {
1162
0
                                            r = result->fRes; /* switch to a new resource, possibly a new tree */
1163
0
                                            dataEntry = result->fData;
1164
0
                                        }
1165
0
                                    } else { /* no resource found, we don't really want to look anymore on this level */
1166
0
                                        break;
1167
0
                                    }
1168
0
                                }
1169
0
                                dataEntry = dataEntry->fParent;
1170
0
                                uprv_strcpy(pathBuf, keyPath);
1171
0
                                myPath = pathBuf;
1172
0
                            } while(r == RES_BOGUS && dataEntry != NULL);
1173
0
                            if(r == RES_BOGUS) {
1174
0
                                *status = U_MISSING_RESOURCE_ERROR;
1175
0
                                result = resB;
1176
0
                            }
1177
0
                            if(pathBuf != stackPath) {
1178
0
                                uprv_free(pathBuf);
1179
0
                            }
1180
0
                        }
1181
0
                    } else { /* we failed to open the resource we're aliasing to */
1182
0
                        *status = intStatus;
1183
0
                    }
1184
0
                    if(chAlias != stackAlias) {
1185
0
                        uprv_free(chAlias);
1186
0
                    }
1187
0
                    if(mainRes != result) {
1188
0
                        ures_close(mainRes);
1189
0
                    }
1190
0
                    ResourceTracer(resB).maybeTrace("getalias");
1191
0
                    return result;
1192
0
                }
1193
0
            } else {
1194
                /* bad alias, should be an error */ 
1195
0
                *status = U_ILLEGAL_ARGUMENT_ERROR;
1196
0
                return resB;
1197
0
            }
1198
0
        } else {
1199
0
            *status = U_TOO_MANY_ALIASES_ERROR;
1200
0
            return resB;
1201
0
        }
1202
0
    }
1203
0
    if(resB == NULL) {
1204
0
        resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1205
        /* test for NULL */
1206
0
        if (resB == NULL) {
1207
0
            *status = U_MEMORY_ALLOCATION_ERROR;
1208
0
            return NULL;
1209
0
        }
1210
0
        ures_setIsStackObject(resB, FALSE);
1211
0
        resB->fResPath = NULL;
1212
0
        resB->fResPathLen = 0;
1213
0
    } else {
1214
0
        if(resB->fData != NULL) {
1215
0
            entryClose(resB->fData);
1216
0
        }
1217
0
        if(resB->fVersion != NULL) {
1218
0
            uprv_free(resB->fVersion);
1219
0
        }
1220
        /* 
1221
        weiv: if stack object was passed in, it doesn't really need to be reinited,
1222
        since the purpose of initing is to remove stack junk. However, at this point 
1223
        we would not do anything to an allocated object, so stack object should be
1224
        treated the same
1225
        */
1226
        /*
1227
        if(ures_isStackObject(resB) != FALSE) {
1228
        ures_initStackObject(resB);
1229
        }
1230
        */
1231
0
        if(parent != resB) {
1232
0
            ures_freeResPath(resB);
1233
0
        }
1234
0
    }
1235
0
    resB->fData = realData;
1236
0
    entryIncrease(resB->fData);
1237
0
    resB->fHasFallback = FALSE;
1238
0
    resB->fIsTopLevel = FALSE;
1239
0
    resB->fIndex = -1;
1240
0
    resB->fKey = key; 
1241
    /*resB->fParentRes = parent;*/
1242
0
    resB->fTopLevelData = parent->fTopLevelData;
1243
0
    if(parent->fResPath && parent != resB) {
1244
0
        ures_appendResPath(resB, parent->fResPath, parent->fResPathLen, status);
1245
0
    }
1246
0
    if(key != NULL) {
1247
0
        ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status);
1248
0
        if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1249
0
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1250
0
        }
1251
0
    } else if(idx >= 0) {
1252
0
        char buf[256];
1253
0
        int32_t len = T_CString_integerToString(buf, idx, 10);
1254
0
        ures_appendResPath(resB, buf, len, status);
1255
0
        if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1256
0
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1257
0
        }
1258
0
    }
1259
    /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1260
0
    {
1261
0
        int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
1262
0
        uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
1263
0
    }
1264
1265
0
    resB->fVersion = NULL;
1266
0
    resB->fRes = r;
1267
    /*resB->fParent = parent->fRes;*/
1268
0
    uprv_memmove(&resB->fResData, rdata, sizeof(ResourceData));
1269
0
    resB->fSize = res_countArrayItems(&(resB->fResData), resB->fRes);
1270
0
    ResourceTracer(resB).trace("get");
1271
0
    return resB;
1272
0
}
1273
1274
0
UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) {
1275
0
    UBool isStackObject;
1276
0
    if(U_FAILURE(*status) || r == original) {
1277
0
        return r;
1278
0
    }
1279
0
    if(original != NULL) {
1280
0
        if(r == NULL) {
1281
0
            isStackObject = FALSE;
1282
0
            r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1283
            /* test for NULL */
1284
0
            if (r == NULL) {
1285
0
                *status = U_MEMORY_ALLOCATION_ERROR;
1286
0
                return NULL;
1287
0
            }
1288
0
        } else {
1289
0
            isStackObject = ures_isStackObject(r);
1290
0
            ures_closeBundle(r, FALSE);
1291
0
        }
1292
0
        uprv_memcpy(r, original, sizeof(UResourceBundle));
1293
0
        r->fResPath = NULL;
1294
0
        r->fResPathLen = 0;
1295
0
        if(original->fResPath) {
1296
0
            ures_appendResPath(r, original->fResPath, original->fResPathLen, status);
1297
0
        }
1298
0
        ures_setIsStackObject(r, isStackObject);
1299
0
        if(r->fData != NULL) {
1300
0
            entryIncrease(r->fData);
1301
0
        }
1302
0
    }
1303
0
    return r;
1304
0
}
1305
1306
/**
1307
 * Functions to retrieve data from resource bundles.
1308
 */
1309
1310
0
U_CAPI const UChar* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) {
1311
0
    const UChar *s;
1312
0
    if (status==NULL || U_FAILURE(*status)) {
1313
0
        return NULL;
1314
0
    }
1315
0
    if(resB == NULL) {
1316
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1317
0
        return NULL;
1318
0
    }
1319
0
    s = res_getString({resB}, &(resB->fResData), resB->fRes, len);
1320
0
    if (s == NULL) {
1321
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1322
0
    }
1323
0
    return s;
1324
0
}
1325
1326
static const char *
1327
ures_toUTF8String(const UChar *s16, int32_t length16,
1328
                  char *dest, int32_t *pLength,
1329
                  UBool forceCopy,
1330
0
                  UErrorCode *status) {
1331
0
    int32_t capacity;
1332
1333
0
    if (U_FAILURE(*status)) {
1334
0
        return NULL;
1335
0
    }
1336
0
    if (pLength != NULL) {
1337
0
        capacity = *pLength;
1338
0
    } else {
1339
0
        capacity = 0;
1340
0
    }
1341
0
    if (capacity < 0 || (capacity > 0 && dest == NULL)) {
1342
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1343
0
        return NULL;
1344
0
    }
1345
1346
0
    if (length16 == 0) {
1347
        /* empty string, return as read-only pointer */
1348
0
        if (pLength != NULL) {
1349
0
            *pLength = 0;
1350
0
        }
1351
0
        if (forceCopy) {
1352
0
            u_terminateChars(dest, capacity, 0, status);
1353
0
            return dest;
1354
0
        } else {
1355
0
            return "";
1356
0
        }
1357
0
    } else {
1358
        /* We need to transform the string to the destination buffer. */
1359
0
        if (capacity < length16) {
1360
            /* No chance for the string to fit. Pure preflighting. */
1361
0
            return u_strToUTF8(NULL, 0, pLength, s16, length16, status);
1362
0
        }
1363
0
        if (!forceCopy && (length16 <= 0x2aaaaaaa)) {
1364
            /*
1365
             * We know the string will fit into dest because each UChar turns
1366
             * into at most three UTF-8 bytes. Fill the latter part of dest
1367
             * so that callers do not expect to use dest as a string pointer,
1368
             * hopefully leading to more robust code for when resource bundles
1369
             * may store UTF-8 natively.
1370
             * (In which case dest would not be used at all.)
1371
             *
1372
             * We do not do this if forceCopy=TRUE because then the caller
1373
             * expects the string to start exactly at dest.
1374
             *
1375
             * The test above for <= 0x2aaaaaaa prevents overflows.
1376
             * The +1 is for the NUL terminator.
1377
             */
1378
0
            int32_t maxLength = 3 * length16 + 1;
1379
0
            if (capacity > maxLength) {
1380
0
                dest += capacity - maxLength;
1381
0
                capacity = maxLength;
1382
0
            }
1383
0
        }
1384
0
        return u_strToUTF8(dest, capacity, pLength, s16, length16, status);
1385
0
    }
1386
0
}
1387
1388
U_CAPI const char * U_EXPORT2
1389
ures_getUTF8String(const UResourceBundle *resB,
1390
                   char *dest, int32_t *pLength,
1391
                   UBool forceCopy,
1392
0
                   UErrorCode *status) {
1393
0
    int32_t length16;
1394
0
    const UChar *s16 = ures_getString(resB, &length16, status);
1395
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1396
0
}
1397
1398
U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len, 
1399
0
                                               UErrorCode*               status) {
1400
0
  const uint8_t *p;
1401
0
  if (status==NULL || U_FAILURE(*status)) {
1402
0
    return NULL;
1403
0
  }
1404
0
  if(resB == NULL) {
1405
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1406
0
    return NULL;
1407
0
  }
1408
0
  p = res_getBinary({resB}, &(resB->fResData), resB->fRes, len);
1409
0
  if (p == NULL) {
1410
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1411
0
  }
1412
0
  return p;
1413
0
}
1414
1415
U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len, 
1416
0
                                                   UErrorCode*               status) {
1417
0
  const int32_t *p;
1418
0
  if (status==NULL || U_FAILURE(*status)) {
1419
0
    return NULL;
1420
0
  }
1421
0
  if(resB == NULL) {
1422
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1423
0
    return NULL;
1424
0
  }
1425
0
  p = res_getIntVector({resB}, &(resB->fResData), resB->fRes, len);
1426
0
  if (p == NULL) {
1427
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1428
0
  }
1429
0
  return p;
1430
0
}
1431
1432
/* this function returns a signed integer */ 
1433
/* it performs sign extension */
1434
0
U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) {
1435
0
  if (status==NULL || U_FAILURE(*status)) {
1436
0
    return 0xffffffff;
1437
0
  }
1438
0
  if(resB == NULL) {
1439
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1440
0
    return 0xffffffff;
1441
0
  }
1442
0
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1443
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1444
0
    return 0xffffffff;
1445
0
  }
1446
0
  return res_getInt({resB}, resB->fRes);
1447
0
}
1448
1449
0
U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) {
1450
0
  if (status==NULL || U_FAILURE(*status)) {
1451
0
    return 0xffffffff;
1452
0
  }
1453
0
  if(resB == NULL) {
1454
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1455
0
    return 0xffffffff;
1456
0
  }
1457
0
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1458
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1459
0
    return 0xffffffff;
1460
0
  }
1461
0
  return res_getUInt({resB}, resB->fRes);
1462
0
}
1463
1464
0
U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
1465
0
  if(resB == NULL) {
1466
0
    return URES_NONE;
1467
0
  }
1468
0
  return res_getPublicType(resB->fRes);
1469
0
}
1470
1471
0
U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) {
1472
  //
1473
  // TODO: Trace ures_getKey? I guess not usually.
1474
  //
1475
  // We usually get the key string to decide whether we want the value, or to
1476
  // make a key-value pair. Tracing the value should suffice.
1477
  //
1478
  // However, I believe we have some data (e.g., in res_index) where the key
1479
  // strings are the data. Tracing the enclosing table should suffice.
1480
  //
1481
0
  if(resB == NULL) {
1482
0
    return NULL;
1483
0
  }
1484
0
  return(resB->fKey);
1485
0
}
1486
1487
0
U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
1488
0
  if(resB == NULL) {
1489
0
    return 0;
1490
0
  }
1491
  
1492
0
  return resB->fSize;
1493
0
}
1494
1495
0
static const UChar* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) {
1496
0
  if(RES_GET_TYPE(r) == URES_ALIAS) {
1497
0
    const UChar* result = 0;
1498
0
    UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, NULL, status);
1499
0
    result = ures_getString(tempRes, len, status);
1500
0
    ures_close(tempRes);
1501
0
    return result;
1502
0
  } else {
1503
0
    return res_getString({resB, sIndex}, &(resB->fResData), r, len); 
1504
0
  }
1505
0
}
1506
1507
0
U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){
1508
0
  if(resB == NULL) {
1509
0
    return;
1510
0
  }
1511
0
  resB->fIndex = -1;
1512
0
}
1513
1514
0
U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
1515
0
  if(resB == NULL) {
1516
0
    return FALSE;
1517
0
  }
1518
0
  return (UBool)(resB->fIndex < resB->fSize-1);
1519
0
}
1520
1521
0
U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) {
1522
0
  Resource r = RES_BOGUS;
1523
  
1524
0
  if (status==NULL || U_FAILURE(*status)) {
1525
0
    return NULL;
1526
0
  }
1527
0
  if(resB == NULL) {
1528
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1529
0
    return NULL;
1530
0
  }
1531
  
1532
0
  if(resB->fIndex == resB->fSize-1) {
1533
0
    *status = U_INDEX_OUTOFBOUNDS_ERROR;
1534
0
  } else {
1535
0
    resB->fIndex++;
1536
0
    switch(RES_GET_TYPE(resB->fRes)) {
1537
0
    case URES_STRING:
1538
0
    case URES_STRING_V2:
1539
0
      return res_getString({resB}, &(resB->fResData), resB->fRes, len);
1540
0
    case URES_TABLE:
1541
0
    case URES_TABLE16:
1542
0
    case URES_TABLE32:
1543
0
      r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, key);
1544
0
      if(r == RES_BOGUS && resB->fHasFallback) {
1545
        /* TODO: do the fallback */
1546
0
      }
1547
0
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1548
0
    case URES_ARRAY:
1549
0
    case URES_ARRAY16:
1550
0
      r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1551
0
      if(r == RES_BOGUS && resB->fHasFallback) {
1552
        /* TODO: do the fallback */
1553
0
      }
1554
0
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1555
0
    case URES_ALIAS:
1556
0
      return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status);
1557
0
    case URES_INT:
1558
0
    case URES_BINARY:
1559
0
    case URES_INT_VECTOR:
1560
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1561
0
        U_FALLTHROUGH;
1562
0
    default:
1563
0
      return NULL;
1564
0
    }
1565
0
  }
1566
1567
0
  return NULL;
1568
0
}
1569
1570
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
1571
0
    const char *key = NULL;
1572
0
    Resource r = RES_BOGUS;
1573
1574
0
    if (status==NULL || U_FAILURE(*status)) {
1575
            /*return NULL;*/
1576
0
            return fillIn;
1577
0
    }
1578
0
    if(resB == NULL) {
1579
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
1580
            /*return NULL;*/
1581
0
            return fillIn;
1582
0
    }
1583
1584
0
    if(resB->fIndex == resB->fSize-1) {
1585
0
      *status = U_INDEX_OUTOFBOUNDS_ERROR;
1586
      /*return NULL;*/
1587
0
    } else {
1588
0
        resB->fIndex++;
1589
0
        switch(RES_GET_TYPE(resB->fRes)) {
1590
0
        case URES_INT:
1591
0
        case URES_BINARY:
1592
0
        case URES_STRING:
1593
0
        case URES_STRING_V2:
1594
0
        case URES_INT_VECTOR:
1595
0
            return ures_copyResb(fillIn, resB, status);
1596
0
        case URES_TABLE:
1597
0
        case URES_TABLE16:
1598
0
        case URES_TABLE32:
1599
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);
1600
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1601
                /* TODO: do the fallback */
1602
0
            }
1603
0
            return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1604
0
        case URES_ARRAY:
1605
0
        case URES_ARRAY16:
1606
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1607
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1608
                /* TODO: do the fallback */
1609
0
            }
1610
0
            return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1611
0
        default:
1612
            /*return NULL;*/
1613
0
            return fillIn;
1614
0
        }
1615
0
    }
1616
    /*return NULL;*/
1617
0
    return fillIn;
1618
0
}
1619
1620
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
1621
0
    const char* key = NULL;
1622
0
    Resource r = RES_BOGUS;
1623
1624
0
    if (status==NULL || U_FAILURE(*status)) {
1625
        /*return NULL;*/
1626
0
        return fillIn;
1627
0
    }
1628
0
    if(resB == NULL) {
1629
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1630
        /*return NULL;*/
1631
0
        return fillIn;
1632
0
    }
1633
1634
0
    if(indexR >= 0 && resB->fSize > indexR) {
1635
0
        switch(RES_GET_TYPE(resB->fRes)) {
1636
0
        case URES_INT:
1637
0
        case URES_BINARY:
1638
0
        case URES_STRING:
1639
0
        case URES_STRING_V2:
1640
0
        case URES_INT_VECTOR:
1641
0
            return ures_copyResb(fillIn, resB, status);
1642
0
        case URES_TABLE:
1643
0
        case URES_TABLE16:
1644
0
        case URES_TABLE32:
1645
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);
1646
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1647
                /* TODO: do the fallback */
1648
0
            }
1649
0
            return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1650
0
        case URES_ARRAY:
1651
0
        case URES_ARRAY16:
1652
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, indexR);
1653
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1654
                /* TODO: do the fallback */
1655
0
            }
1656
0
            return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1657
0
        default:
1658
            /*return NULL;*/
1659
0
            return fillIn;
1660
0
        }
1661
0
    } else {
1662
0
        *status = U_MISSING_RESOURCE_ERROR;
1663
0
    }
1664
    /*return NULL;*/
1665
0
    return fillIn;
1666
0
}
1667
1668
0
U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
1669
0
    const char* key = NULL;
1670
0
    Resource r = RES_BOGUS;
1671
1672
0
    if (status==NULL || U_FAILURE(*status)) {
1673
0
        return NULL;
1674
0
    }
1675
0
    if(resB == NULL) {
1676
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1677
0
        return NULL;
1678
0
    }
1679
1680
0
    if(indexS >= 0 && resB->fSize > indexS) {
1681
0
        switch(RES_GET_TYPE(resB->fRes)) {
1682
0
        case URES_STRING:
1683
0
        case URES_STRING_V2:
1684
0
            return res_getString({resB}, &(resB->fResData), resB->fRes, len);
1685
0
        case URES_TABLE:
1686
0
        case URES_TABLE16:
1687
0
        case URES_TABLE32:
1688
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);
1689
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1690
                /* TODO: do the fallback */
1691
0
            }
1692
0
            return ures_getStringWithAlias(resB, r, indexS, len, status);
1693
0
        case URES_ARRAY:
1694
0
        case URES_ARRAY16:
1695
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, indexS);
1696
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1697
                /* TODO: do the fallback */
1698
0
            }
1699
0
            return ures_getStringWithAlias(resB, r, indexS, len, status);
1700
0
        case URES_ALIAS:
1701
0
            return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);
1702
0
        case URES_INT:
1703
0
        case URES_BINARY:
1704
0
        case URES_INT_VECTOR:
1705
0
            *status = U_RESOURCE_TYPE_MISMATCH;
1706
0
            break;
1707
0
        default:
1708
          /* must not occur */
1709
0
          *status = U_INTERNAL_PROGRAM_ERROR;
1710
0
          break;
1711
0
        }
1712
0
    } else {
1713
0
        *status = U_MISSING_RESOURCE_ERROR;
1714
0
    }
1715
0
    return NULL;
1716
0
}
1717
1718
U_CAPI const char * U_EXPORT2
1719
ures_getUTF8StringByIndex(const UResourceBundle *resB,
1720
                          int32_t idx,
1721
                          char *dest, int32_t *pLength,
1722
                          UBool forceCopy,
1723
0
                          UErrorCode *status) {
1724
0
    int32_t length16;
1725
0
    const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status);
1726
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1727
0
}
1728
1729
/*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1730
  return resB->fResPath;
1731
}*/
1732
1733
U_CAPI UResourceBundle* U_EXPORT2
1734
ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status) 
1735
0
{
1736
0
  UResourceBundle *first = NULL; 
1737
0
  UResourceBundle *result = fillIn;
1738
0
  char *packageName = NULL;
1739
0
  char *pathToResource = NULL, *save = NULL;
1740
0
  char *locale = NULL, *localeEnd = NULL;
1741
0
  int32_t length;
1742
1743
0
  if(status == NULL || U_FAILURE(*status)) {
1744
0
    return result;
1745
0
  }
1746
1747
0
  length = (int32_t)(uprv_strlen(path)+1);
1748
0
  save = pathToResource = (char *)uprv_malloc(length*sizeof(char));
1749
  /* test for NULL */
1750
0
  if(pathToResource == NULL) {
1751
0
    *status = U_MEMORY_ALLOCATION_ERROR;
1752
0
    return result;
1753
0
  }
1754
0
  uprv_memcpy(pathToResource, path, length);
1755
1756
0
  locale = pathToResource;
1757
0
  if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
1758
0
    pathToResource++;
1759
0
    packageName = pathToResource;
1760
0
    pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);
1761
0
    if(pathToResource == NULL) {
1762
0
      *status = U_ILLEGAL_ARGUMENT_ERROR;
1763
0
    } else {
1764
0
      *pathToResource = 0;
1765
0
      locale = pathToResource+1;
1766
0
    }
1767
0
  }
1768
1769
0
  localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
1770
0
  if(localeEnd != NULL) {
1771
0
    *localeEnd = 0;
1772
0
  }
1773
1774
0
  first = ures_open(packageName, locale, status);
1775
1776
0
  if(U_SUCCESS(*status)) {
1777
0
    if(localeEnd) {
1778
0
      result = ures_findSubResource(first, localeEnd+1, fillIn, status);
1779
0
    } else {
1780
0
      result = ures_copyResb(fillIn, first, status);
1781
0
    }
1782
0
    ures_close(first);
1783
0
  }
1784
0
  uprv_free(save);
1785
0
  return result;
1786
0
}
1787
1788
U_CAPI UResourceBundle* U_EXPORT2
1789
ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status) 
1790
0
{
1791
0
  Resource res = RES_BOGUS;
1792
0
  UResourceBundle *result = fillIn;
1793
0
  const char *key;
1794
1795
0
  if(status == NULL || U_FAILURE(*status)) {
1796
0
    return result;
1797
0
  }
1798
1799
  /* here we do looping and circular alias checking */
1800
  /* this loop is here because aliasing is resolved on this level, not on res level */
1801
  /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1802
0
  do {
1803
0
    res = res_findResource(&(resB->fResData), resB->fRes, &path, &key); 
1804
0
    if(res != RES_BOGUS) {
1805
0
        result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1806
0
        resB = result;
1807
0
    } else {
1808
0
        *status = U_MISSING_RESOURCE_ERROR;
1809
0
        break;
1810
0
    }
1811
0
  } while(*path); /* there is more stuff in the path */
1812
1813
0
  return result;
1814
0
}
1815
U_CAPI const UChar* U_EXPORT2 
1816
ures_getStringByKeyWithFallback(const UResourceBundle *resB, 
1817
                                const char* inKey, 
1818
                                int32_t* len,
1819
0
                                UErrorCode *status) {
1820
1821
0
    UResourceBundle stack;
1822
0
    const UChar* retVal = NULL;
1823
0
    ures_initStackObject(&stack);
1824
0
    ures_getByKeyWithFallback(resB, inKey, &stack, status);
1825
0
    int32_t length;
1826
0
    retVal = ures_getString(&stack, &length, status);
1827
0
    ures_close(&stack);
1828
0
    if (U_FAILURE(*status)) {
1829
0
        return NULL;
1830
0
    }
1831
0
    if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
1832
0
        retVal = NULL;
1833
0
        length = 0;
1834
0
        *status = U_MISSING_RESOURCE_ERROR;
1835
0
    }
1836
0
    if (len != NULL) {
1837
0
        *len = length;
1838
0
    }
1839
0
    return retVal;
1840
0
}
1841
1842
/*
1843
  Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1844
*/  
1845
0
static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
1846
0
  Resource resource = table;  /* The current resource */
1847
0
  icu::CharString path;
1848
0
  UErrorCode errorCode = U_ZERO_ERROR;
1849
0
  path.append(key, errorCode);
1850
0
  if (U_FAILURE(errorCode)) { return RES_BOGUS; }
1851
0
  char *pathPart = path.data();  /* Path from current resource to desired resource */
1852
0
  UResType type = (UResType)RES_GET_TYPE(resource);  /* the current resource type */
1853
0
  while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
1854
0
    char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
1855
0
    if (nextPathPart != NULL) {
1856
0
      *nextPathPart = 0;  /* Terminating null for this part of path. */
1857
0
      nextPathPart++;
1858
0
    } else {
1859
0
      nextPathPart = uprv_strchr(pathPart, 0);
1860
0
    }
1861
0
    int32_t t;
1862
0
    const char *pathP = pathPart;
1863
0
    resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
1864
0
    type = (UResType)RES_GET_TYPE(resource);
1865
0
    pathPart = nextPathPart; 
1866
0
  }
1867
0
  if (*pathPart) {
1868
0
    return RES_BOGUS;
1869
0
  }
1870
0
  return resource;
1871
0
}
1872
1873
U_CAPI UResourceBundle* U_EXPORT2 
1874
ures_getByKeyWithFallback(const UResourceBundle *resB, 
1875
                          const char* inKey, 
1876
                          UResourceBundle *fillIn, 
1877
0
                          UErrorCode *status) {
1878
0
    Resource res = RES_BOGUS, rootRes = RES_BOGUS;
1879
    /*UResourceDataEntry *realData = NULL;*/
1880
0
    UResourceBundle *helper = NULL;
1881
1882
0
    if (status==NULL || U_FAILURE(*status)) {
1883
0
        return fillIn;
1884
0
    }
1885
0
    if(resB == NULL) {
1886
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1887
0
        return fillIn;
1888
0
    }
1889
1890
0
    int32_t type = RES_GET_TYPE(resB->fRes);
1891
0
    if(URES_IS_TABLE(type)) {
1892
0
        res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey);
1893
0
        const char* key = inKey;
1894
0
        if(res == RES_BOGUS) {
1895
0
            UResourceDataEntry *dataEntry = resB->fData;
1896
0
            CharString path;
1897
0
            char *myPath = NULL;
1898
0
            const char* resPath = resB->fResPath;
1899
0
            int32_t len = resB->fResPathLen;
1900
0
            while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
1901
0
                dataEntry = dataEntry->fParent;
1902
0
                rootRes = dataEntry->fData.rootRes;
1903
1904
0
                if(dataEntry->fBogus == U_ZERO_ERROR) {
1905
0
                    path.clear();
1906
0
                    if (len > 0) {
1907
0
                        path.append(resPath, len, *status);
1908
0
                    }
1909
0
                    path.append(inKey, *status);
1910
0
                    if (U_FAILURE(*status)) {
1911
0
                        ures_close(helper);
1912
0
                        return fillIn;
1913
0
                    }
1914
0
                    myPath = path.data();
1915
0
                    key = inKey;
1916
0
                    do {
1917
0
                        res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
1918
0
                        if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
1919
                            /* We hit an alias, but we didn't finish following the path. */
1920
0
                            helper = init_resb_result(&(dataEntry->fData), res, NULL, -1, dataEntry, resB, 0, helper, status); 
1921
                            /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1922
0
                            if(helper) {
1923
0
                              dataEntry = helper->fData;
1924
0
                              rootRes = helper->fRes;
1925
0
                              resPath = helper->fResPath;
1926
0
                              len = helper->fResPathLen;
1927
1928
0
                            } else {
1929
0
                              break;
1930
0
                            }
1931
0
                        } else if (res == RES_BOGUS) {
1932
0
                            break;
1933
0
                        }
1934
0
                    } while(*myPath); /* Continue until the whole path is consumed */
1935
0
                }
1936
0
            }
1937
            /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1938
0
            if(res != RES_BOGUS) {
1939
              /* check if resB->fResPath gives the right name here */
1940
0
                if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
1941
0
                    *status = U_USING_DEFAULT_WARNING;
1942
0
                } else {
1943
0
                    *status = U_USING_FALLBACK_WARNING;
1944
0
                }
1945
1946
0
                fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);
1947
0
            } else {
1948
0
                *status = U_MISSING_RESOURCE_ERROR;
1949
0
            }
1950
0
        } else {
1951
0
            fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1952
0
        }
1953
0
    } 
1954
0
    else {
1955
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1956
0
    }
1957
0
    ures_close(helper);
1958
0
    return fillIn;
1959
0
}
1960
1961
namespace {
1962
1963
void getAllItemsWithFallback(
1964
        const UResourceBundle *bundle, ResourceDataValue &value,
1965
        ResourceSink &sink,
1966
0
        UErrorCode &errorCode) {
1967
0
    if (U_FAILURE(errorCode)) { return; }
1968
    // We recursively enumerate child-first,
1969
    // only storing parent items in the absence of child items.
1970
    // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
1971
    // to prevent a parent item from being stored.
1972
    //
1973
    // It would be possible to recursively enumerate parent-first,
1974
    // overriding parent items with child items.
1975
    // When the sink sees the no-fallback/no-inheritance marker,
1976
    // then it would remove the parent's item.
1977
    // We would deserialize parent values even though they are overridden in a child bundle.
1978
0
    value.setData(&bundle->fResData);
1979
0
    UResourceDataEntry *parentEntry = bundle->fData->fParent;
1980
0
    UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus);
1981
0
    value.setResource(bundle->fRes, ResourceTracer(bundle));
1982
0
    sink.put(bundle->fKey, value, !hasParent, errorCode);
1983
0
    if (hasParent) {
1984
        // We might try to query the sink whether
1985
        // any fallback from the parent bundle is still possible.
1986
1987
        // Turn the parent UResourceDataEntry into a UResourceBundle,
1988
        // much like in ures_openWithType().
1989
        // TODO: See if we can refactor ures_getByKeyWithFallback()
1990
        // and pull out an inner function that takes and returns a UResourceDataEntry
1991
        // so that we need not create UResourceBundle objects.
1992
0
        UResourceBundle parentBundle;
1993
0
        ures_initStackObject(&parentBundle);
1994
0
        parentBundle.fTopLevelData = parentBundle.fData = parentEntry;
1995
        // TODO: What is the difference between bundle fData and fTopLevelData?
1996
0
        uprv_memcpy(&parentBundle.fResData, &parentEntry->fData, sizeof(ResourceData));
1997
        // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1998
0
        parentBundle.fHasFallback = !parentBundle.fResData.noFallback;
1999
0
        parentBundle.fIsTopLevel = TRUE;
2000
0
        parentBundle.fRes = parentBundle.fResData.rootRes;
2001
0
        parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes);
2002
0
        parentBundle.fIndex = -1;
2003
0
        entryIncrease(parentEntry);
2004
2005
        // Look up the container item in the parent bundle.
2006
0
        UResourceBundle containerBundle;
2007
0
        ures_initStackObject(&containerBundle);
2008
0
        const UResourceBundle *rb;
2009
0
        UErrorCode pathErrorCode = U_ZERO_ERROR;  // Ignore if parents up to root do not have this path.
2010
0
        if (bundle->fResPath == NULL || *bundle->fResPath == 0) {
2011
0
            rb = &parentBundle;
2012
0
        } else {
2013
0
            rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,
2014
0
                                           &containerBundle, &pathErrorCode);
2015
0
        }
2016
0
        if (U_SUCCESS(pathErrorCode)) {
2017
0
            getAllItemsWithFallback(rb, value, sink, errorCode);
2018
0
        }
2019
0
        ures_close(&containerBundle);
2020
0
        ures_close(&parentBundle);
2021
0
    }
2022
0
}
2023
2024
}  // namespace
2025
2026
// Requires a ResourceDataValue fill-in, so that we need not cast from a ResourceValue.
2027
// Unfortunately, the caller must know which subclass to make and pass in.
2028
// Alternatively, we could make it as polymorphic as in Java by
2029
// returning a ResourceValue pointer (possibly wrapped into a LocalPointer)
2030
// that the caller then owns.
2031
//
2032
// Also requires a UResourceBundle fill-in, so that the value's ResourceTracer
2033
// can point to a non-local bundle.
2034
// Without tracing, the child bundle could be a function-local object.
2035
U_CAPI void U_EXPORT2
2036
ures_getValueWithFallback(const UResourceBundle *bundle, const char *path,
2037
                          UResourceBundle *tempFillIn,
2038
0
                          ResourceDataValue &value, UErrorCode &errorCode) {
2039
0
    if (U_FAILURE(errorCode)) { return; }
2040
0
    if (path == nullptr) {
2041
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
2042
0
        return;
2043
0
    }
2044
0
    const UResourceBundle *rb;
2045
0
    if (*path == 0) {
2046
        // empty path
2047
0
        rb = bundle;
2048
0
    } else {
2049
0
        rb = ures_getByKeyWithFallback(bundle, path, tempFillIn, &errorCode);
2050
0
        if (U_FAILURE(errorCode)) {
2051
0
            return;
2052
0
        }
2053
0
    }
2054
0
    value.setData(&rb->fResData);
2055
0
    value.setResource(rb->fRes, ResourceTracer(rb));
2056
0
}
2057
2058
U_CAPI void U_EXPORT2
2059
ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,
2060
0
                             icu::ResourceSink &sink, UErrorCode &errorCode) {
2061
0
    if (U_FAILURE(errorCode)) { return; }
2062
0
    if (path == nullptr) {
2063
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
2064
0
        return;
2065
0
    }
2066
0
    StackUResourceBundle stackBundle;
2067
0
    const UResourceBundle *rb;
2068
0
    if (*path == 0) {
2069
        // empty path
2070
0
        rb = bundle;
2071
0
    } else {
2072
0
        rb = ures_getByKeyWithFallback(bundle, path, stackBundle.getAlias(), &errorCode);
2073
0
        if (U_FAILURE(errorCode)) {
2074
0
            return;
2075
0
        }
2076
0
    }
2077
    // Get all table items with fallback.
2078
0
    ResourceDataValue value;
2079
0
    getAllItemsWithFallback(rb, value, sink, errorCode);
2080
0
}
2081
2082
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
2083
0
    Resource res = RES_BOGUS;
2084
0
    UResourceDataEntry *realData = NULL;
2085
0
    const char *key = inKey;
2086
2087
0
    if (status==NULL || U_FAILURE(*status)) {
2088
0
        return fillIn;
2089
0
    }
2090
0
    if(resB == NULL) {
2091
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2092
0
        return fillIn;
2093
0
    }
2094
2095
0
    int32_t type = RES_GET_TYPE(resB->fRes);
2096
0
    if(URES_IS_TABLE(type)) {
2097
0
        int32_t t;
2098
0
        res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
2099
0
        if(res == RES_BOGUS) {
2100
0
            key = inKey;
2101
0
            if(resB->fHasFallback == TRUE) {
2102
0
                const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2103
0
                if(U_SUCCESS(*status)) {
2104
                  /* check if resB->fResPath gives the right name here */
2105
0
                    return init_resb_result(rd, res, key, -1, realData, resB, 0, fillIn, status);
2106
0
                } else {
2107
0
                    *status = U_MISSING_RESOURCE_ERROR;
2108
0
                }
2109
0
            } else {
2110
0
                *status = U_MISSING_RESOURCE_ERROR;
2111
0
            }
2112
0
        } else {
2113
0
            return init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
2114
0
        }
2115
0
    } 
2116
#if 0
2117
    /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2118
    /* not currently */
2119
    else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2120
        /* here should go a first attempt to locate the key using index table */
2121
        const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2122
        if(U_SUCCESS(*status)) {
2123
            return init_resb_result(rd, res, key, realData, resB, fillIn, status);
2124
        } else {
2125
            *status = U_MISSING_RESOURCE_ERROR;
2126
        }
2127
    }
2128
#endif    
2129
0
    else {
2130
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2131
0
    }
2132
0
    return fillIn;
2133
0
}
2134
2135
0
U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
2136
0
    Resource res = RES_BOGUS;
2137
0
    UResourceDataEntry *realData = NULL;
2138
0
    const char* key = inKey;
2139
2140
0
    if (status==NULL || U_FAILURE(*status)) {
2141
0
        return NULL;
2142
0
    }
2143
0
    if(resB == NULL) {
2144
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2145
0
        return NULL;
2146
0
    }
2147
2148
0
    int32_t type = RES_GET_TYPE(resB->fRes);
2149
0
    if(URES_IS_TABLE(type)) {
2150
0
        int32_t t=0;
2151
2152
0
        res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
2153
2154
0
        if(res == RES_BOGUS) {
2155
0
            key = inKey;
2156
0
            if(resB->fHasFallback == TRUE) {
2157
0
                const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2158
0
                if(U_SUCCESS(*status)) {
2159
0
                    switch (RES_GET_TYPE(res)) {
2160
0
                    case URES_STRING:
2161
0
                    case URES_STRING_V2:
2162
0
                        return res_getString({resB, key}, rd, res, len);
2163
0
                    case URES_ALIAS:
2164
0
                      {
2165
0
                        const UChar* result = 0;
2166
0
                        UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2167
0
                        result = ures_getString(tempRes, len, status);
2168
0
                        ures_close(tempRes);
2169
0
                        return result;
2170
0
                      }
2171
0
                    default:
2172
0
                        *status = U_RESOURCE_TYPE_MISMATCH;
2173
0
                    }
2174
0
                } else {
2175
0
                    *status = U_MISSING_RESOURCE_ERROR;
2176
0
                }
2177
0
            } else {
2178
0
                *status = U_MISSING_RESOURCE_ERROR;
2179
0
            }
2180
0
        } else {
2181
0
            switch (RES_GET_TYPE(res)) {
2182
0
            case URES_STRING:
2183
0
            case URES_STRING_V2:
2184
0
                return res_getString({resB, key}, &(resB->fResData), res, len);
2185
0
            case URES_ALIAS:
2186
0
              {
2187
0
                const UChar* result = 0;
2188
0
                UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2189
0
                result = ures_getString(tempRes, len, status);
2190
0
                ures_close(tempRes);
2191
0
                return result;
2192
0
              }
2193
0
            default:
2194
0
                *status = U_RESOURCE_TYPE_MISMATCH;
2195
0
            }
2196
0
        }
2197
0
    } 
2198
#if 0 
2199
    /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2200
    /* not currently */   
2201
    else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2202
        /* here should go a first attempt to locate the key using index table */
2203
        const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2204
        if(U_SUCCESS(*status)) {
2205
            // TODO: Tracing
2206
            return res_getString(rd, res, len);
2207
        } else {
2208
            *status = U_MISSING_RESOURCE_ERROR;
2209
        }
2210
    } 
2211
#endif    
2212
0
    else {
2213
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2214
0
    }
2215
0
    return NULL;
2216
0
}
2217
2218
U_CAPI const char * U_EXPORT2
2219
ures_getUTF8StringByKey(const UResourceBundle *resB,
2220
                        const char *key,
2221
                        char *dest, int32_t *pLength,
2222
                        UBool forceCopy,
2223
0
                        UErrorCode *status) {
2224
0
    int32_t length16;
2225
0
    const UChar *s16 = ures_getStringByKey(resB, key, &length16, status);
2226
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
2227
0
}
2228
2229
/* TODO: clean from here down */
2230
2231
/**
2232
 *  INTERNAL: Get the name of the first real locale (not placeholder) 
2233
 *  that has resource bundle data.
2234
 */
2235
U_CAPI const char*  U_EXPORT2
2236
ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
2237
0
{
2238
0
    if (status==NULL || U_FAILURE(*status)) {
2239
0
        return NULL;
2240
0
    }
2241
0
    if (!resourceBundle) {
2242
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2243
0
        return NULL;
2244
0
    } else {
2245
0
      return resourceBundle->fData->fName;
2246
0
    }
2247
0
}
2248
2249
U_CAPI const char* U_EXPORT2 
2250
ures_getLocale(const UResourceBundle* resourceBundle, 
2251
               UErrorCode* status)
2252
0
{
2253
0
  return ures_getLocaleInternal(resourceBundle, status);
2254
0
}
2255
2256
2257
U_CAPI const char* U_EXPORT2 
2258
ures_getLocaleByType(const UResourceBundle* resourceBundle, 
2259
                     ULocDataLocaleType type, 
2260
0
                     UErrorCode* status) {
2261
0
    if (status==NULL || U_FAILURE(*status)) {
2262
0
        return NULL;
2263
0
    }
2264
0
    if (!resourceBundle) {
2265
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2266
0
        return NULL;
2267
0
    } else {
2268
0
        switch(type) {
2269
0
        case ULOC_ACTUAL_LOCALE:
2270
0
            return resourceBundle->fData->fName;
2271
0
        case ULOC_VALID_LOCALE:
2272
0
            return resourceBundle->fTopLevelData->fName;
2273
0
        case ULOC_REQUESTED_LOCALE:
2274
0
        default:
2275
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2276
0
            return NULL;
2277
0
        }
2278
0
    }
2279
0
}
2280
2281
0
U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
2282
0
  if(resB == NULL) {
2283
0
    return NULL;
2284
0
  }
2285
2286
0
  return resB->fData->fName;
2287
0
}
2288
2289
#ifdef URES_DEBUG
2290
U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
2291
  if(resB == NULL) {
2292
    return NULL;
2293
  }
2294
2295
  return resB->fData->fPath;
2296
}
2297
#endif
2298
2299
static UResourceBundle*
2300
ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
2301
0
                  UResOpenType openType, UErrorCode* status) {
2302
0
    if(U_FAILURE(*status)) {
2303
0
        return NULL;
2304
0
    }
2305
2306
0
    UResourceDataEntry *entry;
2307
0
    if(openType != URES_OPEN_DIRECT) {
2308
        /* first "canonicalize" the locale ID */
2309
0
        char canonLocaleID[ULOC_FULLNAME_CAPACITY];
2310
0
        uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
2311
0
        if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
2312
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2313
0
            return NULL;
2314
0
        }
2315
0
        entry = entryOpen(path, canonLocaleID, openType, status);
2316
0
    } else {
2317
0
        entry = entryOpenDirect(path, localeID, status);
2318
0
    }
2319
0
    if(U_FAILURE(*status)) {
2320
0
        return NULL;
2321
0
    }
2322
0
    if(entry == NULL) {
2323
0
        *status = U_MISSING_RESOURCE_ERROR;
2324
0
        return NULL;
2325
0
    }
2326
2327
0
    UBool isStackObject;
2328
0
    if(r == NULL) {
2329
0
        r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
2330
0
        if(r == NULL) {
2331
0
            entryClose(entry);
2332
0
            *status = U_MEMORY_ALLOCATION_ERROR;
2333
0
            return NULL;
2334
0
        }
2335
0
        isStackObject = FALSE;
2336
0
    } else {  // fill-in
2337
0
        isStackObject = ures_isStackObject(r);
2338
0
        ures_closeBundle(r, FALSE);
2339
0
    }
2340
0
    uprv_memset(r, 0, sizeof(UResourceBundle));
2341
0
    ures_setIsStackObject(r, isStackObject);
2342
2343
0
    r->fTopLevelData = r->fData = entry;
2344
0
    uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));
2345
0
    r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;
2346
0
    r->fIsTopLevel = TRUE;
2347
0
    r->fRes = r->fResData.rootRes;
2348
0
    r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
2349
0
    r->fIndex = -1;
2350
2351
0
    ResourceTracer(r).traceOpen();
2352
2353
0
    return r;
2354
0
}
2355
2356
U_CAPI UResourceBundle* U_EXPORT2
2357
0
ures_open(const char* path, const char* localeID, UErrorCode* status) {
2358
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2359
0
}
2360
2361
U_CAPI UResourceBundle* U_EXPORT2
2362
0
ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
2363
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);
2364
0
}
2365
2366
/**
2367
 *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed 
2368
 *  or sought. However, alias substitution will happen!
2369
 */
2370
U_CAPI UResourceBundle*  U_EXPORT2
2371
0
ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
2372
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);
2373
0
}
2374
2375
/**
2376
 *  Internal API: This function is used to open a resource bundle 
2377
 *  proper fallback chaining is executed while initialization. 
2378
 *  The result is stored in cache for later fallback search.
2379
 * 
2380
 * Same as ures_open(), but uses the fill-in parameter and does not allocate a new bundle.
2381
 */
2382
U_CAPI void U_EXPORT2
2383
ures_openFillIn(UResourceBundle *r, const char* path,
2384
0
                const char* localeID, UErrorCode* status) {
2385
0
    if(U_SUCCESS(*status) && r == NULL) {
2386
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2387
0
        return;
2388
0
    }
2389
0
    ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2390
0
}
2391
2392
/**
2393
 * Same as ures_openDirect(), but uses the fill-in parameter and does not allocate a new bundle.
2394
 */
2395
U_CAPI void U_EXPORT2
2396
0
ures_openDirectFillIn(UResourceBundle *r, const char* path, const char* localeID, UErrorCode* status) {
2397
0
    if(U_SUCCESS(*status) && r == NULL) {
2398
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2399
0
        return;
2400
0
    }
2401
0
    ures_openWithType(r, path, localeID, URES_OPEN_DIRECT, status);
2402
0
}
2403
2404
/**
2405
 *  API: Counts members. For arrays and tables, returns number of resources.
2406
 *  For strings, returns 1.
2407
 */
2408
U_CAPI int32_t  U_EXPORT2
2409
ures_countArrayItems(const UResourceBundle* resourceBundle,
2410
                  const char* resourceKey,
2411
                  UErrorCode* status)
2412
0
{
2413
0
    UResourceBundle resData;
2414
0
    ures_initStackObject(&resData);
2415
0
    if (status==NULL || U_FAILURE(*status)) {
2416
0
        return 0;
2417
0
    }
2418
0
    if(resourceBundle == NULL) {
2419
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2420
0
        return 0;
2421
0
    }
2422
0
    ures_getByKey(resourceBundle, resourceKey, &resData, status);
2423
    
2424
0
    if(resData.fResData.data != NULL) {
2425
0
        int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
2426
0
        ures_close(&resData);
2427
0
        return result;
2428
0
    } else {
2429
0
        *status = U_MISSING_RESOURCE_ERROR;
2430
0
        ures_close(&resData);
2431
0
        return 0;
2432
0
    }
2433
0
}
2434
2435
/**
2436
 * Internal function.
2437
 * Return the version number associated with this ResourceBundle as a string.
2438
 *
2439
 * @param resourceBundle The resource bundle for which the version is checked.
2440
 * @return  A version number string as specified in the resource bundle or its parent.
2441
 *          The caller does not own this string.
2442
 * @see ures_getVersion
2443
 * @internal
2444
 */
2445
U_CAPI const char* U_EXPORT2 
2446
ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
2447
0
{
2448
0
    if (!resourceBundle) return NULL;
2449
2450
0
    if(resourceBundle->fVersion == NULL) {
2451
2452
        /* If the version ID has not been built yet, then do so.  Retrieve */
2453
        /* the minor version from the file. */
2454
0
        UErrorCode status = U_ZERO_ERROR;
2455
0
        int32_t minor_len = 0;
2456
0
        int32_t len;
2457
2458
0
        const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
2459
        
2460
        /* Determine the length of of the final version string.  This is */
2461
        /* the length of the major part + the length of the separator */
2462
        /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2463
        /* the end). */
2464
2465
0
        len = (minor_len > 0) ? minor_len : 1;
2466
    
2467
        /* Allocate the string, and build it up. */
2468
        /* + 1 for zero byte */
2469
2470
2471
0
        ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len); 
2472
        /* Check for null pointer. */
2473
0
        if (((UResourceBundle *)resourceBundle)->fVersion == NULL) {
2474
0
            return NULL;
2475
0
        }
2476
       
2477
0
        if(minor_len > 0) {
2478
0
            u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
2479
0
            resourceBundle->fVersion[len] =  '\0';
2480
0
        }
2481
0
        else {
2482
0
            uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
2483
0
        }
2484
0
    }
2485
2486
0
    return resourceBundle->fVersion;
2487
0
}
2488
2489
U_CAPI const char*  U_EXPORT2
2490
ures_getVersionNumber(const UResourceBundle*   resourceBundle)
2491
0
{
2492
0
    return ures_getVersionNumberInternal(resourceBundle);
2493
0
}
2494
2495
0
U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) {
2496
0
    if (!resB) return;
2497
2498
0
    u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));
2499
0
}
2500
2501
/** Tree support functions *******************************/
2502
0
#define INDEX_LOCALE_NAME "res_index"
2503
0
#define INDEX_TAG         "InstalledLocales"
2504
0
#define DEFAULT_TAG       "default"
2505
2506
#if defined(URES_TREE_DEBUG)
2507
#include <stdio.h>
2508
#endif
2509
2510
typedef struct ULocalesContext {
2511
    UResourceBundle installed;
2512
    UResourceBundle curr;
2513
} ULocalesContext;
2514
2515
static void U_CALLCONV
2516
0
ures_loc_closeLocales(UEnumeration *enumerator) {
2517
0
    ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
2518
0
    ures_close(&ctx->curr);
2519
0
    ures_close(&ctx->installed);
2520
0
    uprv_free(ctx);
2521
0
    uprv_free(enumerator);
2522
0
}
2523
2524
static int32_t U_CALLCONV
2525
0
ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) {
2526
0
    ULocalesContext *ctx = (ULocalesContext *)en->context;
2527
0
    return ures_getSize(&ctx->installed);
2528
0
}
2529
2530
U_CDECL_BEGIN
2531
2532
2533
static const char * U_CALLCONV
2534
ures_loc_nextLocale(UEnumeration* en,
2535
                    int32_t* resultLength,
2536
0
                    UErrorCode* status) {
2537
0
    ULocalesContext *ctx = (ULocalesContext *)en->context;
2538
0
    UResourceBundle *res = &(ctx->installed);
2539
0
    UResourceBundle *k = NULL;
2540
0
    const char *result = NULL;
2541
0
    int32_t len = 0;
2542
0
    if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status)) != 0) {
2543
0
        result = ures_getKey(k);
2544
0
        len = (int32_t)uprv_strlen(result);
2545
0
    }
2546
0
    if (resultLength) {
2547
0
        *resultLength = len;
2548
0
    }
2549
0
    return result;
2550
0
}
2551
2552
static void U_CALLCONV 
2553
ures_loc_resetLocales(UEnumeration* en, 
2554
0
                      UErrorCode* /*status*/) {
2555
0
    UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
2556
0
    ures_resetIterator(res);
2557
0
}
2558
2559
U_CDECL_END
2560
2561
static const UEnumeration gLocalesEnum = {
2562
    NULL,
2563
        NULL,
2564
        ures_loc_closeLocales,
2565
        ures_loc_countLocales,
2566
        uenum_unextDefault,
2567
        ures_loc_nextLocale,
2568
        ures_loc_resetLocales
2569
};
2570
2571
2572
U_CAPI UEnumeration* U_EXPORT2
2573
ures_openAvailableLocales(const char *path, UErrorCode *status)
2574
0
{
2575
0
    UResourceBundle *idx = NULL;
2576
0
    UEnumeration *en = NULL;
2577
0
    ULocalesContext *myContext = NULL;
2578
2579
0
    if(U_FAILURE(*status)) {
2580
0
        return NULL;
2581
0
    }
2582
0
    myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));
2583
0
    en =  (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2584
0
    if(!en || !myContext) {
2585
0
        *status = U_MEMORY_ALLOCATION_ERROR;
2586
0
        uprv_free(en);
2587
0
        uprv_free(myContext);
2588
0
        return NULL;
2589
0
    }
2590
0
    uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
2591
2592
0
    ures_initStackObject(&myContext->installed);
2593
0
    ures_initStackObject(&myContext->curr);
2594
0
    idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);
2595
0
    ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);
2596
0
    if(U_SUCCESS(*status)) {
2597
#if defined(URES_TREE_DEBUG)
2598
        fprintf(stderr, "Got %s::%s::[%s] : %s\n", 
2599
            path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
2600
#endif
2601
0
        en->context = myContext;
2602
0
    } else {
2603
#if defined(URES_TREE_DEBUG)
2604
        fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
2605
#endif
2606
0
        ures_close(&myContext->installed);
2607
0
        uprv_free(myContext);
2608
0
        uprv_free(en);
2609
0
        en = NULL;
2610
0
    }
2611
    
2612
0
    ures_close(idx);
2613
    
2614
0
    return en;
2615
0
}
2616
2617
0
static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) {
2618
0
    const char *loc;
2619
0
    while ((loc = uenum_next(locEnum, NULL, status)) != NULL) {
2620
0
        if (uprv_strcmp(loc, locToSearch) == 0) {
2621
0
            return TRUE;
2622
0
        }
2623
0
    }
2624
0
    return FALSE;
2625
0
}
2626
2627
U_CAPI int32_t U_EXPORT2
2628
ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
2629
                             const char *path, const char *resName, const char *keyword, const char *locid,
2630
                             UBool *isAvailable, UBool omitDefault, UErrorCode *status)
2631
0
{
2632
0
    char kwVal[1024] = ""; /* value of keyword 'keyword' */
2633
0
    char defVal[1024] = ""; /* default value for given locale */
2634
0
    char defLoc[1024] = ""; /* default value for given locale */
2635
0
    char base[1024] = ""; /* base locale */
2636
0
    char found[1024] = "";
2637
0
    char parent[1024] = "";
2638
0
    char full[1024] = "";
2639
0
    UResourceBundle bund1, bund2;
2640
0
    UResourceBundle *res = NULL;
2641
0
    UErrorCode subStatus = U_ZERO_ERROR;
2642
0
    int32_t length = 0;
2643
0
    if(U_FAILURE(*status)) return 0;
2644
0
    uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
2645
0
    if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
2646
0
        kwVal[0]=0;
2647
0
    }
2648
0
    uloc_getBaseName(locid, base, 1024-1,&subStatus);
2649
#if defined(URES_TREE_DEBUG)
2650
    fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n", 
2651
            locid, keyword, kwVal, base, u_errorName(subStatus));
2652
#endif
2653
0
    ures_initStackObject(&bund1);
2654
0
    ures_initStackObject(&bund2);
2655
    
2656
    
2657
0
    uprv_strcpy(parent, base);
2658
0
    uprv_strcpy(found, base);
2659
2660
0
    if(isAvailable) { 
2661
0
        UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);
2662
0
        *isAvailable = TRUE;
2663
0
        if (U_SUCCESS(subStatus)) {
2664
0
            *isAvailable = isLocaleInList(locEnum, parent, &subStatus);
2665
0
        }
2666
0
        uenum_close(locEnum);
2667
0
    }
2668
2669
0
    if(U_FAILURE(subStatus)) {
2670
0
        *status = subStatus;
2671
0
        return 0;
2672
0
    }
2673
    
2674
0
    do {
2675
0
        subStatus = U_ZERO_ERROR;
2676
0
        res = ures_open(path, parent, &subStatus);
2677
0
        if(((subStatus == U_USING_FALLBACK_WARNING) ||
2678
0
            (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
2679
0
        {
2680
0
            *isAvailable = FALSE;
2681
0
        }
2682
0
        isAvailable = NULL; /* only want to set this the first time around */
2683
        
2684
#if defined(URES_TREE_DEBUG)
2685
        fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
2686
#endif
2687
0
        if(U_FAILURE(subStatus)) {
2688
0
            *status = subStatus;
2689
0
        } else if(subStatus == U_ZERO_ERROR) {
2690
0
            ures_getByKey(res,resName,&bund1, &subStatus);
2691
0
            if(subStatus == U_ZERO_ERROR) {
2692
0
                const UChar *defUstr;
2693
0
                int32_t defLen;
2694
                /* look for default item */
2695
#if defined(URES_TREE_DEBUG)
2696
                fprintf(stderr, "%s;%s : loaded default -> %s\n",
2697
                    path?path:"ICUDATA", parent, u_errorName(subStatus));
2698
#endif
2699
0
                defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2700
0
                if(U_SUCCESS(subStatus) && defLen) {
2701
0
                    u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2702
#if defined(URES_TREE_DEBUG)
2703
                    fprintf(stderr, "%s;%s -> default %s=%s,  %s\n", 
2704
                        path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
2705
#endif
2706
0
                    uprv_strcpy(defLoc, parent);
2707
0
                    if(kwVal[0]==0) {
2708
0
                        uprv_strcpy(kwVal, defVal);
2709
#if defined(URES_TREE_DEBUG)
2710
                        fprintf(stderr, "%s;%s -> kwVal =  %s\n", 
2711
                            path?path:"ICUDATA", parent, keyword, kwVal);
2712
#endif
2713
0
                    }
2714
0
                }
2715
0
            }
2716
0
        }
2717
        
2718
0
        subStatus = U_ZERO_ERROR;
2719
2720
0
        if (res != NULL) {
2721
0
            uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));
2722
0
        }
2723
2724
0
        uloc_getParent(found,parent,sizeof(parent),&subStatus);
2725
0
        ures_close(res);
2726
0
    } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));
2727
    
2728
    /* Now, see if we can find the kwVal collator.. start the search over.. */
2729
0
    uprv_strcpy(parent, base);
2730
0
    uprv_strcpy(found, base);
2731
    
2732
0
    do {
2733
0
        subStatus = U_ZERO_ERROR;
2734
0
        res = ures_open(path, parent, &subStatus);
2735
0
        if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2736
0
            *isAvailable = FALSE;
2737
0
        }
2738
0
        isAvailable = NULL; /* only want to set this the first time around */
2739
        
2740
#if defined(URES_TREE_DEBUG)
2741
        fprintf(stderr, "%s;%s -> %s (looking for %s)\n", 
2742
            path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2743
#endif
2744
0
        if(U_FAILURE(subStatus)) {
2745
0
            *status = subStatus;
2746
0
        } else if(subStatus == U_ZERO_ERROR) {
2747
0
            ures_getByKey(res,resName,&bund1, &subStatus);
2748
#if defined(URES_TREE_DEBUG)
2749
/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
2750
#endif
2751
0
            if(subStatus == U_ZERO_ERROR) {
2752
0
                ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2753
#if defined(URES_TREE_DEBUG)
2754
/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
2755
#endif
2756
0
                if(subStatus == U_ZERO_ERROR) {
2757
#if defined(URES_TREE_DEBUG)
2758
                    fprintf(stderr, "%s;%s -> full0 %s=%s,  %s\n", 
2759
                        path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
2760
#endif
2761
0
                    uprv_strcpy(full, parent);
2762
0
                    if(*full == 0) {
2763
0
                        uprv_strcpy(full, "root");
2764
0
                    }
2765
                        /* now, recalculate default kw if need be */
2766
0
                        if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2767
0
                          const UChar *defUstr;
2768
0
                          int32_t defLen;
2769
                          /* look for default item */
2770
#if defined(URES_TREE_DEBUG)
2771
                            fprintf(stderr, "%s;%s -> recalculating Default0\n", 
2772
                                    path?path:"ICUDATA", full);
2773
#endif
2774
0
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2775
0
                          if(U_SUCCESS(subStatus) && defLen) {
2776
0
                            u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2777
#if defined(URES_TREE_DEBUG)
2778
                            fprintf(stderr, "%s;%s -> default0 %s=%s,  %s\n", 
2779
                                    path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2780
#endif
2781
0
                            uprv_strcpy(defLoc, full);
2782
0
                          }
2783
0
                        } /* end of recalculate default KW */
2784
#if defined(URES_TREE_DEBUG)
2785
                        else {
2786
                          fprintf(stderr, "No trim0,  %s <= %s\n", defLoc, full);
2787
                        }
2788
#endif
2789
0
                } else {
2790
#if defined(URES_TREE_DEBUG)
2791
                    fprintf(stderr, "err=%s in %s looking for %s\n", 
2792
                        u_errorName(subStatus), parent, kwVal);
2793
#endif
2794
0
                }
2795
0
            }
2796
0
        }
2797
        
2798
0
        subStatus = U_ZERO_ERROR;
2799
        
2800
0
        uprv_strcpy(found, parent);
2801
0
        uloc_getParent(found,parent,1023,&subStatus);
2802
0
        ures_close(res);
2803
0
    } while(!full[0] && *found && U_SUCCESS(*status));
2804
    
2805
0
    if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
2806
#if defined(URES_TREE_DEBUG)
2807
        fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
2808
#endif
2809
0
        uprv_strcpy(kwVal, defVal);
2810
0
        uprv_strcpy(parent, base);
2811
0
        uprv_strcpy(found, base);
2812
        
2813
0
        do { /* search for 'default' named item */
2814
0
            subStatus = U_ZERO_ERROR;
2815
0
            res = ures_open(path, parent, &subStatus);
2816
0
            if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2817
0
                *isAvailable = FALSE;
2818
0
            }
2819
0
            isAvailable = NULL; /* only want to set this the first time around */
2820
            
2821
#if defined(URES_TREE_DEBUG)
2822
            fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
2823
                path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2824
#endif
2825
0
            if(U_FAILURE(subStatus)) {
2826
0
                *status = subStatus;
2827
0
            } else if(subStatus == U_ZERO_ERROR) {
2828
0
                ures_getByKey(res,resName,&bund1, &subStatus);
2829
0
                if(subStatus == U_ZERO_ERROR) {
2830
0
                    ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2831
0
                    if(subStatus == U_ZERO_ERROR) {
2832
#if defined(URES_TREE_DEBUG)
2833
                        fprintf(stderr, "%s;%s -> full1 %s=%s,  %s\n", path?path:"ICUDATA",
2834
                            parent, keyword, kwVal, u_errorName(subStatus));
2835
#endif
2836
0
                        uprv_strcpy(full, parent);
2837
0
                        if(*full == 0) {
2838
0
                            uprv_strcpy(full, "root");
2839
0
                        }
2840
                        
2841
                        /* now, recalculate default kw if need be */
2842
0
                        if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2843
0
                          const UChar *defUstr;
2844
0
                          int32_t defLen;
2845
                          /* look for default item */
2846
#if defined(URES_TREE_DEBUG)
2847
                            fprintf(stderr, "%s;%s -> recalculating Default1\n", 
2848
                                    path?path:"ICUDATA", full);
2849
#endif
2850
0
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2851
0
                          if(U_SUCCESS(subStatus) && defLen) {
2852
0
                            u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2853
#if defined(URES_TREE_DEBUG)
2854
                            fprintf(stderr, "%s;%s -> default %s=%s,  %s\n", 
2855
                                    path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2856
#endif
2857
0
                            uprv_strcpy(defLoc, full);
2858
0
                          }
2859
0
                        } /* end of recalculate default KW */
2860
#if defined(URES_TREE_DEBUG)
2861
                        else {
2862
                          fprintf(stderr, "No trim1,  %s <= %s\n", defLoc, full);
2863
                        }
2864
#endif
2865
0
                    }
2866
0
                }
2867
0
            }
2868
0
            subStatus = U_ZERO_ERROR;
2869
            
2870
0
            uprv_strcpy(found, parent);
2871
0
            uloc_getParent(found,parent,1023,&subStatus);
2872
0
            ures_close(res);
2873
0
        } while(!full[0] && *found && U_SUCCESS(*status));
2874
0
    }
2875
    
2876
0
    if(U_SUCCESS(*status)) {
2877
0
        if(!full[0]) {
2878
#if defined(URES_TREE_DEBUG)
2879
          fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
2880
#endif
2881
0
          *status = U_MISSING_RESOURCE_ERROR;
2882
0
        } else if(omitDefault) {
2883
#if defined(URES_TREE_DEBUG)
2884
          fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
2885
#endif        
2886
0
          if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
2887
            /* found the keyword in a *child* of where the default tag was present. */
2888
0
            if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
2889
              /* and the default is in or in an ancestor of the current locale */
2890
#if defined(URES_TREE_DEBUG)
2891
              fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
2892
#endif
2893
0
              kwVal[0]=0;
2894
0
            }
2895
0
          }
2896
0
        }
2897
0
        uprv_strcpy(found, full);
2898
0
        if(kwVal[0]) {
2899
0
            uprv_strcat(found, "@");
2900
0
            uprv_strcat(found, keyword);
2901
0
            uprv_strcat(found, "=");
2902
0
            uprv_strcat(found, kwVal);
2903
0
        } else if(!omitDefault) {
2904
0
            uprv_strcat(found, "@");
2905
0
            uprv_strcat(found, keyword);
2906
0
            uprv_strcat(found, "=");
2907
0
            uprv_strcat(found, defVal);
2908
0
        }
2909
0
    }
2910
    /* we found the default locale - no need to repeat it.*/
2911
    
2912
0
    ures_close(&bund1);
2913
0
    ures_close(&bund2);
2914
    
2915
0
    length = (int32_t)uprv_strlen(found);
2916
2917
0
    if(U_SUCCESS(*status)) {
2918
0
        int32_t copyLength = uprv_min(length, resultCapacity);
2919
0
        if(copyLength>0) {
2920
0
            uprv_strncpy(result, found, copyLength);
2921
0
        }
2922
0
        if(length == 0) {
2923
0
          *status = U_MISSING_RESOURCE_ERROR; 
2924
0
        }
2925
0
    } else {
2926
0
        length = 0;
2927
0
        result[0]=0;
2928
0
    }
2929
0
    return u_terminateChars(result, resultCapacity, length, status);
2930
0
}
2931
2932
U_CAPI UEnumeration* U_EXPORT2
2933
ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
2934
0
{
2935
0
#define VALUES_BUF_SIZE 2048
2936
0
#define VALUES_LIST_SIZE 512
2937
    
2938
0
    char       valuesBuf[VALUES_BUF_SIZE];
2939
0
    int32_t    valuesIndex = 0;
2940
0
    const char *valuesList[VALUES_LIST_SIZE];
2941
0
    int32_t    valuesCount = 0;
2942
    
2943
0
    const char *locale;
2944
0
    int32_t     locLen;
2945
    
2946
0
    UEnumeration *locs = NULL;
2947
    
2948
0
    UResourceBundle    item;
2949
0
    UResourceBundle    subItem;
2950
    
2951
0
    ures_initStackObject(&item);
2952
0
    ures_initStackObject(&subItem);
2953
0
    locs = ures_openAvailableLocales(path, status);
2954
    
2955
0
    if(U_FAILURE(*status)) {
2956
0
        ures_close(&item);
2957
0
        ures_close(&subItem);
2958
0
        return NULL;
2959
0
    }
2960
    
2961
0
    valuesBuf[0]=0;
2962
0
    valuesBuf[1]=0;
2963
    
2964
0
    while((locale = uenum_next(locs, &locLen, status)) != 0) {
2965
0
        UResourceBundle   *bund = NULL;
2966
0
        UResourceBundle   *subPtr = NULL;
2967
0
        UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
2968
0
        bund = ures_open(path, locale, &subStatus);
2969
        
2970
#if defined(URES_TREE_DEBUG)
2971
        if(!bund || U_FAILURE(subStatus)) {
2972
            fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n", 
2973
                path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2974
        }
2975
#endif
2976
        
2977
0
        ures_getByKey(bund, keyword, &item, &subStatus);
2978
        
2979
0
        if(!bund || U_FAILURE(subStatus)) {
2980
#if defined(URES_TREE_DEBUG)
2981
            fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n", 
2982
                path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2983
#endif
2984
0
            ures_close(bund);
2985
0
            bund = NULL;
2986
0
            continue;
2987
0
        }
2988
        
2989
0
        while((subPtr = ures_getNextResource(&item,&subItem,&subStatus)) != 0
2990
0
            && U_SUCCESS(subStatus)) {
2991
0
            const char *k;
2992
0
            int32_t i;
2993
0
            k = ures_getKey(subPtr);
2994
2995
#if defined(URES_TREE_DEBUG)
2996
            /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2997
#endif
2998
0
            if(k == NULL || *k == 0 ||
2999
0
                    uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
3000
                // empty or "default" or unlisted type
3001
0
                continue;
3002
0
            }
3003
0
            for(i=0; i<valuesCount; i++) {
3004
0
                if(!uprv_strcmp(valuesList[i],k)) {
3005
0
                    k = NULL; /* found duplicate */
3006
0
                    break;
3007
0
                }
3008
0
            }
3009
0
            if(k != NULL) {
3010
0
                int32_t kLen = (int32_t)uprv_strlen(k);
3011
0
                if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
3012
0
                    ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
3013
0
                    *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
3014
0
                } else {
3015
0
                    uprv_strcpy(valuesBuf+valuesIndex, k);
3016
0
                    valuesList[valuesCount++] = valuesBuf+valuesIndex;
3017
0
                    valuesIndex += kLen;
3018
#if defined(URES_TREE_DEBUG)
3019
                    fprintf(stderr, "%s | %s | %s | [%s]   (UNIQUE)\n",
3020
                        path?path:"<ICUDATA>", keyword, locale, k);
3021
#endif
3022
0
                    valuesBuf[valuesIndex++] = 0; /* terminate */
3023
0
                }
3024
0
            }
3025
0
        }
3026
0
        ures_close(bund);
3027
0
    }
3028
0
    valuesBuf[valuesIndex++] = 0; /* terminate */
3029
    
3030
0
    ures_close(&item);
3031
0
    ures_close(&subItem);
3032
0
    uenum_close(locs);
3033
#if defined(URES_TREE_DEBUG)
3034
    fprintf(stderr, "%s:  size %d, #%d\n", u_errorName(*status), 
3035
        valuesIndex, valuesCount);
3036
#endif
3037
0
    return uloc_openKeywordList(valuesBuf, valuesIndex, status);
3038
0
}
3039
#if 0
3040
/* This code isn't needed, and given the documentation warnings the implementation is suspect */
3041
U_CAPI UBool U_EXPORT2
3042
ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
3043
    if(res1==NULL || res2==NULL){
3044
        return res1==res2; /* pointer comparison */
3045
    }
3046
    if(res1->fKey==NULL||  res2->fKey==NULL){
3047
        return (res1->fKey==res2->fKey);
3048
    }else{
3049
        if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
3050
            return FALSE;
3051
        }
3052
    }
3053
    if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
3054
        return FALSE;
3055
    }
3056
    if(res1->fData->fPath == NULL||  res2->fData->fPath==NULL){
3057
        return (res1->fData->fPath == res2->fData->fPath);
3058
    }else{
3059
        if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
3060
            return FALSE;
3061
        }
3062
    }
3063
    if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
3064
        return FALSE;
3065
    }
3066
    if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
3067
        return FALSE;
3068
    }
3069
    if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
3070
        return FALSE;
3071
    }
3072
    if(res1->fRes != res2->fRes){
3073
        return FALSE;
3074
    }
3075
    return TRUE;
3076
}
3077
U_CAPI UResourceBundle* U_EXPORT2
3078
ures_clone(const UResourceBundle* res, UErrorCode* status){
3079
    UResourceBundle* bundle = NULL;
3080
    UResourceBundle* ret = NULL;
3081
    if(U_FAILURE(*status) || res == NULL){
3082
        return NULL;
3083
    }
3084
    bundle = ures_open(res->fData->fPath, res->fData->fName, status);
3085
    if(res->fResPath!=NULL){
3086
        ret = ures_findSubResource(bundle, res->fResPath, NULL, status);
3087
        ures_close(bundle);
3088
    }else{
3089
        ret = bundle;
3090
    }
3091
    return ret;
3092
}
3093
U_CAPI const UResourceBundle* U_EXPORT2
3094
ures_getParentBundle(const UResourceBundle* res){
3095
    if(res==NULL){
3096
        return NULL;
3097
    }
3098
    return res->fParentRes;
3099
}
3100
#endif
3101
3102
U_CAPI void U_EXPORT2
3103
0
ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) {
3104
0
  const UChar *str;
3105
0
  int32_t len;
3106
0
  str = ures_getStringByKey(res, key, &len, status);
3107
0
  if(U_SUCCESS(*status)) {
3108
0
    u_versionFromUString(ver, str);
3109
0
  } 
3110
0
}
3111
3112
/* eof */