Coverage Report

Created: 2024-10-29 06:58

/src/icu/source/common/uresbund.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
******************************************************************************
5
* Copyright (C) 1997-2016, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
******************************************************************************
8
*
9
* File uresbund.cpp
10
*
11
* Modification History:
12
*
13
*   Date        Name        Description
14
*   04/01/97    aliu        Creation.
15
*   06/14/99    stephen     Removed functions taking a filename suffix.
16
*   07/20/99    stephen     Changed for UResourceBundle typedef'd to void*
17
*   11/09/99    weiv            Added ures_getLocale()
18
*   March 2000  weiv        Total overhaul - using data in DLLs
19
*   06/20/2000  helena      OS/400 port changes; mostly typecast.
20
*   06/24/02    weiv        Added support for resource sharing
21
******************************************************************************
22
*/
23
24
#include "unicode/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
                                    return NULL;
1087
0
                                }
1088
0
                            }
1089
0
                            uprv_strcpy(pathBuf, keyPath);
1090
0
                            result = mainRes;
1091
                            /* now we have fallback following here */
1092
0
                            do {
1093
0
                                r = dataEntry->fData.rootRes;     
1094
                                /* this loop handles 'found' resources over several levels */
1095
0
                                while(*myPath && U_SUCCESS(*status)) {
1096
0
                                    r = res_findResource(&(dataEntry->fData), r, &myPath, &temp);
1097
0
                                    if(r != RES_BOGUS) { /* found a resource, but it might be an indirection */
1098
0
                                        resB = init_resb_result(&(dataEntry->fData), r, temp, -1, dataEntry, result, noAlias+1, resB, status);
1099
0
                                        result = resB;
1100
0
                                        if(result) {
1101
0
                                            r = result->fRes; /* switch to a new resource, possibly a new tree */
1102
0
                                            dataEntry = result->fData;
1103
0
                                        }
1104
0
                                    } else { /* no resource found, we don't really want to look anymore on this level */
1105
0
                                        break;
1106
0
                                    }
1107
0
                                }
1108
0
                                dataEntry = dataEntry->fParent;
1109
0
                                uprv_strcpy(pathBuf, keyPath);
1110
0
                                myPath = pathBuf;
1111
0
                            } while(r == RES_BOGUS && dataEntry != NULL);
1112
0
                            if(r == RES_BOGUS) {
1113
0
                                *status = U_MISSING_RESOURCE_ERROR;
1114
0
                                result = resB;
1115
0
                            }
1116
0
                            if(pathBuf != stackPath) {
1117
0
                                uprv_free(pathBuf);
1118
0
                            }
1119
0
                        }
1120
0
                    } else { /* we failed to open the resource we're aliasing to */
1121
0
                        *status = intStatus;
1122
0
                    }
1123
0
                    if(chAlias != stackAlias) {
1124
0
                        uprv_free(chAlias);
1125
0
                    }
1126
0
                    if(mainRes != result) {
1127
0
                        ures_close(mainRes);
1128
0
                    }
1129
0
                    return result;
1130
0
                }
1131
0
            } else {
1132
                /* bad alias, should be an error */ 
1133
0
                *status = U_ILLEGAL_ARGUMENT_ERROR;
1134
0
                return resB;
1135
0
            }
1136
0
        } else {
1137
0
            *status = U_TOO_MANY_ALIASES_ERROR;
1138
0
            return resB;
1139
0
        }
1140
0
    }
1141
0
    if(resB == NULL) {
1142
0
        resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1143
        /* test for NULL */
1144
0
        if (resB == NULL) {
1145
0
            *status = U_MEMORY_ALLOCATION_ERROR;
1146
0
            return NULL;
1147
0
        }
1148
0
        ures_setIsStackObject(resB, FALSE);
1149
0
        resB->fResPath = NULL;
1150
0
        resB->fResPathLen = 0;
1151
0
    } else {
1152
0
        if(resB->fData != NULL) {
1153
0
            entryClose(resB->fData);
1154
0
        }
1155
0
        if(resB->fVersion != NULL) {
1156
0
            uprv_free(resB->fVersion);
1157
0
        }
1158
        /* 
1159
        weiv: if stack object was passed in, it doesn't really need to be reinited,
1160
        since the purpose of initing is to remove stack junk. However, at this point 
1161
        we would not do anything to an allocated object, so stack object should be
1162
        treated the same
1163
        */
1164
        /*
1165
        if(ures_isStackObject(resB) != FALSE) {
1166
        ures_initStackObject(resB);
1167
        }
1168
        */
1169
0
        if(parent != resB) {
1170
0
            ures_freeResPath(resB);
1171
0
        }
1172
0
    }
1173
0
    resB->fData = realData;
1174
0
    entryIncrease(resB->fData);
1175
0
    resB->fHasFallback = FALSE;
1176
0
    resB->fIsTopLevel = FALSE;
1177
0
    resB->fIndex = -1;
1178
0
    resB->fKey = key; 
1179
    /*resB->fParentRes = parent;*/
1180
0
    resB->fTopLevelData = parent->fTopLevelData;
1181
0
    if(parent->fResPath && parent != resB) {
1182
0
        ures_appendResPath(resB, parent->fResPath, parent->fResPathLen, status);
1183
0
    }
1184
0
    if(key != NULL) {
1185
0
        ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status);
1186
0
        if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1187
0
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1188
0
        }
1189
0
    } else if(idx >= 0) {
1190
0
        char buf[256];
1191
0
        int32_t len = T_CString_integerToString(buf, idx, 10);
1192
0
        ures_appendResPath(resB, buf, len, status);
1193
0
        if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1194
0
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1195
0
        }
1196
0
    }
1197
    /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1198
0
    {
1199
0
        int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
1200
0
        uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
1201
0
    }
1202
1203
0
    resB->fVersion = NULL;
1204
0
    resB->fRes = r;
1205
    /*resB->fParent = parent->fRes;*/
1206
0
    uprv_memmove(&resB->fResData, rdata, sizeof(ResourceData));
1207
0
    resB->fSize = res_countArrayItems(&(resB->fResData), resB->fRes);
1208
0
    return resB;
1209
0
}
1210
1211
0
UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) {
1212
0
    UBool isStackObject;
1213
0
    if(U_FAILURE(*status) || r == original) {
1214
0
        return r;
1215
0
    }
1216
0
    if(original != NULL) {
1217
0
        if(r == NULL) {
1218
0
            isStackObject = FALSE;
1219
0
            r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1220
            /* test for NULL */
1221
0
            if (r == NULL) {
1222
0
                *status = U_MEMORY_ALLOCATION_ERROR;
1223
0
                return NULL;
1224
0
            }
1225
0
        } else {
1226
0
            isStackObject = ures_isStackObject(r);
1227
0
            ures_closeBundle(r, FALSE);
1228
0
        }
1229
0
        uprv_memcpy(r, original, sizeof(UResourceBundle));
1230
0
        r->fResPath = NULL;
1231
0
        r->fResPathLen = 0;
1232
0
        if(original->fResPath) {
1233
0
            ures_appendResPath(r, original->fResPath, original->fResPathLen, status);
1234
0
        }
1235
0
        ures_setIsStackObject(r, isStackObject);
1236
0
        if(r->fData != NULL) {
1237
0
            entryIncrease(r->fData);
1238
0
        }
1239
0
    }
1240
0
    return r;
1241
0
}
1242
1243
/**
1244
 * Functions to retrieve data from resource bundles.
1245
 */
