Coverage Report

Created: 2026-03-12 06:42

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