1246
1247
0
U_CAPI const UChar* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) {
1248
0
    const UChar *s;
1249
0
    if (status==NULL || U_FAILURE(*status)) {
1250
0
        return NULL;
1251
0
    }
1252
0
    if(resB == NULL) {
1253
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1254
0
        return NULL;
1255
0
    }
1256
0
    s = res_getString(&(resB->fResData), resB->fRes, len);
1257
0
    if (s == NULL) {
1258
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1259
0
    }
1260
0
    return s;
1261
0
}
1262
1263
static const char *
1264
ures_toUTF8String(const UChar *s16, int32_t length16,
1265
                  char *dest, int32_t *pLength,
1266
                  UBool forceCopy,
1267
0
                  UErrorCode *status) {
1268
0
    int32_t capacity;
1269
1270
0
    if (U_FAILURE(*status)) {
1271
0
        return NULL;
1272
0
    }
1273
0
    if (pLength != NULL) {
1274
0
        capacity = *pLength;
1275
0
    } else {
1276
0
        capacity = 0;
1277
0
    }
1278
0
    if (capacity < 0 || (capacity > 0 && dest == NULL)) {
1279
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1280
0
        return NULL;
1281
0
    }
1282
1283
0
    if (length16 == 0) {
1284
        /* empty string, return as read-only pointer */
1285
0
        if (pLength != NULL) {
1286
0
            *pLength = 0;
1287
0
        }
1288
0
        if (forceCopy) {
1289
0
            u_terminateChars(dest, capacity, 0, status);
1290
0
            return dest;
1291
0
        } else {
1292
0
            return "";
1293
0
        }
1294
0
    } else {
1295
        /* We need to transform the string to the destination buffer. */
1296
0
        if (capacity < length16) {
1297
            /* No chance for the string to fit. Pure preflighting. */
1298
0
            return u_strToUTF8(NULL, 0, pLength, s16, length16, status);
1299
0
        }
1300
0
        if (!forceCopy && (length16 <= 0x2aaaaaaa)) {
1301
            /*
1302
             * We know the string will fit into dest because each UChar turns
1303
             * into at most three UTF-8 bytes. Fill the latter part of dest
1304
             * so that callers do not expect to use dest as a string pointer,
1305
             * hopefully leading to more robust code for when resource bundles
1306
             * may store UTF-8 natively.
1307
             * (In which case dest would not be used at all.)
1308
             *
1309
             * We do not do this if forceCopy=TRUE because then the caller
1310
             * expects the string to start exactly at dest.
1311
             *
1312
             * The test above for <= 0x2aaaaaaa prevents overflows.
1313
             * The +1 is for the NUL terminator.
1314
             */
1315
0
            int32_t maxLength = 3 * length16 + 1;
1316
0
            if (capacity > maxLength) {
1317
0
                dest += capacity - maxLength;
1318
0
                capacity = maxLength;
1319
0
            }
1320
0
        }
1321
0
        return u_strToUTF8(dest, capacity, pLength, s16, length16, status);
1322
0
    }
1323
0
}
1324
1325
U_CAPI const char * U_EXPORT2
1326
ures_getUTF8String(const UResourceBundle *resB,
1327
                   char *dest, int32_t *pLength,
1328
                   UBool forceCopy,
1329
0
                   UErrorCode *status) {
1330
0
    int32_t length16;
1331
0
    const UChar *s16 = ures_getString(resB, &length16, status);
1332
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1333
0
}
1334
1335
U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len, 
1336
0
                                               UErrorCode*               status) {
1337
0
  const uint8_t *p;
1338
0
  if (status==NULL || U_FAILURE(*status)) {
1339
0
    return NULL;
1340
0
  }
1341
0
  if(resB == NULL) {
1342
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1343
0
    return NULL;
1344
0
  }
1345
0
  p = res_getBinary(&(resB->fResData), resB->fRes, len);
1346
0
  if (p == NULL) {
1347
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1348
0
  }
1349
0
  return p;
1350
0
}
1351
1352
U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len, 
1353
0
                                                   UErrorCode*               status) {
1354
0
  const int32_t *p;
1355
0
  if (status==NULL || U_FAILURE(*status)) {
1356
0
    return NULL;
1357
0
  }
1358
0
  if(resB == NULL) {
1359
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1360
0
    return NULL;
1361
0
  }
1362
0
  p = res_getIntVector(&(resB->fResData), resB->fRes, len);
1363
0
  if (p == NULL) {
1364
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1365
0
  }
1366
0
  return p;
1367
0
}
1368
1369
/* this function returns a signed integer */ 
1370
/* it performs sign extension */
1371
0
U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) {
1372
0
  if (status==NULL || U_FAILURE(*status)) {
1373
0
    return 0xffffffff;
1374
0
  }
1375
0
  if(resB == NULL) {
1376
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1377
0
    return 0xffffffff;
1378
0
  }
1379
0
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1380
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1381
0
    return 0xffffffff;
1382
0
  }
1383
0
  return RES_GET_INT(resB->fRes);
1384
0
}
1385
1386
0
U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) {
1387
0
  if (status==NULL || U_FAILURE(*status)) {
1388
0
    return 0xffffffff;
1389
0
  }
1390
0
  if(resB == NULL) {
1391
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1392
0
    return 0xffffffff;
1393
0
  }
1394
0
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1395
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1396
0
    return 0xffffffff;
1397
0
  }
1398
0
  return RES_GET_UINT(resB->fRes);
1399
0
}
1400
1401
0
U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
1402
0
  if(resB == NULL) {
1403
0
    return URES_NONE;
1404
0
  }
1405
0
  return res_getPublicType(resB->fRes);
1406
0
}
1407
1408
0
U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) {
1409
0
  if(resB == NULL) {
1410
0
    return NULL;
1411
0
  }
1412
  
1413
0
  return(resB->fKey);
1414
0
}
1415
1416
0
U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
1417
0
  if(resB == NULL) {
1418
0
    return 0;
1419
0
  }
1420
  
1421
0
  return resB->fSize;
1422
0
}
1423
1424
0
static const UChar* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) {
1425
0
  if(RES_GET_TYPE(r) == URES_ALIAS) {
1426
0
    const UChar* result = 0;
1427
0
    UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, NULL, status);
1428
0
    result = ures_getString(tempRes, len, status);
1429
0
    ures_close(tempRes);
1430
0
    return result;
1431
0
  } else {
1432
0
    return res_getString(&(resB->fResData), r, len); 
1433
0
  }
1434
0
}
1435
1436
0
U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){
1437
0
  if(resB == NULL) {
1438
0
    return;
1439
0
  }
1440
0
  resB->fIndex = -1;
1441
0
}
1442
1443
0
U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
1444
0
  if(resB == NULL) {
1445
0
    return FALSE;
1446
0
  }
1447
0
  return (UBool)(resB->fIndex < resB->fSize-1);
1448
0
}
1449
1450
0
U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) {
1451
0
  Resource r = RES_BOGUS;
1452
  
1453
0
  if (status==NULL || U_FAILURE(*status)) {
1454
0
    return NULL;
1455
0
  }
1456
0
  if(resB == NULL) {
1457
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1458
0
    return NULL;
1459
0
  }
1460
  
1461
0
  if(resB->fIndex == resB->fSize-1) {
1462
0
    *status = U_INDEX_OUTOFBOUNDS_ERROR;
1463
0
  } else {
1464
0
    resB->fIndex++;
1465
0
    switch(RES_GET_TYPE(resB->fRes)) {
1466
0
    case URES_STRING:
1467
0
    case URES_STRING_V2:
1468
0
      return res_getString(&(resB->fResData), resB->fRes, len); 
1469
0
    case URES_TABLE:
1470
0
    case URES_TABLE16:
1471
0
    case URES_TABLE32:
1472
0
      r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, key);
1473
0
      if(r == RES_BOGUS && resB->fHasFallback) {
1474
        /* TODO: do the fallback */
1475
0
      }
1476
0
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1477
0
    case URES_ARRAY:
1478
0
    case URES_ARRAY16:
1479
0
      r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1480
0
      if(r == RES_BOGUS && resB->fHasFallback) {
1481
        /* TODO: do the fallback */
1482
0
      }
1483
0
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1484
0
    case URES_ALIAS:
1485
0
      return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status);
1486
0
    case URES_INT:
1487
0
    case URES_BINARY:
1488
0
    case URES_INT_VECTOR:
1489
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1490
0
        U_FALLTHROUGH;
1491
0
    default:
1492
0
      return NULL;
1493
0
    }
1494
0
  }
1495
1496
0
  return NULL;
1497
0
}
1498
1499
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
1500
0
    const char *key = NULL;
1501
0
    Resource r = RES_BOGUS;
1502
1503
0
    if (status==NULL || U_FAILURE(*status)) {
1504
            /*return NULL;*/
1505
0
            return fillIn;
1506
0
    }
1507
0
    if(resB == NULL) {
1508
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
1509
            /*return NULL;*/
1510
0
            return fillIn;
1511
0
    }
1512
1513
0
    if(resB->fIndex == resB->fSize-1) {
1514
0
      *status = U_INDEX_OUTOFBOUNDS_ERROR;
1515
      /*return NULL;*/
1516
0
    } else {
1517
0
        resB->fIndex++;
1518
0
        switch(RES_GET_TYPE(resB->fRes)) {
1519
0
        case URES_INT:
1520
0
        case URES_BINARY:
1521
0
        case URES_STRING:
1522
0
        case URES_STRING_V2:
1523
0
        case URES_INT_VECTOR:
1524
0
            return ures_copyResb(fillIn, resB, status);
1525
0
        case URES_TABLE:
1526
0
        case URES_TABLE16:
1527
0
        case URES_TABLE32:
1528
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);
1529
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1530
                /* TODO: do the fallback */
1531
0
            }
1532
0
            return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1533
0
        case URES_ARRAY:
1534
0
        case URES_ARRAY16:
1535
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1536
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1537
                /* TODO: do the fallback */
1538
0
            }
1539
0
            return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1540
0
        default:
1541
            /*return NULL;*/
1542
0
            return fillIn;
1543
0
        }
1544
0
    }
1545
    /*return NULL;*/
1546
0
    return fillIn;
1547
0
}
1548
1549
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
1550
0
    const char* key = NULL;
1551
0
    Resource r = RES_BOGUS;
1552
1553
0
    if (status==NULL || U_FAILURE(*status)) {
1554
        /*return NULL;*/
1555
0
        return fillIn;
1556
0
    }
1557
0
    if(resB == NULL) {
1558
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1559
        /*return NULL;*/
1560
0
        return fillIn;
1561
0
    }
1562
1563
0
    if(indexR >= 0 && resB->fSize > indexR) {
1564
0
        switch(RES_GET_TYPE(resB->fRes)) {
1565
0
        case URES_INT:
1566
0
        case URES_BINARY:
1567
0
        case URES_STRING:
1568
0
        case URES_STRING_V2:
1569
0
        case URES_INT_VECTOR:
1570
0
            return ures_copyResb(fillIn, resB, status);
1571
0
        case URES_TABLE:
1572
0
        case URES_TABLE16:
1573
0
        case URES_TABLE32:
1574
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);
1575
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1576
                /* TODO: do the fallback */
1577
0
            }
1578
0
            return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1579
0
        case URES_ARRAY:
1580
0
        case URES_ARRAY16:
1581
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, indexR);
1582
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1583
                /* TODO: do the fallback */
1584
0
            }
1585
0
            return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1586
0
        default:
1587
            /*return NULL;*/
1588
0
            return fillIn;
1589
0
        }
1590
0
    } else {
1591
0
        *status = U_MISSING_RESOURCE_ERROR;
1592
0
    }
1593
    /*return NULL;*/
1594
0
    return fillIn;
1595
0
}
1596
1597
0
U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
1598
0
    const char* key = NULL;
1599
0
    Resource r = RES_BOGUS;
1600
1601
0
    if (status==NULL || U_FAILURE(*status)) {
1602
0
        return NULL;
1603
0
    }
1604
0
    if(resB == NULL) {
1605
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1606
0
        return NULL;
1607
0
    }
1608
1609
0
    if(indexS >= 0 && resB->fSize > indexS) {
1610
0
        switch(RES_GET_TYPE(resB->fRes)) {
1611
0
        case URES_STRING:
1612
0
        case URES_STRING_V2:
1613
0
            return res_getString(&(resB->fResData), resB->fRes, len);
1614
0
        case URES_TABLE:
1615
0
        case URES_TABLE16:
1616
0
        case URES_TABLE32:
1617
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);
1618
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1619
                /* TODO: do the fallback */
1620
0
            }
1621
0
            return ures_getStringWithAlias(resB, r, indexS, len, status);
1622
0
        case URES_ARRAY:
1623
0
        case URES_ARRAY16:
1624
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, indexS);
1625
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1626
                /* TODO: do the fallback */
1627
0
            }
1628
0
            return ures_getStringWithAlias(resB, r, indexS, len, status);
1629
0
        case URES_ALIAS:
1630
0
            return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);
1631
0
        case URES_INT:
1632
0
        case URES_BINARY:
1633
0
        case URES_INT_VECTOR:
1634
0
            *status = U_RESOURCE_TYPE_MISMATCH;
1635
0
            break;
1636
0
        default:
1637
          /* must not occur */
1638
0
          *status = U_INTERNAL_PROGRAM_ERROR;
1639
0
          break;
1640
0
        }
1641
0
    } else {
1642
0
        *status = U_MISSING_RESOURCE_ERROR;
1643
0
    }
1644
0
    return NULL;
1645
0
}
1646
1647
U_CAPI const char * U_EXPORT2
1648
ures_getUTF8StringByIndex(const UResourceBundle *resB,
1649
                          int32_t idx,
1650
                          char *dest, int32_t *pLength,
1651
                          UBool forceCopy,
1652
0
                          UErrorCode *status) {
1653
0
    int32_t length16;
1654
0
    const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status);
1655
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1656
0
}
1657
1658
/*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1659
  return resB->fResPath;
1660
}*/
1661
1662
U_CAPI UResourceBundle* U_EXPORT2
1663
ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status) 
1664
0
{
1665
0
  UResourceBundle *first = NULL; 
1666
0
  UResourceBundle *result = fillIn;
1667
0
  char *packageName = NULL;
1668
0
  char *pathToResource = NULL, *save = NULL;
1669
0
  char *locale = NULL, *localeEnd = NULL;
1670
0
  int32_t length;
1671
1672
0
  if(status == NULL || U_FAILURE(*status)) {
1673
0
    return result;
1674
0
  }
1675
1676
0
  length = (int32_t)(uprv_strlen(path)+1);
1677
0
  save = pathToResource = (char *)uprv_malloc(length*sizeof(char));
1678
  /* test for NULL */
1679
0
  if(pathToResource == NULL) {
1680
0
    *status = U_MEMORY_ALLOCATION_ERROR;
1681
0
    return result;
1682
0
  }
1683
0
  uprv_memcpy(pathToResource, path, length);
1684
1685
0
  locale = pathToResource;
1686
0
  if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
1687
0
    pathToResource++;
1688
0
    packageName = pathToResource;
1689
0
    pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);
1690
0
    if(pathToResource == NULL) {
1691
0
      *status = U_ILLEGAL_ARGUMENT_ERROR;
1692
0
    } else {
1693
0
      *pathToResource = 0;
1694
0
      locale = pathToResource+1;
1695
0
    }
1696
0
  }
1697
1698
0
  localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
1699
0
  if(localeEnd != NULL) {
1700
0
    *localeEnd = 0;
1701
0
  }
1702
1703
0
  first = ures_open(packageName, locale, status);
1704
1705
0
  if(U_SUCCESS(*status)) {
1706
0
    if(localeEnd) {
1707
0
      result = ures_findSubResource(first, localeEnd+1, fillIn, status);
1708
0
    } else {
1709
0
      result = ures_copyResb(fillIn, first, status);
1710
0
    }
1711
0
    ures_close(first);
1712
0
  }
1713
0
  uprv_free(save);
1714
0
  return result;
1715
0
}
1716
1717
U_CAPI UResourceBundle* U_EXPORT2
1718
ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status) 
1719
0
{
1720
0
  Resource res = RES_BOGUS;
1721
0
  UResourceBundle *result = fillIn;
1722
0
  const char *key;
1723
1724
0
  if(status == NULL || U_FAILURE(*status)) {
1725
0
    return result;
1726
0
  }
1727
1728
  /* here we do looping and circular alias checking */
1729
  /* this loop is here because aliasing is resolved on this level, not on res level */
1730
  /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1731
0
  do {
1732
0
    res = res_findResource(&(resB->fResData), resB->fRes, &path, &key); 
1733
0
    if(res != RES_BOGUS) {
1734
0
        result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1735
0
        resB = result;
1736
0
    } else {
1737
0
        *status = U_MISSING_RESOURCE_ERROR;
1738
0
        break;
1739
0
    }
1740
0
  } while(*path); /* there is more stuff in the path */
1741
1742
0
  return result;
1743
0
}
1744
U_INTERNAL const UChar* U_EXPORT2 
1745
ures_getStringByKeyWithFallback(const UResourceBundle *resB, 
1746
                                const char* inKey, 
1747
                                int32_t* len,
1748
0
                                UErrorCode *status) {
1749
1750
0
    UResourceBundle stack;
1751
0
    const UChar* retVal = NULL;
1752
0
    ures_initStackObject(&stack);
1753
0
    ures_getByKeyWithFallback(resB, inKey, &stack, status);
1754
0
    int32_t length;
1755
0
    retVal = ures_getString(&stack, &length, status);
1756
0
    ures_close(&stack);
1757
0
    if (U_FAILURE(*status)) {
1758
0
        return NULL;
1759
0
    }
1760
0
    if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
1761
0
        retVal = NULL;
1762
0
        length = 0;
1763
0
        *status = U_MISSING_RESOURCE_ERROR;
1764
0
    }
1765
0
    if (len != NULL) {
1766
0
        *len = length;
1767
0
    }
1768
0
    return retVal;
1769
0
}
1770
1771
/*
1772
  Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1773
*/  
1774
0
static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
1775
0
  Resource resource = table;  /* The current resource */
1776
0
  icu::CharString path;
1777
0
  UErrorCode errorCode = U_ZERO_ERROR;
1778
0
  path.append(key, errorCode);
1779
0
  if (U_FAILURE(errorCode)) { return RES_BOGUS; }
1780
0
  char *pathPart = path.data();  /* Path from current resource to desired resource */
1781
0
  UResType type = (UResType)RES_GET_TYPE(resource);  /* the current resource type */
1782
0
  while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
1783
0
    char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
1784
0
    if (nextPathPart != NULL) {
1785
0
      *nextPathPart = 0;  /* Terminating null for this part of path. */
1786
0
      nextPathPart++;
1787
0
    } else {
1788
0
      nextPathPart = uprv_strchr(pathPart, 0);
1789
0
    }
1790
0
    int32_t t;
1791
0
    const char *pathP = pathPart;
1792
0
    resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
1793
0
    type = (UResType)RES_GET_TYPE(resource);
1794
0
    pathPart = nextPathPart; 
1795
0
  }
1796
0
  if (*pathPart) {
1797
0
    return RES_BOGUS;
1798
0
  }
1799
0
  return resource;
1800
0
}
1801
1802
U_CAPI UResourceBundle* U_EXPORT2 
1803
ures_getByKeyWithFallback(const UResourceBundle *resB, 
1804
                          const char* inKey, 
1805
                          UResourceBundle *fillIn, 
1806
0
                          UErrorCode *status) {
1807
0
    Resource res = RES_BOGUS, rootRes = RES_BOGUS;
1808
    /*UResourceDataEntry *realData = NULL;*/
1809
0
    UResourceBundle *helper = NULL;
1810
1811
0
    if (status==NULL || U_FAILURE(*status)) {
1812
0
        return fillIn;
1813
0
    }
1814
0
    if(resB == NULL) {
1815
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1816
0
        return fillIn;
1817
0
    }
1818
1819
0
    int32_t type = RES_GET_TYPE(resB->fRes);
1820
0
    if(URES_IS_TABLE(type)) {
1821
0
        res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey);
1822
0
        const char* key = inKey;
1823
0
        if(res == RES_BOGUS) {
1824
0
            UResourceDataEntry *dataEntry = resB->fData;
1825
0
            CharString path;
1826
0
            char *myPath = NULL;
1827
0
            const char* resPath = resB->fResPath;
1828
0
            int32_t len = resB->fResPathLen;
1829
0
            while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
1830
0
                dataEntry = dataEntry->fParent;
1831
0
                rootRes = dataEntry->fData.rootRes;
1832
1833
0
                if(dataEntry->fBogus == U_ZERO_ERROR) {
1834
0
                    path.clear();
1835
0
                    if (len > 0) {
1836
0
                        path.append(resPath, len, *status);
1837
0
                    }
1838
0
                    path.append(inKey, *status);
1839
0
                    if (U_FAILURE(*status)) {
1840
0
                        ures_close(helper);
1841
0
                        return fillIn;
1842
0
                    }
1843
0
                    myPath = path.data();
1844
0
                    key = inKey;
1845
0
                    do {
1846
0
                        res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
1847
0
                        if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
1848
                            /* We hit an alias, but we didn't finish following the path. */
1849
0
                            helper = init_resb_result(&(dataEntry->fData), res, NULL, -1, dataEntry, resB, 0, helper, status); 
1850
                            /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1851
0
                            if(helper) {
1852
0
                              dataEntry = helper->fData;
1853
0
                              rootRes = helper->fRes;
1854
0
                              resPath = helper->fResPath;
1855
0
                              len = helper->fResPathLen;
1856
1857
0
                            } else {
1858
0
                              break;
1859
0
                            }
1860
0
                        }
1861
0
                    } while(*myPath); /* Continue until the whole path is consumed */
1862
0
                }
1863
0
            }
1864
            /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1865
0
            if(res != RES_BOGUS) {
1866
              /* check if resB->fResPath gives the right name here */
1867
0
                if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
1868
0
                    *status = U_USING_DEFAULT_WARNING;
1869
0
                } else {
1870
0
                    *status = U_USING_FALLBACK_WARNING;
1871
0
                }
1872
1873
0
                fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);
1874
0
            } else {
1875
0
                *status = U_MISSING_RESOURCE_ERROR;
1876
0
            }
1877
0
        } else {
1878
0
            fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1879
0
        }
1880
0
    } 
1881
0
    else {
1882
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1883
0
    }
1884
0
    ures_close(helper);
1885
0
    return fillIn;
1886
0
}
1887
1888
namespace {
1889
1890
void getAllItemsWithFallback(
1891
        const UResourceBundle *bundle, ResourceDataValue &value,
1892
        ResourceSink &sink,
1893
0
        UErrorCode &errorCode) {
1894
0
    if (U_FAILURE(errorCode)) { return; }
1895
    // We recursively enumerate child-first,
1896
    // only storing parent items in the absence of child items.
1897
    // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
1898
    // to prevent a parent item from being stored.
1899
    //
1900
    // It would be possible to recursively enumerate parent-first,
1901
    // overriding parent items with child items.
1902
    // When the sink sees the no-fallback/no-inheritance marker,
1903
    // then it would remove the parent's item.
1904
    // We would deserialize parent values even though they are overridden in a child bundle.
1905
0
    value.pResData = &bundle->fResData;
1906
0
    UResourceDataEntry *parentEntry = bundle->fData->fParent;
1907
0
    UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus);
1908
0
    value.setResource(bundle->fRes);
1909
0
    sink.put(bundle->fKey, value, !hasParent, errorCode);
1910
0
    if (hasParent) {
1911
        // We might try to query the sink whether
1912
        // any fallback from the parent bundle is still possible.
1913
1914
        // Turn the parent UResourceDataEntry into a UResourceBundle,
1915
        // much like in ures_openWithType().
1916
        // TODO: See if we can refactor ures_getByKeyWithFallback()
1917
        // and pull out an inner function that takes and returns a UResourceDataEntry
1918
        // so that we need not create UResourceBundle objects.
1919
0
        UResourceBundle parentBundle;
1920
0
        ures_initStackObject(&parentBundle);
1921
0
        parentBundle.fTopLevelData = parentBundle.fData = parentEntry;
1922
        // TODO: What is the difference between bundle fData and fTopLevelData?
1923
0
        uprv_memcpy(&parentBundle.fResData, &parentEntry->fData, sizeof(ResourceData));
1924
        // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1925
0
        parentBundle.fHasFallback = !parentBundle.fResData.noFallback;
1926
0
        parentBundle.fIsTopLevel = TRUE;
1927
0
        parentBundle.fRes = parentBundle.fResData.rootRes;
1928
0
        parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes);
1929
0
        parentBundle.fIndex = -1;
1930
0
        entryIncrease(parentEntry);
1931
1932
        // Look up the container item in the parent bundle.
1933
0
        UResourceBundle containerBundle;
1934
0
        ures_initStackObject(&containerBundle);
1935
0
        const UResourceBundle *rb;
1936
0
        UErrorCode pathErrorCode = U_ZERO_ERROR;  // Ignore if parents up to root do not have this path.
1937
0
        if (bundle->fResPath == NULL || *bundle->fResPath == 0) {
1938
0
            rb = &parentBundle;
1939
0
        } else {
1940
0
            rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,
1941
0
                                           &containerBundle, &pathErrorCode);
1942
0
        }
1943
0
        if (U_SUCCESS(pathErrorCode)) {
1944
0
            getAllItemsWithFallback(rb, value, sink, errorCode);
1945
0
        }
1946
0
        ures_close(&containerBundle);
1947
0
        ures_close(&parentBundle);
1948
0
    }
1949
0
}
1950
1951
}  // namespace
1952
1953
U_CAPI void U_EXPORT2
1954
ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,
1955
0
                             icu::ResourceSink &sink, UErrorCode &errorCode) {
1956
0
    if (U_FAILURE(errorCode)) { return; }
1957
0
    if (path == NULL) {
1958
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
1959
0
        return;
1960
0
    }
1961
0
    UResourceBundle stackBundle;
1962
0
    ures_initStackObject(&stackBundle);
1963
0
    const UResourceBundle *rb;
1964
0
    if (*path == 0) {
1965
        // empty path
1966
0
        rb = bundle;
1967
0
    } else {
1968
0
        rb = ures_getByKeyWithFallback(bundle, path, &stackBundle, &errorCode);
1969
0
        if (U_FAILURE(errorCode)) {
1970
0
            ures_close(&stackBundle);
1971
0
            return;
1972
0
        }
1973
0
    }
1974
    // Get all table items with fallback.
1975
0
    ResourceDataValue value;
1976
0
    getAllItemsWithFallback(rb, value, sink, errorCode);
1977
0
    ures_close(&stackBundle);
1978
0
}
1979
1980
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
1981
0
    Resource res = RES_BOGUS;
1982
0
    UResourceDataEntry *realData = NULL;
1983
0
    const char *key = inKey;
1984
1985
0
    if (status==NULL || U_FAILURE(*status)) {
1986
0
        return fillIn;
1987
0
    }
1988
0
    if(resB == NULL) {
1989
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1990
0
        return fillIn;
1991
0
    }
1992
1993
0
    int32_t type = RES_GET_TYPE(resB->fRes);
1994
0
    if(URES_IS_TABLE(type)) {
1995
0
        int32_t t;
1996
0
        res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
1997
0
        if(res == RES_BOGUS) {
1998
0
            key = inKey;
1999
0
            if(resB->fHasFallback == TRUE) {
2000
0
                const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2001
0
                if(U_SUCCESS(*status)) {
2002
                  /* check if resB->fResPath gives the right name here */
2003
0
                    return init_resb_result(rd, res, key, -1, realData, resB, 0, fillIn, status);
2004
0
                } else {
2005
0
                    *status = U_MISSING_RESOURCE_ERROR;
2006
0
                }
2007
0
            } else {
2008
0
                *status = U_MISSING_RESOURCE_ERROR;
2009
0
            }
2010
0
        } else {
2011
0
            return init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
2012
0
        }
2013
0
    } 
2014
#if 0
2015
    /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2016
    /* not currently */
2017
    else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2018
        /* here should go a first attempt to locate the key using index table */
2019
        const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2020
        if(U_SUCCESS(*status)) {
2021
            return init_resb_result(rd, res, key, realData, resB, fillIn, status);
2022
        } else {
2023
            *status = U_MISSING_RESOURCE_ERROR;
2024
        }
2025
    }
2026
#endif    
2027
0
    else {
2028
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2029
0
    }
2030
0
    return fillIn;
2031
0
}
2032
2033
0
U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
2034
0
    Resource res = RES_BOGUS;
2035
0
    UResourceDataEntry *realData = NULL;
2036
0
    const char* key = inKey;
2037
2038
0
    if (status==NULL || U_FAILURE(*status)) {
2039
0
        return NULL;
2040
0
    }
2041
0
    if(resB == NULL) {
2042
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2043
0
        return NULL;
2044
0
    }
2045
2046
0
    int32_t type = RES_GET_TYPE(resB->fRes);
2047
0
    if(URES_IS_TABLE(type)) {
2048
0
        int32_t t=0;
2049
2050
0
        res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
2051
2052
0
        if(res == RES_BOGUS) {
2053
0
            key = inKey;
2054
0
            if(resB->fHasFallback == TRUE) {
2055
0
                const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2056
0
                if(U_SUCCESS(*status)) {
2057
0
                    switch (RES_GET_TYPE(res)) {
2058
0
                    case URES_STRING:
2059
0
                    case URES_STRING_V2:
2060
0
                        return res_getString(rd, res, len);
2061
0
                    case URES_ALIAS:
2062
0
                      {
2063
0
                        const UChar* result = 0;
2064
0
                        UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2065
0
                        result = ures_getString(tempRes, len, status);
2066
0
                        ures_close(tempRes);
2067
0
                        return result;
2068
0
                      }
2069
0
                    default:
2070
0
                        *status = U_RESOURCE_TYPE_MISMATCH;
2071
0
                    }
2072
0
                } else {
2073
0
                    *status = U_MISSING_RESOURCE_ERROR;
2074
0
                }
2075
0
            } else {
2076
0
                *status = U_MISSING_RESOURCE_ERROR;
2077
0
            }
2078
0
        } else {
2079
0
            switch (RES_GET_TYPE(res)) {
2080
0
            case URES_STRING:
2081
0
            case URES_STRING_V2:
2082
0
                return res_getString(&(resB->fResData), res, len);
2083
0
            case URES_ALIAS:
2084
0
              {
2085
0
                const UChar* result = 0;
2086
0
                UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2087
0
                result = ures_getString(tempRes, len, status);
2088
0
                ures_close(tempRes);
2089
0
                return result;
2090
0
              }
2091
0
            default:
2092
0
                *status = U_RESOURCE_TYPE_MISMATCH;
2093
0
            }
2094
0
        }
2095
0
    } 
2096
#if 0 
2097
    /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2098
    /* not currently */   
2099
    else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2100
        /* here should go a first attempt to locate the key using index table */
2101
        const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2102
        if(U_SUCCESS(*status)) {
2103
            return res_getString(rd, res, len);
2104
        } else {
2105
            *status = U_MISSING_RESOURCE_ERROR;
2106
        }
2107
    } 
2108
#endif    
2109
0
    else {
2110
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2111
0
    }
2112
0
    return NULL;
2113
0
}
2114
2115
U_CAPI const char * U_EXPORT2
2116
ures_getUTF8StringByKey(const UResourceBundle *resB,
2117
                        const char *key,
2118
                        char *dest, int32_t *pLength,
2119
                        UBool forceCopy,
2120
0
                        UErrorCode *status) {
2121
0
    int32_t length16;
2122
0
    const UChar *s16 = ures_getStringByKey(resB, key, &length16, status);
2123
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
2124
0
}
2125
2126
/* TODO: clean from here down */
2127
2128
/**
2129
 *  INTERNAL: Get the name of the first real locale (not placeholder) 
2130
 *  that has resource bundle data.
2131
 */
2132
U_INTERNAL const char*  U_EXPORT2
2133
ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
2134
0
{
2135
0
    if (status==NULL || U_FAILURE(*status)) {
2136
0
        return NULL;
2137
0
    }
2138
0
    if (!resourceBundle) {
2139
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2140
0
        return NULL;
2141
0
    } else {
2142
0
      return resourceBundle->fData->fName;
2143
0
    }
2144
0
}
2145
2146
U_CAPI const char* U_EXPORT2 
2147
ures_getLocale(const UResourceBundle* resourceBundle, 
2148
               UErrorCode* status)
2149
0
{
2150
0
  return ures_getLocaleInternal(resourceBundle, status);
2151
0
}
2152
2153
2154
U_CAPI const char* U_EXPORT2 
2155
ures_getLocaleByType(const UResourceBundle* resourceBundle, 
2156
                     ULocDataLocaleType type, 
2157
0
                     UErrorCode* status) {
2158
0
    if (status==NULL || U_FAILURE(*status)) {
2159
0
        return NULL;
2160
0
    }
2161
0
    if (!resourceBundle) {
2162
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2163
0
        return NULL;
2164
0
    } else {
2165
0
        switch(type) {
2166
0
        case ULOC_ACTUAL_LOCALE:
2167
0
            return resourceBundle->fData->fName;
2168
0
        case ULOC_VALID_LOCALE:
2169
0
            return resourceBundle->fTopLevelData->fName;
2170
0
        case ULOC_REQUESTED_LOCALE:
2171
0
        default:
2172
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2173
0
            return NULL;
2174
0
        }
2175
0
    }
2176
0
}
2177
2178
0
U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
2179
0
  if(resB == NULL) {
2180
0
    return NULL;
2181
0
  }
2182
2183
0
  return resB->fData->fName;
2184
0
}
2185
2186
#ifdef URES_DEBUG
2187
U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
2188
  if(resB == NULL) {
2189
    return NULL;
2190
  }
2191
2192
  return resB->fData->fPath;
2193
}
2194
#endif
2195
2196
static UResourceBundle*
2197
ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
2198
0
                  UResOpenType openType, UErrorCode* status) {
2199
0
    if(U_FAILURE(*status)) {
2200
0
        return NULL;
2201
0
    }
2202
2203
0
    UResourceDataEntry *entry;
2204
0
    if(openType != URES_OPEN_DIRECT) {
2205
        /* first "canonicalize" the locale ID */
2206
0
        char canonLocaleID[ULOC_FULLNAME_CAPACITY];
2207
0
        uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
2208
0
        if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
2209
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2210
0
            return NULL;
2211
0
        }
2212
0
        entry = entryOpen(path, canonLocaleID, openType, status);
2213
0
    } else {
2214
0
        entry = entryOpenDirect(path, localeID, status);
2215
0
    }
2216
0
    if(U_FAILURE(*status)) {
2217
0
        return NULL;
2218
0
    }
2219
0
    if(entry == NULL) {
2220
0
        *status = U_MISSING_RESOURCE_ERROR;
2221
0
        return NULL;
2222
0
    }
2223
2224
0
    UBool isStackObject;
2225
0
    if(r == NULL) {
2226
0
        r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
2227
0
        if(r == NULL) {
2228
0
            entryClose(entry);
2229
0
            *status = U_MEMORY_ALLOCATION_ERROR;
2230
0
            return NULL;
2231
0
        }
2232
0
        isStackObject = FALSE;
2233
0
    } else {  // fill-in
2234
0
        isStackObject = ures_isStackObject(r);
2235
0
        ures_closeBundle(r, FALSE);
2236
0
    }
2237
0
    uprv_memset(r, 0, sizeof(UResourceBundle));
2238
0
    ures_setIsStackObject(r, isStackObject);
2239
2240
0
    r->fTopLevelData = r->fData = entry;
2241
0
    uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));
2242
0
    r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;
2243
0
    r->fIsTopLevel = TRUE;
2244
0
    r->fRes = r->fResData.rootRes;
2245
0
    r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
2246
0
    r->fIndex = -1;
2247
2248
0
    return r;
2249
0
}
2250
2251
U_CAPI UResourceBundle* U_EXPORT2
2252
0
ures_open(const char* path, const char* localeID, UErrorCode* status) {
2253
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2254
0
}
2255
2256
U_CAPI UResourceBundle* U_EXPORT2
2257
0
ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
2258
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);
2259
0
}
2260
2261
/**
2262
 *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed 
2263
 *  or sought. However, alias substitution will happen!
2264
 */
2265
U_CAPI UResourceBundle*  U_EXPORT2
2266
0
ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
2267
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);
2268
0
}
2269
2270
/**
2271
 *  API: This function is used to open a resource bundle 
2272
 *  proper fallback chaining is executed while initialization. 
2273
 *  The result is stored in cache for later fallback search.
2274
 */
2275
U_CAPI void U_EXPORT2
2276
ures_openFillIn(UResourceBundle *r, const char* path,
2277
0
                const char* localeID, UErrorCode* status) {
2278
0
    if(U_SUCCESS(*status) && r == NULL) {
2279
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2280
0
        return;
2281
0
    }
2282
0
    ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2283
0
}
2284
2285
/**
2286
 *  API: Counts members. For arrays and tables, returns number of resources.
2287
 *  For strings, returns 1.
2288
 */
2289
U_CAPI int32_t  U_EXPORT2
2290
ures_countArrayItems(const UResourceBundle* resourceBundle,
2291
                  const char* resourceKey,
2292
                  UErrorCode* status)
2293
0
{
2294
0
    UResourceBundle resData;
2295
0
    ures_initStackObject(&resData);
2296
0
    if (status==NULL || U_FAILURE(*status)) {
2297
0
        return 0;
2298
0
    }
2299
0
    if(resourceBundle == NULL) {
2300
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2301
0
        return 0;
2302
0
    }
2303
0
    ures_getByKey(resourceBundle, resourceKey, &resData, status);
2304
    
2305
0
    if(resData.fResData.data != NULL) {
2306
0
        int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
2307
0
        ures_close(&resData);
2308
0
        return result;
2309
0
    } else {
2310
0
        *status = U_MISSING_RESOURCE_ERROR;
2311
0
        ures_close(&resData);
2312
0
        return 0;
2313
0
    }
2314
0
}
2315
2316
/**
2317
 * Internal function.
2318
 * Return the version number associated with this ResourceBundle as a string.
2319
 *
2320
 * @param resourceBundle The resource bundle for which the version is checked.
2321
 * @return  A version number string as specified in the resource bundle or its parent.
2322
 *          The caller does not own this string.
2323
 * @see ures_getVersion
2324
 * @internal
2325
 */
2326
U_INTERNAL const char* U_EXPORT2 
2327
ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
2328
0
{
2329
0
    if (!resourceBundle) return NULL;
2330
2331
0
    if(resourceBundle->fVersion == NULL) {
2332
2333
        /* If the version ID has not been built yet, then do so.  Retrieve */
2334
        /* the minor version from the file. */
2335
0
        UErrorCode status = U_ZERO_ERROR;
2336
0
        int32_t minor_len = 0;
2337
0
        int32_t len;
2338
2339
0
        const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
2340
        
2341
        /* Determine the length of of the final version string.  This is */
2342
        /* the length of the major part + the length of the separator */
2343
        /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2344
        /* the end). */
2345
2346
0
        len = (minor_len > 0) ? minor_len : 1;
2347
    
2348
        /* Allocate the string, and build it up. */
2349
        /* + 1 for zero byte */
2350
2351
2352
0
        ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len); 
2353
        /* Check for null pointer. */
2354
0
        if (((UResourceBundle *)resourceBundle)->fVersion == NULL) {
2355
0
            return NULL;
2356
0
        }
2357
       
2358
0
        if(minor_len > 0) {
2359
0
            u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
2360
0
            resourceBundle->fVersion[len] =  '\0';
2361
0
        }
2362
0
        else {
2363
0
            uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
2364
0
        }
2365
0
    }
2366
2367
0
    return resourceBundle->fVersion;
2368
0
}
2369
2370
U_CAPI const char*  U_EXPORT2
2371
ures_getVersionNumber(const UResourceBundle*   resourceBundle)
2372
0
{
2373
0
    return ures_getVersionNumberInternal(resourceBundle);
2374
0
}
2375
2376
0
U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) {
2377
0
    if (!resB) return;
2378
2379
0
    u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));
2380
0
}
2381
2382
/** Tree support functions *******************************/
2383
0
#define INDEX_LOCALE_NAME "res_index"
2384
0
#define INDEX_TAG         "InstalledLocales"
2385
0
#define DEFAULT_TAG       "default"
2386
2387
#if defined(URES_TREE_DEBUG)
2388
#include <stdio.h>
2389
#endif
2390
2391
typedef struct ULocalesContext {
2392
    UResourceBundle installed;
2393
    UResourceBundle curr;
2394
} ULocalesContext;
2395
2396
static void U_CALLCONV
2397
0
ures_loc_closeLocales(UEnumeration *enumerator) {
2398
0
    ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
2399
0
    ures_close(&ctx->curr);
2400
0
    ures_close(&ctx->installed);
2401
0
    uprv_free(ctx);
2402
0
    uprv_free(enumerator);
2403
0
}
2404
2405
static int32_t U_CALLCONV
2406
0
ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) {
2407
0
    ULocalesContext *ctx = (ULocalesContext *)en->context;
2408
0
    return ures_getSize(&ctx->installed);
2409
0
}
2410
2411
U_CDECL_BEGIN
2412
2413
2414
static const char * U_CALLCONV
2415
ures_loc_nextLocale(UEnumeration* en,
2416
                    int32_t* resultLength,
2417
0
                    UErrorCode* status) {
2418
0
    ULocalesContext *ctx = (ULocalesContext *)en->context;
2419
0
    UResourceBundle *res = &(ctx->installed);
2420
0
    UResourceBundle *k = NULL;
2421
0
    const char *result = NULL;
2422
0
    int32_t len = 0;
2423
0
    if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status))) {
2424
0
        result = ures_getKey(k);
2425
0
        len = (int32_t)uprv_strlen(result);
2426
0
    }
2427
0
    if (resultLength) {
2428
0
        *resultLength = len;
2429
0
    }
2430
0
    return result;
2431
0
}
2432
2433
static void U_CALLCONV 
2434
ures_loc_resetLocales(UEnumeration* en, 
2435
0
                      UErrorCode* /*status*/) {
2436
0
    UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
2437
0
    ures_resetIterator(res);
2438
0
}
2439
2440
U_CDECL_END
2441
2442
static const UEnumeration gLocalesEnum = {
2443
    NULL,
2444
        NULL,
2445
        ures_loc_closeLocales,
2446
        ures_loc_countLocales,
2447
        uenum_unextDefault,
2448
        ures_loc_nextLocale,
2449
        ures_loc_resetLocales
2450
};
2451
2452
2453
U_CAPI UEnumeration* U_EXPORT2
2454
ures_openAvailableLocales(const char *path, UErrorCode *status)
2455
0
{
2456
0
    UResourceBundle *idx = NULL;
2457
0
    UEnumeration *en = NULL;
2458
0
    ULocalesContext *myContext = NULL;
2459
2460
0
    if(U_FAILURE(*status)) {
2461
0
        return NULL;
2462
0
    }
2463
0
    myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));
2464
0
    en =  (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2465
0
    if(!en || !myContext) {
2466
0
        *status = U_MEMORY_ALLOCATION_ERROR;
2467
0
        uprv_free(en);
2468
0
        uprv_free(myContext);
2469
0
        return NULL;
2470
0
    }
2471
0
    uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
2472
2473
0
    ures_initStackObject(&myContext->installed);
2474
0
    ures_initStackObject(&myContext->curr);
2475
0
    idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);
2476
0
    ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);
2477
0
    if(U_SUCCESS(*status)) {
2478
#if defined(URES_TREE_DEBUG)
2479
        fprintf(stderr, "Got %s::%s::[%s] : %s\n", 
2480
            path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
2481
#endif
2482
0
        en->context = myContext;
2483
0
    } else {
2484
#if defined(URES_TREE_DEBUG)
2485
        fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
2486
#endif
2487
0
        ures_close(&myContext->installed);
2488
0
        uprv_free(myContext);
2489
0
        uprv_free(en);
2490
0
        en = NULL;
2491
0
    }
2492
    
2493
0
    ures_close(idx);
2494
    
2495
0
    return en;
2496
0
}
2497
2498
0
static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) {
2499
0
    const char *loc;
2500
0
    while ((loc = uenum_next(locEnum, NULL, status)) != NULL) {
2501
0
        if (uprv_strcmp(loc, locToSearch) == 0) {
2502
0
            return TRUE;
2503
0
        }
2504
0
    }
2505
0
    return FALSE;
2506
0
}
2507
2508
U_CAPI int32_t U_EXPORT2
2509
ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
2510
                             const char *path, const char *resName, const char *keyword, const char *locid,
2511
                             UBool *isAvailable, UBool omitDefault, UErrorCode *status)
2512
0
{
2513
0
    char kwVal[1024] = ""; /* value of keyword 'keyword' */
2514
0
    char defVal[1024] = ""; /* default value for given locale */
2515
0
    char defLoc[1024] = ""; /* default value for given locale */
2516
0
    char base[1024] = ""; /* base locale */
2517
0
    char found[1024];
2518
0
    char parent[1024];
2519
0
    char full[1024] = "";
2520
0
    UResourceBundle bund1, bund2;
2521
0
    UResourceBundle *res = NULL;
2522
0
    UErrorCode subStatus = U_ZERO_ERROR;
2523
0
    int32_t length = 0;
2524
0
    if(U_FAILURE(*status)) return 0;
2525
0
    uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
2526
0
    if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
2527
0
        kwVal[0]=0;
2528
0
    }
2529
0
    uloc_getBaseName(locid, base, 1024-1,&subStatus);
2530
#if defined(URES_TREE_DEBUG)
2531
    fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n", 
2532
            locid, keyword, kwVal, base, u_errorName(subStatus));
2533
#endif
2534
0
    ures_initStackObject(&bund1);
2535
0
    ures_initStackObject(&bund2);
2536
    
2537
    
2538
0
    uprv_strcpy(parent, base);
2539
0
    uprv_strcpy(found, base);
2540
2541
0
    if(isAvailable) { 
2542
0
        UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);
2543
0
        *isAvailable = TRUE;
2544
0
        if (U_SUCCESS(subStatus)) {
2545
0
            *isAvailable = isLocaleInList(locEnum, parent, &subStatus);
2546
0
        }
2547
0
        uenum_close(locEnum);
2548
0
    }
2549
2550
0
    if(U_FAILURE(subStatus)) {
2551
0
        *status = subStatus;
2552
0
        return 0;
2553
0
    }
2554
    
2555
0
    do {
2556
0
        subStatus = U_ZERO_ERROR;
2557
0
        res = ures_open(path, parent, &subStatus);
2558
0
        if(((subStatus == U_USING_FALLBACK_WARNING) ||
2559
0
            (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
2560
0
        {
2561
0
            *isAvailable = FALSE;
2562
0
        }
2563
0
        isAvailable = NULL; /* only want to set this the first time around */
2564
        
2565
#if defined(URES_TREE_DEBUG)
2566
        fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
2567
#endif
2568
0
        if(U_FAILURE(subStatus)) {
2569
0
            *status = subStatus;
2570
0
        } else if(subStatus == U_ZERO_ERROR) {
2571
0
            ures_getByKey(res,resName,&bund1, &subStatus);
2572
0
            if(subStatus == U_ZERO_ERROR) {
2573
0
                const UChar *defUstr;
2574
0
                int32_t defLen;
2575
                /* look for default item */
2576
#if defined(URES_TREE_DEBUG)
2577
                fprintf(stderr, "%s;%s : loaded default -> %s\n",
2578
                    path?path:"ICUDATA", parent, u_errorName(subStatus));
2579
#endif
2580
0
                defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2581
0
                if(U_SUCCESS(subStatus) && defLen) {
2582
0
                    u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2583
#if defined(URES_TREE_DEBUG)
2584
                    fprintf(stderr, "%s;%s -> default %s=%s,  %s\n", 
2585
                        path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
2586
#endif
2587
0
                    uprv_strcpy(defLoc, parent);
2588
0
                    if(kwVal[0]==0) {
2589
0
                        uprv_strcpy(kwVal, defVal);
2590
#if defined(URES_TREE_DEBUG)
2591
                        fprintf(stderr, "%s;%s -> kwVal =  %s\n", 
2592
                            path?path:"ICUDATA", parent, keyword, kwVal);
2593
#endif
2594
0
                    }
2595
0
                }
2596
0
            }
2597
0
        }
2598
        
2599
0
        subStatus = U_ZERO_ERROR;
2600
2601
0
        if (res != NULL) {
2602
0
            uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));
2603
0
        }
2604
2605
0
        uloc_getParent(found,parent,sizeof(parent),&subStatus);
2606
0
        ures_close(res);
2607
0
    } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));
2608
    
2609
    /* Now, see if we can find the kwVal collator.. start the search over.. */
2610
0
    uprv_strcpy(parent, base);
2611
0
    uprv_strcpy(found, base);
2612
    
2613
0
    do {
2614
0
        subStatus = U_ZERO_ERROR;
2615
0
        res = ures_open(path, parent, &subStatus);
2616
0
        if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2617
0
            *isAvailable = FALSE;
2618
0
        }
2619
0
        isAvailable = NULL; /* only want to set this the first time around */
2620
        
2621
#if defined(URES_TREE_DEBUG)
2622
        fprintf(stderr, "%s;%s -> %s (looking for %s)\n", 
2623
            path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2624
#endif
2625
0
        if(U_FAILURE(subStatus)) {
2626
0
            *status = subStatus;
2627
0
        } else if(subStatus == U_ZERO_ERROR) {
2628
0
            ures_getByKey(res,resName,&bund1, &subStatus);
2629
#if defined(URES_TREE_DEBUG)
2630
/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
2631
#endif
2632
0
            if(subStatus == U_ZERO_ERROR) {
2633
0
                ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2634
#if defined(URES_TREE_DEBUG)
2635
/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
2636
#endif
2637
0
                if(subStatus == U_ZERO_ERROR) {
2638
#if defined(URES_TREE_DEBUG)
2639
                    fprintf(stderr, "%s;%s -> full0 %s=%s,  %s\n", 
2640
                        path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
2641
#endif
2642
0
                    uprv_strcpy(full, parent);
2643
0
                    if(*full == 0) {
2644
0
                        uprv_strcpy(full, "root");
2645
0
                    }
2646
                        /* now, recalculate default kw if need be */
2647
0
                        if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2648
0
                          const UChar *defUstr;
2649
0
                          int32_t defLen;
2650
                          /* look for default item */
2651
#if defined(URES_TREE_DEBUG)
2652
                            fprintf(stderr, "%s;%s -> recalculating Default0\n", 
2653
                                    path?path:"ICUDATA", full);
2654
#endif
2655
0
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2656
0
                          if(U_SUCCESS(subStatus) && defLen) {
2657
0
                            u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2658
#if defined(URES_TREE_DEBUG)
2659
                            fprintf(stderr, "%s;%s -> default0 %s=%s,  %s\n", 
2660
                                    path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2661
#endif
2662
0
                            uprv_strcpy(defLoc, full);
2663
0
                          }
2664
0
                        } /* end of recalculate default KW */
2665
#if defined(URES_TREE_DEBUG)
2666
                        else {
2667
                          fprintf(stderr, "No trim0,  %s <= %s\n", defLoc, full);
2668
                        }
2669
#endif
2670
0
                } else {
2671
#if defined(URES_TREE_DEBUG)
2672
                    fprintf(stderr, "err=%s in %s looking for %s\n", 
2673
                        u_errorName(subStatus), parent, kwVal);
2674
#endif
2675
0
                }
2676
0
            }
2677
0
        }
2678
        
2679
0
        subStatus = U_ZERO_ERROR;
2680
        
2681
0
        uprv_strcpy(found, parent);
2682
0
        uloc_getParent(found,parent,1023,&subStatus);
2683
0
        ures_close(res);
2684
0
    } while(!full[0] && *found && U_SUCCESS(*status));
2685
    
2686
0
    if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
2687
#if defined(URES_TREE_DEBUG)
2688
        fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
2689
#endif
2690
0
        uprv_strcpy(kwVal, defVal);
2691
0
        uprv_strcpy(parent, base);
2692
0
        uprv_strcpy(found, base);
2693
        
2694
0
        do { /* search for 'default' named item */
2695
0
            subStatus = U_ZERO_ERROR;
2696
0
            res = ures_open(path, parent, &subStatus);
2697
0
            if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2698
0
                *isAvailable = FALSE;
2699
0
            }
2700
0
            isAvailable = NULL; /* only want to set this the first time around */
2701
            
2702
#if defined(URES_TREE_DEBUG)
2703
            fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
2704
                path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2705
#endif
2706
0
            if(U_FAILURE(subStatus)) {
2707
0
                *status = subStatus;
2708
0
            } else if(subStatus == U_ZERO_ERROR) {
2709
0
                ures_getByKey(res,resName,&bund1, &subStatus);
2710
0
                if(subStatus == U_ZERO_ERROR) {
2711
0
                    ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2712
0
                    if(subStatus == U_ZERO_ERROR) {
2713
#if defined(URES_TREE_DEBUG)
2714
                        fprintf(stderr, "%s;%s -> full1 %s=%s,  %s\n", path?path:"ICUDATA",
2715
                            parent, keyword, kwVal, u_errorName(subStatus));
2716
#endif
2717
0
                        uprv_strcpy(full, parent);
2718
0
                        if(*full == 0) {
2719
0
                            uprv_strcpy(full, "root");
2720
0
                        }
2721
                        
2722
                        /* now, recalculate default kw if need be */
2723
0
                        if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2724
0
                          const UChar *defUstr;
2725
0
                          int32_t defLen;
2726
                          /* look for default item */
2727
#if defined(URES_TREE_DEBUG)
2728
                            fprintf(stderr, "%s;%s -> recalculating Default1\n", 
2729
                                    path?path:"ICUDATA", full);
2730
#endif
2731
0
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2732
0
                          if(U_SUCCESS(subStatus) && defLen) {
2733
0
                            u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2734
#if defined(URES_TREE_DEBUG)
2735
                            fprintf(stderr, "%s;%s -> default %s=%s,  %s\n", 
2736
                                    path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2737
#endif
2738
0
                            uprv_strcpy(defLoc, full);
2739
0
                          }
2740
0
                        } /* end of recalculate default KW */
2741
#if defined(URES_TREE_DEBUG)
2742
                        else {
2743
                          fprintf(stderr, "No trim1,  %s <= %s\n", defLoc, full);
2744
                        }
2745
#endif
2746
0
                    }
2747
0
                }
2748
0
            }
2749
0
            subStatus = U_ZERO_ERROR;
2750
            
2751
0
            uprv_strcpy(found, parent);
2752
0
            uloc_getParent(found,parent,1023,&subStatus);
2753
0
            ures_close(res);
2754
0
        } while(!full[0] && *found && U_SUCCESS(*status));
2755
0
    }
2756
    
2757
0
    if(U_SUCCESS(*status)) {
2758
0
        if(!full[0]) {
2759
#if defined(URES_TREE_DEBUG)
2760
          fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
2761
#endif
2762
0
          *status = U_MISSING_RESOURCE_ERROR;
2763
0
        } else if(omitDefault) {
2764
#if defined(URES_TREE_DEBUG)
2765
          fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
2766
#endif        
2767
0
          if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
2768
            /* found the keyword in a *child* of where the default tag was present. */
2769
0
            if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
2770
              /* and the default is in or in an ancestor of the current locale */
2771
#if defined(URES_TREE_DEBUG)
2772
              fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
2773
#endif
2774
0
              kwVal[0]=0;
2775
0
            }
2776
0
          }
2777
0
        }
2778
0
        uprv_strcpy(found, full);
2779
0
        if(kwVal[0]) {
2780
0
            uprv_strcat(found, "@");
2781
0
            uprv_strcat(found, keyword);
2782
0
            uprv_strcat(found, "=");
2783
0
            uprv_strcat(found, kwVal);
2784
0
        } else if(!omitDefault) {
2785
0
            uprv_strcat(found, "@");
2786
0
            uprv_strcat(found, keyword);
2787
0
            uprv_strcat(found, "=");
2788
0
            uprv_strcat(found, defVal);
2789
0
        }
2790
0
    }
2791
    /* we found the default locale - no need to repeat it.*/
2792
    
2793
0
    ures_close(&bund1);
2794
0
    ures_close(&bund2);
2795
    
2796
0
    length = (int32_t)uprv_strlen(found);
2797
2798
0
    if(U_SUCCESS(*status)) {
2799
0
        int32_t copyLength = uprv_min(length, resultCapacity);
2800
0
        if(copyLength>0) {
2801
0
            uprv_strncpy(result, found, copyLength);
2802
0
        }
2803
0
        if(length == 0) {
2804
0
          *status = U_MISSING_RESOURCE_ERROR; 
2805
0
        }
2806
0
    } else {
2807
0
        length = 0;
2808
0
        result[0]=0;
2809
0
    }
2810
0
    return u_terminateChars(result, resultCapacity, length, status);
2811
0
}
2812
2813
U_CAPI UEnumeration* U_EXPORT2
2814
ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
2815
0
{
2816
0
#define VALUES_BUF_SIZE 2048
2817
0
#define VALUES_LIST_SIZE 512
2818
    
2819
0
    char       valuesBuf[VALUES_BUF_SIZE];
2820
0
    int32_t    valuesIndex = 0;
2821
0
    const char *valuesList[VALUES_LIST_SIZE];
2822
0
    int32_t    valuesCount = 0;
2823
    
2824
0
    const char *locale;
2825
0
    int32_t     locLen;
2826
    
2827
0
    UEnumeration *locs = NULL;
2828
    
2829
0
    UResourceBundle    item;
2830
0
    UResourceBundle    subItem;
2831
    
2832
0
    ures_initStackObject(&item);
2833
0
    ures_initStackObject(&subItem);
2834
0
    locs = ures_openAvailableLocales(path, status);
2835
    
2836
0
    if(U_FAILURE(*status)) {
2837
0
        ures_close(&item);
2838
0
        ures_close(&subItem);
2839
0
        return NULL;
2840
0
    }
2841
    
2842
0
    valuesBuf[0]=0;
2843
0
    valuesBuf[1]=0;
2844
    
2845
0
    while((locale = uenum_next(locs, &locLen, status))) {
2846
0
        UResourceBundle   *bund = NULL;
2847
0
        UResourceBundle   *subPtr = NULL;
2848
0
        UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
2849
0
        bund = ures_openDirect(path, locale, &subStatus);
2850
        
2851
#if defined(URES_TREE_DEBUG)
2852
        if(!bund || U_FAILURE(subStatus)) {
2853
            fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n", 
2854
                path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2855
        }
2856
#endif
2857
        
2858
0
        ures_getByKey(bund, keyword, &item, &subStatus);
2859
        
2860
0
        if(!bund || U_FAILURE(subStatus)) {
2861
#if defined(URES_TREE_DEBUG)
2862
            fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n", 
2863
                path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2864
#endif
2865
0
            ures_close(bund);
2866
0
            bund = NULL;
2867
0
            continue;
2868
0
        }
2869
        
2870
0
        while((subPtr = ures_getNextResource(&item,&subItem,&subStatus))
2871
0
            && U_SUCCESS(subStatus)) {
2872
0
            const char *k;
2873
0
            int32_t i;
2874
0
            k = ures_getKey(subPtr);
2875
2876
#if defined(URES_TREE_DEBUG)
2877
            /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2878
#endif
2879
0
            if(k == NULL || *k == 0 ||
2880
0
                    uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
2881
                // empty or "default" or unlisted type
2882
0
                continue;
2883
0
            }
2884
0
            for(i=0; i<valuesCount; i++) {
2885
0
                if(!uprv_strcmp(valuesList[i],k)) {
2886
0
                    k = NULL; /* found duplicate */
2887
0
                    break;
2888
0
                }
2889
0
            }
2890
0
            if(k != NULL) {
2891
0
                int32_t kLen = (int32_t)uprv_strlen(k);
2892
0
                if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
2893
0
                    ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
2894
0
                    *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
2895
0
                } else {
2896
0
                    uprv_strcpy(valuesBuf+valuesIndex, k);
2897
0
                    valuesList[valuesCount++] = valuesBuf+valuesIndex;
2898
0
                    valuesIndex += kLen;
2899
#if defined(URES_TREE_DEBUG)
2900
                    fprintf(stderr, "%s | %s | %s | [%s]   (UNIQUE)\n",
2901
                        path?path:"<ICUDATA>", keyword, locale, k);
2902
#endif
2903
0
                    valuesBuf[valuesIndex++] = 0; /* terminate */
2904
0
                }
2905
0
            }
2906
0
        }
2907
0
        ures_close(bund);
2908
0
    }
2909
0
    valuesBuf[valuesIndex++] = 0; /* terminate */
2910
    
2911
0
    ures_close(&item);
2912
0
    ures_close(&subItem);
2913
0
    uenum_close(locs);
2914
#if defined(URES_TREE_DEBUG)
2915
    fprintf(stderr, "%s:  size %d, #%d\n", u_errorName(*status), 
2916
        valuesIndex, valuesCount);
2917
#endif
2918
0
    return uloc_openKeywordList(valuesBuf, valuesIndex, status);
2919
0
}
2920
#if 0
2921
/* This code isn't needed, and given the documentation warnings the implementation is suspect */
2922
U_INTERNAL UBool U_EXPORT2
2923
ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
2924
    if(res1==NULL || res2==NULL){
2925
        return res1==res2; /* pointer comparision */
2926
    }
2927
    if(res1->fKey==NULL||  res2->fKey==NULL){
2928
        return (res1->fKey==res2->fKey);
2929
    }else{
2930
        if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
2931
            return FALSE;
2932
        }
2933
    }
2934
    if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
2935
        return FALSE;
2936
    }
2937
    if(res1->fData->fPath == NULL||  res2->fData->fPath==NULL){
2938
        return (res1->fData->fPath == res2->fData->fPath);
2939
    }else{
2940
        if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
2941
            return FALSE;
2942
        }
2943
    }
2944
    if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
2945
        return FALSE;
2946
    }
2947
    if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
2948
        return FALSE;
2949
    }
2950
    if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
2951
        return FALSE;
2952
    }
2953
    if(res1->fRes != res2->fRes){
2954
        return FALSE;
2955
    }
2956
    return TRUE;
2957
}
2958
U_INTERNAL UResourceBundle* U_EXPORT2
2959
ures_clone(const UResourceBundle* res, UErrorCode* status){
2960
    UResourceBundle* bundle = NULL;
2961
    UResourceBundle* ret = NULL;
2962
    if(U_FAILURE(*status) || res == NULL){
2963
        return NULL;
2964
    }
2965
    bundle = ures_open(res->fData->fPath, res->fData->fName, status);
2966
    if(res->fResPath!=NULL){
2967
        ret = ures_findSubResource(bundle, res->fResPath, NULL, status);
2968
        ures_close(bundle);
2969
    }else{
2970
        ret = bundle;
2971
    }
2972
    return ret;
2973
}
2974
U_INTERNAL const UResourceBundle* U_EXPORT2
2975
ures_getParentBundle(const UResourceBundle* res){
2976
    if(res==NULL){
2977
        return NULL;
2978
    }
2979
    return res->fParentRes;
2980
}
2981
#endif
2982
2983
U_INTERNAL void U_EXPORT2
2984
0
ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) {
2985
0
  const UChar *str;
2986
0
  int32_t len;
2987
0
  str = ures_getStringByKey(res, key, &len, status);
2988
0
  if(U_SUCCESS(*status)) {
2989
0
    u_versionFromUString(ver, str);
2990
0
  } 
2991
0
}
2992
2993
/* eof */