Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/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
18
static int32_t U_CALLCONV hashEntry(const UHashTok parm) {
55
18
    UResourceDataEntry *b = (UResourceDataEntry *)parm.pointer;
56
18
    UHashTok namekey, pathkey;
57
18
    namekey.pointer = b->fName;
58
18
    pathkey.pointer = b->fPath;
59
18
    return uhash_hashChars(namekey)+37u*uhash_hashChars(pathkey);
60
18
}
61
62
/* INTERNAL: compares two entries */
63
9
static UBool U_CALLCONV compareEntries(const UHashTok p1, const UHashTok p2) {
64
9
    UResourceDataEntry *b1 = (UResourceDataEntry *)p1.pointer;
65
9
    UResourceDataEntry *b2 = (UResourceDataEntry *)p2.pointer;
66
9
    UHashTok name1, name2, path1, path2;
67
9
    name1.pointer = b1->fName;
68
9
    name2.pointer = b2->fName;
69
9
    path1.pointer = b1->fPath;
70
9
    path2.pointer = b2->fPath;
71
9
    return (UBool)(uhash_compareChars(name1, name2) &&
72
9
        uhash_compareChars(path1, path2));
73
9
}
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
0
83
0
    if(i != NULL) {
84
0
        *i = '\0';
85
0
        return TRUE;
86
0
    }
87
0
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
0
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
0
180
0
    /*if shared data hasn't even been lazy evaluated yet
181
0
    * return 0
182
0
    */
183
0
    umtx_lock(&resbMutex);
184
0
    if (cache == NULL) {
185
0
        umtx_unlock(&resbMutex);
186
0
        return 0;
187
0
    }
188
0
189
0
    do {
190
0
        deletedMore = FALSE;
191
0
        /*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
0
            /* Deletes only if reference counter == 0
197
0
             * Don't worry about the children of this node.
198
0
             * Those will eventually get deleted too, if not already.
199
0
             * Don't worry about the parents of this node.
200
0
             * Those will eventually get deleted too, if not already.
201
0
             */
202
0
            /* 04/05/2002 [weiv] fCountExisting should now be accurate. If it's not zero, that means that    */
203
0
            /* some resource bundles are still open somewhere. */
204
0
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
0
        /*
213
0
         * Do it again to catch bundles (aliases, pool bundle) whose fCountExisting
214
0
         * got decremented by free_entry().
215
0
         */
216
0
    } while(deletedMore);
217
0
    umtx_unlock(&resbMutex);
218
0
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
3
static void U_CALLCONV createCache(UErrorCode &status) {
273
3
    U_ASSERT(cache == NULL);
274
3
    cache = uhash_open(hashEntry, compareEntries, NULL, &status);
275
3
    ucln_common_registerCleanup(UCLN_COMMON_URES, ures_cleanup);
276
3
}
277
     
278
12
static void initCache(UErrorCode *status) {
279
12
    umtx_initOnce(gCacheInitOnce, &createCache, *status);
280
12
}
281
282
/** INTERNAL: sets the name (locale) of the resource bundle to given name */
283
284
3
static void setEntryName(UResourceDataEntry *res, const char *name, UErrorCode *status) {
285
3
    int32_t len = (int32_t)uprv_strlen(name);
286
3
    if(res->fName != NULL && res->fName != res->fNameBuffer) {
287
0
        uprv_free(res->fName);
288
0
    }
289
3
    if (len < (int32_t)sizeof(res->fNameBuffer)) {
290
0
        res->fName = res->fNameBuffer;
291
0
    }
292
3
    else {
293
3
        res->fName = (char *)uprv_malloc(len+1);
294
3
    }
295
3
    if(res->fName == NULL) {
296
0
        *status = U_MEMORY_ALLOCATION_ERROR;
297
3
    } else {
298
3
        uprv_strcpy(res->fName, name);
299
3
    }
300
3
}
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
12
static UResourceDataEntry *init_entry(const char *localeID, const char *path, UErrorCode *status) {
310
12
    UResourceDataEntry *r = NULL;
311
12
    UResourceDataEntry find;
312
12
    /*int32_t hashValue;*/
313
12
    const char *name;
314
12
    char aliasName[100] = { 0 };
315
12
    int32_t aliasLen = 0;
316
12
    /*UBool isAlias = FALSE;*/
317
12
    /*UHashTok hashkey; */
318
12
319
12
    if(U_FAILURE(*status)) {
320
0
        return NULL;
321
0
    }
322
12
323
12
    /* here we try to deduce the right locale name */
324
12
    if(localeID == NULL) { /* if localeID is NULL, we're trying to open default locale */
325
0
        name = uloc_getDefault();
326
12
    } else if(*localeID == 0) { /* if localeID is "" then we try to open root locale */
327
0
        name = kRootLocaleName;
328
12
    } else { /* otherwise, we'll open what we're given */
329
12
        name = localeID;
330
12
    }
331
12
332
12
    find.fName = (char *)name;
333
12
    find.fPath = (char *)path;
334
12
335
12
    /* calculate the hash value of the entry */
336
12
    /*hashkey.pointer = (void *)&find;*/
337
12
    /*hashValue = hashEntry(hashkey);*/
338
12
339
12
    /* check to see if we already have this entry */
340
12
    r = (UResourceDataEntry *)uhash_get(cache, &find);
341
12
    if(r == NULL) {
342
3
        /* if the entry is not yet in the hash table, we'll try to construct a new one */
343
3
        r = (UResourceDataEntry *) uprv_malloc(sizeof(UResourceDataEntry));
344
3
        if(r == NULL) {
345
0
            *status = U_MEMORY_ALLOCATION_ERROR;
346
0
            return NULL;
347
0
        }
348
3
349
3
        uprv_memset(r, 0, sizeof(UResourceDataEntry));
350
3
        /*r->fHashKey = hashValue;*/
351
3
352
3
        setEntryName(r, name, status);
353
3
        if (U_FAILURE(*status)) {
354
0
            uprv_free(r);
355
0
            return NULL;
356
0
        }
357
3
358
3
        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
3
        }
366
3
367
3
        /* this is the actual loading */
368
3
        res_load(&(r->fData), r->fPath, r->fName, status);
369
3
370
3
        if (U_FAILURE(*status)) { 
371
0
            /* 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
3
        } else { /* if we have a regular entry */
375
3
            Resource aliasres;
376
3
            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
3
            if (U_SUCCESS(*status)) {
391
3
                /* handle the alias by trying to get out the %%Alias tag.*/
392
3
                /* We'll try to get alias string from the bundle */
393
3
                aliasres = res_getResource(&(r->fData), "%%ALIAS");
394
3
                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
3
            }
402
3
        }
403
3
404
3
        {
405
3
            UResourceDataEntry *oldR = NULL;
406
3
            if((oldR = (UResourceDataEntry *)uhash_get(cache, r)) == NULL) { /* if the data is not cached */
407
3
                /* just insert it in the cache */
408
3
                UErrorCode cacheStatus = U_ZERO_ERROR;
409
3
                uhash_put(cache, (void *)r, r, &cacheStatus);
410
3
                if (U_FAILURE(cacheStatus)) {
411
0
                    *status = cacheStatus;
412
0
                    free_entry(r);
413
0
                    r = NULL;
414
0
                }
415
3
            } else {
416
0
                /* somebody have already inserted it while we were working, discard newly opened data */
417
0
                /* Also, we could get here IF we opened an alias */
418
0
                free_entry(r);
419
0
                r = oldR;
420
0
            }
421
3
        }
422
3
423
3
    }
424
12
    if(r != NULL) {
425
12
        /* return the real bundle */
426
12
        while(r->fAlias != NULL) {
427
0
            r = r->fAlias;
428
0
        }
429
12
        r->fCountExisting++; /* we increase its reference count */
430
12
        /* if the resource has a warning */
431
12
        /* we don't want to overwrite a status with no error */
432
12
        if(r->fBogus != U_ZERO_ERROR && U_SUCCESS(*status)) {
433
0
             *status = r->fBogus; /* set the returning status */
434
0
        }
435
12
    }
436
12
    return r;
437
12
}
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
0
460
0
    while(*hasChopped && !hasRealData) {
461
0
        r = init_entry(name, path, status);
462
0
        /* 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
0
            /* this entry is not real. We will discard it. */
470
0
            /* However, the parent line for this entry is  */
471
0
            /* not to be used - as there might be parent   */
472
0
            /* lines in cache from previous openings that  */
473
0
            /* are not updated yet. */
474
0
            r->fCountExisting--;
475
0
            /*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
0
482
0
        *isRoot = (UBool)(uprv_strcmp(name, kRootLocaleName) == 0);
483
0
484
0
        /*Fallback data stuff*/
485
0
        *hasChopped = chopLocale(name);
486
0
    }
487
0
    return r;
488
0
}
489
490
12
static void ures_setIsStackObject( UResourceBundle* resB, UBool state) {
491
12
    if(state) {
492
0
        resB->fMagic1 = 0;
493
0
        resB->fMagic2 = 0;
494
12
    } else {
495
12
        resB->fMagic1 = MAGIC1;
496
12
        resB->fMagic2 = MAGIC2;
497
12
    }
498
12
}
499
500
12
static UBool ures_isStackObject(const UResourceBundle* resB) {
501
12
  return((resB->fMagic1 == MAGIC1 && resB->fMagic2 == MAGIC2)?FALSE:TRUE);
502
12
}
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
0
        // 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
0
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
0
                // 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
0
616
0
    char name[ULOC_FULLNAME_CAPACITY];
617
0
    char usrDataPath[96];
618
0
619
0
    initCache(status);
620
0
621
0
    if(U_FAILURE(*status)) {
622
0
        return NULL;
623
0
    }
624
0
625
0
    uprv_strncpy(name, localeID, sizeof(name) - 1);
626
0
    name[sizeof(name) - 1] = 0;
627
0
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
0
 
640
0
    umtx_lock(&resbMutex);
641
0
    { /* umtx_lock */
642
0
        /* 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
0
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
0
                   /* 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
0
668
0
        /* we could have reached this point without having any real data */
669
0
        /* 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
0
            /* 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
0
                // 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
0
688
0
        /* we could still have r == NULL at this point - maybe even default locale is not */
689
0
        /* 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
0
711
0
        // 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
0
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
12
entryOpenDirect(const char* path, const char* localeID, UErrorCode* status) {
738
12
    initCache(status);
739
12
    if(U_FAILURE(*status)) {
740
0
        return NULL;
741
0
    }
742
12
743
12
    umtx_lock(&resbMutex);
744
12
    // findFirstExisting() without fallbacks.
745
12
    UResourceDataEntry *r = init_entry(localeID, path, status);
746
12
    if(U_SUCCESS(*status)) {
747
12
        if(r->fBogus != U_ZERO_ERROR) {
748
0
            r->fCountExisting--;
749
0
            r = NULL;
750
0
        }
751
12
    } else {
752
0
        r = NULL;
753
0
    }
754
12
755
12
    // Some code depends on the ures_openDirect() bundle to have a parent bundle chain,
756
12
    // unless it is marked with "nofallback".
757
12
    UResourceDataEntry *t1 = r;
758
12
    if(r != NULL && uprv_strcmp(localeID, kRootLocaleName) != 0 &&  // not root
759
12
            r->fParent == NULL && !r->fData.noFallback &&
760
12
            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
12
774
12
    if(r != NULL) {
775
12
        // TODO: Does this ever loop?
776
12
        while(t1->fParent != NULL) {
777
0
            t1->fParent->fCountExisting++;
778
0
            t1 = t1->fParent;
779
0
        }
780
12
    }
781
12
    umtx_unlock(&resbMutex);
782
12
    return r;
783
12
}
784
785
/**
786
 * Functions to create and destroy resource bundles.
787
 *     CAUTION:  resbMutex must be locked when calling this function.
788
 */
789
/* INTERNAL: */
790
12
static void entryCloseInt(UResourceDataEntry *resB) {
791
12
    UResourceDataEntry *p = resB;
792
12
793
24
    while(resB != NULL) {
794
12
        p = resB->fParent;
795
12
        resB->fCountExisting--;
796
12
797
12
        /* Entries are left in the cache. TODO: add ures_flushCache() to force a flush
798
12
         of the cache. */
799
12
/*
800
12
        if(resB->fCountExisting <= 0) {
801
12
            uhash_remove(cache, resB);
802
12
            if(resB->fBogus == U_ZERO_ERROR) {
803
12
                res_unload(&(resB->fData));
804
12
            }
805
12
            if(resB->fName != NULL) {
806
12
                uprv_free(resB->fName);
807
12
            }
808
12
            if(resB->fPath != NULL) {
809
12
                uprv_free(resB->fPath);
810
12
            }
811
12
            uprv_free(resB);
812
12
        }
813
12
*/
814
12
815
12
        resB = p;
816
12
    }
817
12
}
818
819
/** 
820
 *  API: closes a resource bundle and cleans up.
821
 */
822
823
12
static void entryClose(UResourceDataEntry *resB) {
824
12
  umtx_lock(&resbMutex);
825
12
  entryCloseInt(resB);
826
12
  umtx_unlock(&resbMutex);
827
12
}
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
0
            /* 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
0
            /* 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
12
static void ures_freeResPath(UResourceBundle *resB) {
877
12
    if (resB->fResPath && resB->fResPath != resB->fResBuf) {
878
0
        uprv_free(resB->fResPath);
879
0
    }
880
12
    resB->fResPath = NULL;
881
12
    resB->fResPathLen = 0;
882
12
}
883
884
static void
885
ures_closeBundle(UResourceBundle* resB, UBool freeBundleObj)
886
12
{
887
12
    if(resB != NULL) {
888
12
        if(resB->fData != NULL) {
889
12
            entryClose(resB->fData);
890
12
        }
891
12
        if(resB->fVersion != NULL) {
892
0
            uprv_free(resB->fVersion);
893
0
        }
894
12
        ures_freeResPath(resB);
895
12
896
12
        if(ures_isStackObject(resB) == FALSE && freeBundleObj) {
897
12
            uprv_free(resB);
898
12
        }
899
#if 0 /*U_DEBUG*/
900
        else {
901
            /* poison the data */
902
            uprv_memset(resB, -1, sizeof(UResourceBundle));
903
        }
904
#endif
905
    }
906
12
}
907
908
U_CAPI void  U_EXPORT2
909
ures_close(UResourceBundle* resB)
910
12
{
911
12
    ures_closeBundle(resB, TRUE);
912
12
}
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
0
                /* 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
0
936
0
                /*
937
0
                * Allocate enough space for both the char * version
938
0
                * of the alias and parent->fResPath.
939
0
                *
940
0
                * We do this so that res_findResource() can modify the path,
941
0
                * which allows us to remove redundant _res_findResource() variants
942
0
                * in uresdata.c.
943
0
                * res_findResource() now NUL-terminates each segment so that table keys
944
0
                * can always be compared with strcmp() instead of strncmp().
945
0
                * Saves code there and simplifies testing and code coverage.
946
0
                *
947
0
                * markus 2003oct17
948
0
                */
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
0
                    /* 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
0
971
0
                if(*chAlias == RES_PATH_SEPARATOR) {
972
0
                    /* 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
0
                        /* this is an XPath alias, starting with "/LOCALE/" */
983
0
                        /* it contains the path to a resource which should be looked up */
984
0
                        /* 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
0
                    /* 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
0
1009
0
1010
0
                {
1011
0
                    /* got almost everything, let's try to open */
1012
0
                    /* 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
0
                            /* no key path. This means that we are going to 
1020
0
                            * to use the corresponding resource from
1021
0
                            * another bundle
1022
0
                            */
1023
0
                            /* first, we are going to get a corresponding parent 
1024
0
                            * resource to the one we are searching.
1025
0
                            */
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
0
                                /* we need to make keyPath from parent's fResPath and
1036
0
                                * current key, if there is a key associated
1037
0
                                */
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
0
                                /* if there is no key, but there is an index, try to get by the index */
1057
0
                                /* 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
0
                            /* this one is a bit trickier. 
1073
0
                            * we start finding keys, but after we resolve one alias, the path might continue.
1074
0
                            * Consider: 
1075
0
                            *     aliastest:alias { "testtypes/anotheralias/Sequence" }
1076
0
                            *     anotheralias:alias { "/ICUDATA/sh/CollationElements" }
1077
0
                            * aliastest resource should finally have the sequence, not collation elements.
1078
0
                            */
1079
0
                            UResourceDataEntry *dataEntry = mainRes->fData;
1080
0
                            char stackPath[URES_MAX_BUFFER_SIZE];
1081
0
                            char *pathBuf = stackPath, *myPath = pathBuf;
1082
0
                            if(uprv_strlen(keyPath) > URES_MAX_BUFFER_SIZE) {
1083
0
                                pathBuf = (char *)uprv_malloc((uprv_strlen(keyPath)+1)*sizeof(char));
1084
0
                                if(pathBuf == NULL) {
1085
0
                                    *status = U_MEMORY_ALLOCATION_ERROR;
1086
0
                                    ures_close(mainRes);
1087
0
                                    return NULL;
1088
0
                                }
1089
0
                            }
1090
0
                            uprv_strcpy(pathBuf, keyPath);
1091
0
                            result = mainRes;
1092
0
                            /* now we have fallback following here */
1093
0
                            do {
1094
0
                                r = dataEntry->fData.rootRes;     
1095
0
                                /* this loop handles 'found' resources over several levels */
1096
0
                                while(*myPath && U_SUCCESS(*status)) {
1097
0
                                    r = res_findResource(&(dataEntry->fData), r, &myPath, &temp);
1098
0
                                    if(r != RES_BOGUS) { /* found a resource, but it might be an indirection */
1099
0
                                        resB = init_resb_result(&(dataEntry->fData), r, temp, -1, dataEntry, result, noAlias+1, resB, status);
1100
0
                                        result = resB;
1101
0
                                        if(result) {
1102
0
                                            r = result->fRes; /* switch to a new resource, possibly a new tree */
1103
0
                                            dataEntry = result->fData;
1104
0
                                        }
1105
0
                                    } else { /* no resource found, we don't really want to look anymore on this level */
1106
0
                                        break;
1107
0
                                    }
1108
0
                                }
1109
0
                                dataEntry = dataEntry->fParent;
1110
0
                                uprv_strcpy(pathBuf, keyPath);
1111
0
                                myPath = pathBuf;
1112
0
                            } while(r == RES_BOGUS && dataEntry != NULL);
1113
0
                            if(r == RES_BOGUS) {
1114
0
                                *status = U_MISSING_RESOURCE_ERROR;
1115
0
                                result = resB;
1116
0
                            }
1117
0
                            if(pathBuf != stackPath) {
1118
0
                                uprv_free(pathBuf);
1119
0
                            }
1120
0
                        }
1121
0
                    } else { /* we failed to open the resource we're aliasing to */
1122
0
                        *status = intStatus;
1123
0
                    }
1124
0
                    if(chAlias != stackAlias) {
1125
0
                        uprv_free(chAlias);
1126
0
                    }
1127
0
                    if(mainRes != result) {
1128
0
                        ures_close(mainRes);
1129
0
                    }
1130
0
                    return result;
1131
0
                }
1132
0
            } else {
1133
0
                /* bad alias, should be an error */ 
1134
0
                *status = U_ILLEGAL_ARGUMENT_ERROR;
1135
0
                return resB;
1136
0
            }
1137
0
        } else {
1138
0
            *status = U_TOO_MANY_ALIASES_ERROR;
1139
0
            return resB;
1140
0
        }
1141
0
    }
1142
0
    if(resB == NULL) {
1143
0
        resB = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1144
0
        /* test for NULL */
1145
0
        if (resB == NULL) {
1146
0
            *status = U_MEMORY_ALLOCATION_ERROR;
1147
0
            return NULL;
1148
0
        }
1149
0
        ures_setIsStackObject(resB, FALSE);
1150
0
        resB->fResPath = NULL;
1151
0
        resB->fResPathLen = 0;
1152
0
    } else {
1153
0
        if(resB->fData != NULL) {
1154
0
            entryClose(resB->fData);
1155
0
        }
1156
0
        if(resB->fVersion != NULL) {
1157
0
            uprv_free(resB->fVersion);
1158
0
        }
1159
0
        /* 
1160
0
        weiv: if stack object was passed in, it doesn't really need to be reinited,
1161
0
        since the purpose of initing is to remove stack junk. However, at this point 
1162
0
        we would not do anything to an allocated object, so stack object should be
1163
0
        treated the same
1164
0
        */
1165
0
        /*
1166
0
        if(ures_isStackObject(resB) != FALSE) {
1167
0
        ures_initStackObject(resB);
1168
0
        }
1169
0
        */
1170
0
        if(parent != resB) {
1171
0
            ures_freeResPath(resB);
1172
0
        }
1173
0
    }
1174
0
    resB->fData = realData;
1175
0
    entryIncrease(resB->fData);
1176
0
    resB->fHasFallback = FALSE;
1177
0
    resB->fIsTopLevel = FALSE;
1178
0
    resB->fIndex = -1;
1179
0
    resB->fKey = key; 
1180
0
    /*resB->fParentRes = parent;*/
1181
0
    resB->fTopLevelData = parent->fTopLevelData;
1182
0
    if(parent->fResPath && parent != resB) {
1183
0
        ures_appendResPath(resB, parent->fResPath, parent->fResPathLen, status);
1184
0
    }
1185
0
    if(key != NULL) {
1186
0
        ures_appendResPath(resB, key, (int32_t)uprv_strlen(key), status);
1187
0
        if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1188
0
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1189
0
        }
1190
0
    } else if(idx >= 0) {
1191
0
        char buf[256];
1192
0
        int32_t len = T_CString_integerToString(buf, idx, 10);
1193
0
        ures_appendResPath(resB, buf, len, status);
1194
0
        if(resB->fResPath[resB->fResPathLen-1] != RES_PATH_SEPARATOR) {
1195
0
            ures_appendResPath(resB, RES_PATH_SEPARATOR_S, 1, status);
1196
0
        }
1197
0
    }
1198
0
    /* Make sure that Purify doesn't complain about uninitialized memory copies. */
1199
0
    {
1200
0
        int32_t usedLen = ((resB->fResBuf == resB->fResPath) ? resB->fResPathLen : 0);
1201
0
        uprv_memset(resB->fResBuf + usedLen, 0, sizeof(resB->fResBuf) - usedLen);
1202
0
    }
1203
0
1204
0
    resB->fVersion = NULL;
1205
0
    resB->fRes = r;
1206
0
    /*resB->fParent = parent->fRes;*/
1207
0
    uprv_memmove(&resB->fResData, rdata, sizeof(ResourceData));
1208
0
    resB->fSize = res_countArrayItems(&(resB->fResData), resB->fRes);
1209
0
    return resB;
1210
0
}
1211
1212
0
UResourceBundle *ures_copyResb(UResourceBundle *r, const UResourceBundle *original, UErrorCode *status) {
1213
0
    UBool isStackObject;
1214
0
    if(U_FAILURE(*status) || r == original) {
1215
0
        return r;
1216
0
    }
1217
0
    if(original != NULL) {
1218
0
        if(r == NULL) {
1219
0
            isStackObject = FALSE;
1220
0
            r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
1221
0
            /* test for NULL */
1222
0
            if (r == NULL) {
1223
0
                *status = U_MEMORY_ALLOCATION_ERROR;
1224
0
                return NULL;
1225
0
            }
1226
0
        } else {
1227
0
            isStackObject = ures_isStackObject(r);
1228
0
            ures_closeBundle(r, FALSE);
1229
0
        }
1230
0
        uprv_memcpy(r, original, sizeof(UResourceBundle));
1231
0
        r->fResPath = NULL;
1232
0
        r->fResPathLen = 0;
1233
0
        if(original->fResPath) {
1234
0
            ures_appendResPath(r, original->fResPath, original->fResPathLen, status);
1235
0
        }
1236
0
        ures_setIsStackObject(r, isStackObject);
1237
0
        if(r->fData != NULL) {
1238
0
            entryIncrease(r->fData);
1239
0
        }
1240
0
    }
1241
0
    return r;
1242
0
}
1243
1244
/**
1245
 * Functions to retrieve data from resource bundles.
1246
 */
1247
1248
0
U_CAPI const UChar* U_EXPORT2 ures_getString(const UResourceBundle* resB, int32_t* len, UErrorCode* status) {
1249
0
    const UChar *s;
1250
0
    if (status==NULL || U_FAILURE(*status)) {
1251
0
        return NULL;
1252
0
    }
1253
0
    if(resB == NULL) {
1254
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1255
0
        return NULL;
1256
0
    }
1257
0
    s = res_getString(&(resB->fResData), resB->fRes, len);
1258
0
    if (s == NULL) {
1259
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1260
0
    }
1261
0
    return s;
1262
0
}
1263
1264
static const char *
1265
ures_toUTF8String(const UChar *s16, int32_t length16,
1266
                  char *dest, int32_t *pLength,
1267
                  UBool forceCopy,
1268
0
                  UErrorCode *status) {
1269
0
    int32_t capacity;
1270
0
1271
0
    if (U_FAILURE(*status)) {
1272
0
        return NULL;
1273
0
    }
1274
0
    if (pLength != NULL) {
1275
0
        capacity = *pLength;
1276
0
    } else {
1277
0
        capacity = 0;
1278
0
    }
1279
0
    if (capacity < 0 || (capacity > 0 && dest == NULL)) {
1280
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1281
0
        return NULL;
1282
0
    }
1283
0
1284
0
    if (length16 == 0) {
1285
0
        /* empty string, return as read-only pointer */
1286
0
        if (pLength != NULL) {
1287
0
            *pLength = 0;
1288
0
        }
1289
0
        if (forceCopy) {
1290
0
            u_terminateChars(dest, capacity, 0, status);
1291
0
            return dest;
1292
0
        } else {
1293
0
            return "";
1294
0
        }
1295
0
    } else {
1296
0
        /* We need to transform the string to the destination buffer. */
1297
0
        if (capacity < length16) {
1298
0
            /* No chance for the string to fit. Pure preflighting. */
1299
0
            return u_strToUTF8(NULL, 0, pLength, s16, length16, status);
1300
0
        }
1301
0
        if (!forceCopy && (length16 <= 0x2aaaaaaa)) {
1302
0
            /*
1303
0
             * We know the string will fit into dest because each UChar turns
1304
0
             * into at most three UTF-8 bytes. Fill the latter part of dest
1305
0
             * so that callers do not expect to use dest as a string pointer,
1306
0
             * hopefully leading to more robust code for when resource bundles
1307
0
             * may store UTF-8 natively.
1308
0
             * (In which case dest would not be used at all.)
1309
0
             *
1310
0
             * We do not do this if forceCopy=TRUE because then the caller
1311
0
             * expects the string to start exactly at dest.
1312
0
             *
1313
0
             * The test above for <= 0x2aaaaaaa prevents overflows.
1314
0
             * The +1 is for the NUL terminator.
1315
0
             */
1316
0
            int32_t maxLength = 3 * length16 + 1;
1317
0
            if (capacity > maxLength) {
1318
0
                dest += capacity - maxLength;
1319
0
                capacity = maxLength;
1320
0
            }
1321
0
        }
1322
0
        return u_strToUTF8(dest, capacity, pLength, s16, length16, status);
1323
0
    }
1324
0
}
1325
1326
U_CAPI const char * U_EXPORT2
1327
ures_getUTF8String(const UResourceBundle *resB,
1328
                   char *dest, int32_t *pLength,
1329
                   UBool forceCopy,
1330
0
                   UErrorCode *status) {
1331
0
    int32_t length16;
1332
0
    const UChar *s16 = ures_getString(resB, &length16, status);
1333
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1334
0
}
1335
1336
U_CAPI const uint8_t* U_EXPORT2 ures_getBinary(const UResourceBundle* resB, int32_t* len, 
1337
0
                                               UErrorCode*               status) {
1338
0
  const uint8_t *p;
1339
0
  if (status==NULL || U_FAILURE(*status)) {
1340
0
    return NULL;
1341
0
  }
1342
0
  if(resB == NULL) {
1343
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1344
0
    return NULL;
1345
0
  }
1346
0
  p = res_getBinary(&(resB->fResData), resB->fRes, len);
1347
0
  if (p == NULL) {
1348
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1349
0
  }
1350
0
  return p;
1351
0
}
1352
1353
U_CAPI const int32_t* U_EXPORT2 ures_getIntVector(const UResourceBundle* resB, int32_t* len, 
1354
0
                                                   UErrorCode*               status) {
1355
0
  const int32_t *p;
1356
0
  if (status==NULL || U_FAILURE(*status)) {
1357
0
    return NULL;
1358
0
  }
1359
0
  if(resB == NULL) {
1360
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1361
0
    return NULL;
1362
0
  }
1363
0
  p = res_getIntVector(&(resB->fResData), resB->fRes, len);
1364
0
  if (p == NULL) {
1365
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1366
0
  }
1367
0
  return p;
1368
0
}
1369
1370
/* this function returns a signed integer */ 
1371
/* it performs sign extension */
1372
0
U_CAPI int32_t U_EXPORT2 ures_getInt(const UResourceBundle* resB, UErrorCode *status) {
1373
0
  if (status==NULL || U_FAILURE(*status)) {
1374
0
    return 0xffffffff;
1375
0
  }
1376
0
  if(resB == NULL) {
1377
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1378
0
    return 0xffffffff;
1379
0
  }
1380
0
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1381
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1382
0
    return 0xffffffff;
1383
0
  }
1384
0
  return RES_GET_INT(resB->fRes);
1385
0
}
1386
1387
0
U_CAPI uint32_t U_EXPORT2 ures_getUInt(const UResourceBundle* resB, UErrorCode *status) {
1388
0
  if (status==NULL || U_FAILURE(*status)) {
1389
0
    return 0xffffffff;
1390
0
  }
1391
0
  if(resB == NULL) {
1392
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1393
0
    return 0xffffffff;
1394
0
  }
1395
0
  if(RES_GET_TYPE(resB->fRes) != URES_INT) {
1396
0
    *status = U_RESOURCE_TYPE_MISMATCH;
1397
0
    return 0xffffffff;
1398
0
  }
1399
0
  return RES_GET_UINT(resB->fRes);
1400
0
}
1401
1402
0
U_CAPI UResType U_EXPORT2 ures_getType(const UResourceBundle *resB) {
1403
0
  if(resB == NULL) {
1404
0
    return URES_NONE;
1405
0
  }
1406
0
  return res_getPublicType(resB->fRes);
1407
0
}
1408
1409
0
U_CAPI const char * U_EXPORT2 ures_getKey(const UResourceBundle *resB) {
1410
0
  if(resB == NULL) {
1411
0
    return NULL;
1412
0
  }
1413
0
  
1414
0
  return(resB->fKey);
1415
0
}
1416
1417
0
U_CAPI int32_t U_EXPORT2 ures_getSize(const UResourceBundle *resB) {
1418
0
  if(resB == NULL) {
1419
0
    return 0;
1420
0
  }
1421
0
  
1422
0
  return resB->fSize;
1423
0
}
1424
1425
0
static const UChar* ures_getStringWithAlias(const UResourceBundle *resB, Resource r, int32_t sIndex, int32_t *len, UErrorCode *status) {
1426
0
  if(RES_GET_TYPE(r) == URES_ALIAS) {
1427
0
    const UChar* result = 0;
1428
0
    UResourceBundle *tempRes = ures_getByIndex(resB, sIndex, NULL, status);
1429
0
    result = ures_getString(tempRes, len, status);
1430
0
    ures_close(tempRes);
1431
0
    return result;
1432
0
  } else {
1433
0
    return res_getString(&(resB->fResData), r, len); 
1434
0
  }
1435
0
}
1436
1437
0
U_CAPI void U_EXPORT2 ures_resetIterator(UResourceBundle *resB){
1438
0
  if(resB == NULL) {
1439
0
    return;
1440
0
  }
1441
0
  resB->fIndex = -1;
1442
0
}
1443
1444
0
U_CAPI UBool U_EXPORT2 ures_hasNext(const UResourceBundle *resB) {
1445
0
  if(resB == NULL) {
1446
0
    return FALSE;
1447
0
  }
1448
0
  return (UBool)(resB->fIndex < resB->fSize-1);
1449
0
}
1450
1451
0
U_CAPI const UChar* U_EXPORT2 ures_getNextString(UResourceBundle *resB, int32_t* len, const char ** key, UErrorCode *status) {
1452
0
  Resource r = RES_BOGUS;
1453
0
  
1454
0
  if (status==NULL || U_FAILURE(*status)) {
1455
0
    return NULL;
1456
0
  }
1457
0
  if(resB == NULL) {
1458
0
    *status = U_ILLEGAL_ARGUMENT_ERROR;
1459
0
    return NULL;
1460
0
  }
1461
0
  
1462
0
  if(resB->fIndex == resB->fSize-1) {
1463
0
    *status = U_INDEX_OUTOFBOUNDS_ERROR;
1464
0
  } else {
1465
0
    resB->fIndex++;
1466
0
    switch(RES_GET_TYPE(resB->fRes)) {
1467
0
    case URES_STRING:
1468
0
    case URES_STRING_V2:
1469
0
      return res_getString(&(resB->fResData), resB->fRes, len); 
1470
0
    case URES_TABLE:
1471
0
    case URES_TABLE16:
1472
0
    case URES_TABLE32:
1473
0
      r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, key);
1474
0
      if(r == RES_BOGUS && resB->fHasFallback) {
1475
0
        /* TODO: do the fallback */
1476
0
      }
1477
0
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1478
0
    case URES_ARRAY:
1479
0
    case URES_ARRAY16:
1480
0
      r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1481
0
      if(r == RES_BOGUS && resB->fHasFallback) {
1482
0
        /* TODO: do the fallback */
1483
0
      }
1484
0
      return ures_getStringWithAlias(resB, r, resB->fIndex, len, status);
1485
0
    case URES_ALIAS:
1486
0
      return ures_getStringWithAlias(resB, resB->fRes, resB->fIndex, len, status);
1487
0
    case URES_INT:
1488
0
    case URES_BINARY:
1489
0
    case URES_INT_VECTOR:
1490
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1491
0
        U_FALLTHROUGH;
1492
0
    default:
1493
0
      return NULL;
1494
0
    }
1495
0
  }
1496
0
1497
0
  return NULL;
1498
0
}
1499
1500
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getNextResource(UResourceBundle *resB, UResourceBundle *fillIn, UErrorCode *status) {
1501
0
    const char *key = NULL;
1502
0
    Resource r = RES_BOGUS;
1503
0
1504
0
    if (status==NULL || U_FAILURE(*status)) {
1505
0
            /*return NULL;*/
1506
0
            return fillIn;
1507
0
    }
1508
0
    if(resB == NULL) {
1509
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
1510
0
            /*return NULL;*/
1511
0
            return fillIn;
1512
0
    }
1513
0
1514
0
    if(resB->fIndex == resB->fSize-1) {
1515
0
      *status = U_INDEX_OUTOFBOUNDS_ERROR;
1516
0
      /*return NULL;*/
1517
0
    } else {
1518
0
        resB->fIndex++;
1519
0
        switch(RES_GET_TYPE(resB->fRes)) {
1520
0
        case URES_INT:
1521
0
        case URES_BINARY:
1522
0
        case URES_STRING:
1523
0
        case URES_STRING_V2:
1524
0
        case URES_INT_VECTOR:
1525
0
            return ures_copyResb(fillIn, resB, status);
1526
0
        case URES_TABLE:
1527
0
        case URES_TABLE16:
1528
0
        case URES_TABLE32:
1529
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, resB->fIndex, &key);
1530
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1531
0
                /* TODO: do the fallback */
1532
0
            }
1533
0
            return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1534
0
        case URES_ARRAY:
1535
0
        case URES_ARRAY16:
1536
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, resB->fIndex);
1537
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1538
0
                /* TODO: do the fallback */
1539
0
            }
1540
0
            return init_resb_result(&(resB->fResData), r, key, resB->fIndex, resB->fData, resB, 0, fillIn, status);
1541
0
        default:
1542
0
            /*return NULL;*/
1543
0
            return fillIn;
1544
0
        }
1545
0
    }
1546
0
    /*return NULL;*/
1547
0
    return fillIn;
1548
0
}
1549
1550
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getByIndex(const UResourceBundle *resB, int32_t indexR, UResourceBundle *fillIn, UErrorCode *status) {
1551
0
    const char* key = NULL;
1552
0
    Resource r = RES_BOGUS;
1553
0
1554
0
    if (status==NULL || U_FAILURE(*status)) {
1555
0
        /*return NULL;*/
1556
0
        return fillIn;
1557
0
    }
1558
0
    if(resB == NULL) {
1559
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1560
0
        /*return NULL;*/
1561
0
        return fillIn;
1562
0
    }
1563
0
1564
0
    if(indexR >= 0 && resB->fSize > indexR) {
1565
0
        switch(RES_GET_TYPE(resB->fRes)) {
1566
0
        case URES_INT:
1567
0
        case URES_BINARY:
1568
0
        case URES_STRING:
1569
0
        case URES_STRING_V2:
1570
0
        case URES_INT_VECTOR:
1571
0
            return ures_copyResb(fillIn, resB, status);
1572
0
        case URES_TABLE:
1573
0
        case URES_TABLE16:
1574
0
        case URES_TABLE32:
1575
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexR, &key);
1576
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1577
0
                /* TODO: do the fallback */
1578
0
            }
1579
0
            return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1580
0
        case URES_ARRAY:
1581
0
        case URES_ARRAY16:
1582
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, indexR);
1583
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1584
0
                /* TODO: do the fallback */
1585
0
            }
1586
0
            return init_resb_result(&(resB->fResData), r, key, indexR, resB->fData, resB, 0, fillIn, status);
1587
0
        default:
1588
0
            /*return NULL;*/
1589
0
            return fillIn;
1590
0
        }
1591
0
    } else {
1592
0
        *status = U_MISSING_RESOURCE_ERROR;
1593
0
    }
1594
0
    /*return NULL;*/
1595
0
    return fillIn;
1596
0
}
1597
1598
0
U_CAPI const UChar* U_EXPORT2 ures_getStringByIndex(const UResourceBundle *resB, int32_t indexS, int32_t* len, UErrorCode *status) {
1599
0
    const char* key = NULL;
1600
0
    Resource r = RES_BOGUS;
1601
0
1602
0
    if (status==NULL || U_FAILURE(*status)) {
1603
0
        return NULL;
1604
0
    }
1605
0
    if(resB == NULL) {
1606
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1607
0
        return NULL;
1608
0
    }
1609
0
1610
0
    if(indexS >= 0 && resB->fSize > indexS) {
1611
0
        switch(RES_GET_TYPE(resB->fRes)) {
1612
0
        case URES_STRING:
1613
0
        case URES_STRING_V2:
1614
0
            return res_getString(&(resB->fResData), resB->fRes, len);
1615
0
        case URES_TABLE:
1616
0
        case URES_TABLE16:
1617
0
        case URES_TABLE32:
1618
0
            r = res_getTableItemByIndex(&(resB->fResData), resB->fRes, indexS, &key);
1619
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1620
0
                /* TODO: do the fallback */
1621
0
            }
1622
0
            return ures_getStringWithAlias(resB, r, indexS, len, status);
1623
0
        case URES_ARRAY:
1624
0
        case URES_ARRAY16:
1625
0
            r = res_getArrayItem(&(resB->fResData), resB->fRes, indexS);
1626
0
            if(r == RES_BOGUS && resB->fHasFallback) {
1627
0
                /* TODO: do the fallback */
1628
0
            }
1629
0
            return ures_getStringWithAlias(resB, r, indexS, len, status);
1630
0
        case URES_ALIAS:
1631
0
            return ures_getStringWithAlias(resB, resB->fRes, indexS, len, status);
1632
0
        case URES_INT:
1633
0
        case URES_BINARY:
1634
0
        case URES_INT_VECTOR:
1635
0
            *status = U_RESOURCE_TYPE_MISMATCH;
1636
0
            break;
1637
0
        default:
1638
0
          /* must not occur */
1639
0
          *status = U_INTERNAL_PROGRAM_ERROR;
1640
0
          break;
1641
0
        }
1642
0
    } else {
1643
0
        *status = U_MISSING_RESOURCE_ERROR;
1644
0
    }
1645
0
    return NULL;
1646
0
}
1647
1648
U_CAPI const char * U_EXPORT2
1649
ures_getUTF8StringByIndex(const UResourceBundle *resB,
1650
                          int32_t idx,
1651
                          char *dest, int32_t *pLength,
1652
                          UBool forceCopy,
1653
0
                          UErrorCode *status) {
1654
0
    int32_t length16;
1655
0
    const UChar *s16 = ures_getStringByIndex(resB, idx, &length16, status);
1656
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
1657
0
}
1658
1659
/*U_CAPI const char *ures_getResPath(UResourceBundle *resB) {
1660
  return resB->fResPath;
1661
}*/
1662
1663
U_CAPI UResourceBundle* U_EXPORT2
1664
ures_findResource(const char* path, UResourceBundle *fillIn, UErrorCode *status) 
1665
0
{
1666
0
  UResourceBundle *first = NULL; 
1667
0
  UResourceBundle *result = fillIn;
1668
0
  char *packageName = NULL;
1669
0
  char *pathToResource = NULL, *save = NULL;
1670
0
  char *locale = NULL, *localeEnd = NULL;
1671
0
  int32_t length;
1672
0
1673
0
  if(status == NULL || U_FAILURE(*status)) {
1674
0
    return result;
1675
0
  }
1676
0
1677
0
  length = (int32_t)(uprv_strlen(path)+1);
1678
0
  save = pathToResource = (char *)uprv_malloc(length*sizeof(char));
1679
0
  /* test for NULL */
1680
0
  if(pathToResource == NULL) {
1681
0
    *status = U_MEMORY_ALLOCATION_ERROR;
1682
0
    return result;
1683
0
  }
1684
0
  uprv_memcpy(pathToResource, path, length);
1685
0
1686
0
  locale = pathToResource;
1687
0
  if(*pathToResource == RES_PATH_SEPARATOR) { /* there is a path specification */
1688
0
    pathToResource++;
1689
0
    packageName = pathToResource;
1690
0
    pathToResource = uprv_strchr(pathToResource, RES_PATH_SEPARATOR);
1691
0
    if(pathToResource == NULL) {
1692
0
      *status = U_ILLEGAL_ARGUMENT_ERROR;
1693
0
    } else {
1694
0
      *pathToResource = 0;
1695
0
      locale = pathToResource+1;
1696
0
    }
1697
0
  }
1698
0
1699
0
  localeEnd = uprv_strchr(locale, RES_PATH_SEPARATOR);
1700
0
  if(localeEnd != NULL) {
1701
0
    *localeEnd = 0;
1702
0
  }
1703
0
1704
0
  first = ures_open(packageName, locale, status);
1705
0
1706
0
  if(U_SUCCESS(*status)) {
1707
0
    if(localeEnd) {
1708
0
      result = ures_findSubResource(first, localeEnd+1, fillIn, status);
1709
0
    } else {
1710
0
      result = ures_copyResb(fillIn, first, status);
1711
0
    }
1712
0
    ures_close(first);
1713
0
  }
1714
0
  uprv_free(save);
1715
0
  return result;
1716
0
}
1717
1718
U_CAPI UResourceBundle* U_EXPORT2
1719
ures_findSubResource(const UResourceBundle *resB, char* path, UResourceBundle *fillIn, UErrorCode *status) 
1720
0
{
1721
0
  Resource res = RES_BOGUS;
1722
0
  UResourceBundle *result = fillIn;
1723
0
  const char *key;
1724
0
1725
0
  if(status == NULL || U_FAILURE(*status)) {
1726
0
    return result;
1727
0
  }
1728
0
1729
0
  /* here we do looping and circular alias checking */
1730
0
  /* this loop is here because aliasing is resolved on this level, not on res level */
1731
0
  /* so, when we encounter an alias, it is not an aggregate resource, so we return */
1732
0
  do {
1733
0
    res = res_findResource(&(resB->fResData), resB->fRes, &path, &key); 
1734
0
    if(res != RES_BOGUS) {
1735
0
        result = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1736
0
        resB = result;
1737
0
    } else {
1738
0
        *status = U_MISSING_RESOURCE_ERROR;
1739
0
        break;
1740
0
    }
1741
0
  } while(*path); /* there is more stuff in the path */
1742
0
1743
0
  return result;
1744
0
}
1745
U_INTERNAL const UChar* U_EXPORT2 
1746
ures_getStringByKeyWithFallback(const UResourceBundle *resB, 
1747
                                const char* inKey, 
1748
                                int32_t* len,
1749
0
                                UErrorCode *status) {
1750
0
1751
0
    UResourceBundle stack;
1752
0
    const UChar* retVal = NULL;
1753
0
    ures_initStackObject(&stack);
1754
0
    ures_getByKeyWithFallback(resB, inKey, &stack, status);
1755
0
    int32_t length;
1756
0
    retVal = ures_getString(&stack, &length, status);
1757
0
    ures_close(&stack);
1758
0
    if (U_FAILURE(*status)) {
1759
0
        return NULL;
1760
0
    }
1761
0
    if (length == 3 && retVal[0] == EMPTY_SET && retVal[1] == EMPTY_SET && retVal[2] == EMPTY_SET ) {
1762
0
        retVal = NULL;
1763
0
        length = 0;
1764
0
        *status = U_MISSING_RESOURCE_ERROR;
1765
0
    }
1766
0
    if (len != NULL) {
1767
0
        *len = length;
1768
0
    }
1769
0
    return retVal;
1770
0
}
1771
1772
/*
1773
  Like res_getTableItemByKey but accepts full paths like "NumberElements/latn/patternsShort".
1774
*/  
1775
0
static Resource getTableItemByKeyPath(const ResourceData *pResData, Resource table, const char *key) {
1776
0
  Resource resource = table;  /* The current resource */
1777
0
  icu::CharString path;
1778
0
  UErrorCode errorCode = U_ZERO_ERROR;
1779
0
  path.append(key, errorCode);
1780
0
  if (U_FAILURE(errorCode)) { return RES_BOGUS; }
1781
0
  char *pathPart = path.data();  /* Path from current resource to desired resource */
1782
0
  UResType type = (UResType)RES_GET_TYPE(resource);  /* the current resource type */
1783
0
  while (*pathPart && resource != RES_BOGUS && URES_IS_CONTAINER(type)) {
1784
0
    char *nextPathPart = uprv_strchr(pathPart, RES_PATH_SEPARATOR);
1785
0
    if (nextPathPart != NULL) {
1786
0
      *nextPathPart = 0;  /* Terminating null for this part of path. */
1787
0
      nextPathPart++;
1788
0
    } else {
1789
0
      nextPathPart = uprv_strchr(pathPart, 0);
1790
0
    }
1791
0
    int32_t t;
1792
0
    const char *pathP = pathPart;
1793
0
    resource = res_getTableItemByKey(pResData, resource, &t, &pathP);
1794
0
    type = (UResType)RES_GET_TYPE(resource);
1795
0
    pathPart = nextPathPart; 
1796
0
  }
1797
0
  if (*pathPart) {
1798
0
    return RES_BOGUS;
1799
0
  }
1800
0
  return resource;
1801
0
}
1802
1803
U_CAPI UResourceBundle* U_EXPORT2 
1804
ures_getByKeyWithFallback(const UResourceBundle *resB, 
1805
                          const char* inKey, 
1806
                          UResourceBundle *fillIn, 
1807
0
                          UErrorCode *status) {
1808
0
    Resource res = RES_BOGUS, rootRes = RES_BOGUS;
1809
0
    /*UResourceDataEntry *realData = NULL;*/
1810
0
    UResourceBundle *helper = NULL;
1811
0
1812
0
    if (status==NULL || U_FAILURE(*status)) {
1813
0
        return fillIn;
1814
0
    }
1815
0
    if(resB == NULL) {
1816
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1817
0
        return fillIn;
1818
0
    }
1819
0
1820
0
    int32_t type = RES_GET_TYPE(resB->fRes);
1821
0
    if(URES_IS_TABLE(type)) {
1822
0
        res = getTableItemByKeyPath(&(resB->fResData), resB->fRes, inKey);
1823
0
        const char* key = inKey;
1824
0
        if(res == RES_BOGUS) {
1825
0
            UResourceDataEntry *dataEntry = resB->fData;
1826
0
            CharString path;
1827
0
            char *myPath = NULL;
1828
0
            const char* resPath = resB->fResPath;
1829
0
            int32_t len = resB->fResPathLen;
1830
0
            while(res == RES_BOGUS && dataEntry->fParent != NULL) { /* Otherwise, we'll look in parents */
1831
0
                dataEntry = dataEntry->fParent;
1832
0
                rootRes = dataEntry->fData.rootRes;
1833
0
1834
0
                if(dataEntry->fBogus == U_ZERO_ERROR) {
1835
0
                    path.clear();
1836
0
                    if (len > 0) {
1837
0
                        path.append(resPath, len, *status);
1838
0
                    }
1839
0
                    path.append(inKey, *status);
1840
0
                    if (U_FAILURE(*status)) {
1841
0
                        ures_close(helper);
1842
0
                        return fillIn;
1843
0
                    }
1844
0
                    myPath = path.data();
1845
0
                    key = inKey;
1846
0
                    do {
1847
0
                        res = res_findResource(&(dataEntry->fData), rootRes, &myPath, &key);
1848
0
                        if (RES_GET_TYPE(res) == URES_ALIAS && *myPath) {
1849
0
                            /* We hit an alias, but we didn't finish following the path. */
1850
0
                            helper = init_resb_result(&(dataEntry->fData), res, NULL, -1, dataEntry, resB, 0, helper, status); 
1851
0
                            /*helper = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, helper, status);*/
1852
0
                            if(helper) {
1853
0
                              dataEntry = helper->fData;
1854
0
                              rootRes = helper->fRes;
1855
0
                              resPath = helper->fResPath;
1856
0
                              len = helper->fResPathLen;
1857
0
1858
0
                            } else {
1859
0
                              break;
1860
0
                            }
1861
0
                        }
1862
0
                    } while(*myPath); /* Continue until the whole path is consumed */
1863
0
                }
1864
0
            }
1865
0
            /*const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);*/
1866
0
            if(res != RES_BOGUS) {
1867
0
              /* check if resB->fResPath gives the right name here */
1868
0
                if(uprv_strcmp(dataEntry->fName, uloc_getDefault())==0 || uprv_strcmp(dataEntry->fName, kRootLocaleName)==0) {
1869
0
                    *status = U_USING_DEFAULT_WARNING;
1870
0
                } else {
1871
0
                    *status = U_USING_FALLBACK_WARNING;
1872
0
                }
1873
0
1874
0
                fillIn = init_resb_result(&(dataEntry->fData), res, inKey, -1, dataEntry, resB, 0, fillIn, status);
1875
0
            } else {
1876
0
                *status = U_MISSING_RESOURCE_ERROR;
1877
0
            }
1878
0
        } else {
1879
0
            fillIn = init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
1880
0
        }
1881
0
    } 
1882
0
    else {
1883
0
        *status = U_RESOURCE_TYPE_MISMATCH;
1884
0
    }
1885
0
    ures_close(helper);
1886
0
    return fillIn;
1887
0
}
1888
1889
namespace {
1890
1891
void getAllItemsWithFallback(
1892
        const UResourceBundle *bundle, ResourceDataValue &value,
1893
        ResourceSink &sink,
1894
0
        UErrorCode &errorCode) {
1895
0
    if (U_FAILURE(errorCode)) { return; }
1896
0
    // We recursively enumerate child-first,
1897
0
    // only storing parent items in the absence of child items.
1898
0
    // The sink needs to store a placeholder value for the no-fallback/no-inheritance marker
1899
0
    // to prevent a parent item from being stored.
1900
0
    //
1901
0
    // It would be possible to recursively enumerate parent-first,
1902
0
    // overriding parent items with child items.
1903
0
    // When the sink sees the no-fallback/no-inheritance marker,
1904
0
    // then it would remove the parent's item.
1905
0
    // We would deserialize parent values even though they are overridden in a child bundle.
1906
0
    value.pResData = &bundle->fResData;
1907
0
    UResourceDataEntry *parentEntry = bundle->fData->fParent;
1908
0
    UBool hasParent = parentEntry != NULL && U_SUCCESS(parentEntry->fBogus);
1909
0
    value.setResource(bundle->fRes);
1910
0
    sink.put(bundle->fKey, value, !hasParent, errorCode);
1911
0
    if (hasParent) {
1912
0
        // We might try to query the sink whether
1913
0
        // any fallback from the parent bundle is still possible.
1914
0
1915
0
        // Turn the parent UResourceDataEntry into a UResourceBundle,
1916
0
        // much like in ures_openWithType().
1917
0
        // TODO: See if we can refactor ures_getByKeyWithFallback()
1918
0
        // and pull out an inner function that takes and returns a UResourceDataEntry
1919
0
        // so that we need not create UResourceBundle objects.
1920
0
        UResourceBundle parentBundle;
1921
0
        ures_initStackObject(&parentBundle);
1922
0
        parentBundle.fTopLevelData = parentBundle.fData = parentEntry;
1923
0
        // TODO: What is the difference between bundle fData and fTopLevelData?
1924
0
        uprv_memcpy(&parentBundle.fResData, &parentEntry->fData, sizeof(ResourceData));
1925
0
        // TODO: Try to replace bundle.fResData with just using bundle.fData->fData.
1926
0
        parentBundle.fHasFallback = !parentBundle.fResData.noFallback;
1927
0
        parentBundle.fIsTopLevel = TRUE;
1928
0
        parentBundle.fRes = parentBundle.fResData.rootRes;
1929
0
        parentBundle.fSize = res_countArrayItems(&(parentBundle.fResData), parentBundle.fRes);
1930
0
        parentBundle.fIndex = -1;
1931
0
        entryIncrease(parentEntry);
1932
0
1933
0
        // Look up the container item in the parent bundle.
1934
0
        UResourceBundle containerBundle;
1935
0
        ures_initStackObject(&containerBundle);
1936
0
        const UResourceBundle *rb;
1937
0
        UErrorCode pathErrorCode = U_ZERO_ERROR;  // Ignore if parents up to root do not have this path.
1938
0
        if (bundle->fResPath == NULL || *bundle->fResPath == 0) {
1939
0
            rb = &parentBundle;
1940
0
        } else {
1941
0
            rb = ures_getByKeyWithFallback(&parentBundle, bundle->fResPath,
1942
0
                                           &containerBundle, &pathErrorCode);
1943
0
        }
1944
0
        if (U_SUCCESS(pathErrorCode)) {
1945
0
            getAllItemsWithFallback(rb, value, sink, errorCode);
1946
0
        }
1947
0
        ures_close(&containerBundle);
1948
0
        ures_close(&parentBundle);
1949
0
    }
1950
0
}
1951
1952
}  // namespace
1953
1954
U_CAPI void U_EXPORT2
1955
ures_getAllItemsWithFallback(const UResourceBundle *bundle, const char *path,
1956
0
                             icu::ResourceSink &sink, UErrorCode &errorCode) {
1957
0
    if (U_FAILURE(errorCode)) { return; }
1958
0
    if (path == NULL) {
1959
0
        errorCode = U_ILLEGAL_ARGUMENT_ERROR;
1960
0
        return;
1961
0
    }
1962
0
    UResourceBundle stackBundle;
1963
0
    ures_initStackObject(&stackBundle);
1964
0
    const UResourceBundle *rb;
1965
0
    if (*path == 0) {
1966
0
        // empty path
1967
0
        rb = bundle;
1968
0
    } else {
1969
0
        rb = ures_getByKeyWithFallback(bundle, path, &stackBundle, &errorCode);
1970
0
        if (U_FAILURE(errorCode)) {
1971
0
            ures_close(&stackBundle);
1972
0
            return;
1973
0
        }
1974
0
    }
1975
0
    // Get all table items with fallback.
1976
0
    ResourceDataValue value;
1977
0
    getAllItemsWithFallback(rb, value, sink, errorCode);
1978
0
    ures_close(&stackBundle);
1979
0
}
1980
1981
0
U_CAPI UResourceBundle* U_EXPORT2 ures_getByKey(const UResourceBundle *resB, const char* inKey, UResourceBundle *fillIn, UErrorCode *status) {
1982
0
    Resource res = RES_BOGUS;
1983
0
    UResourceDataEntry *realData = NULL;
1984
0
    const char *key = inKey;
1985
0
1986
0
    if (status==NULL || U_FAILURE(*status)) {
1987
0
        return fillIn;
1988
0
    }
1989
0
    if(resB == NULL) {
1990
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
1991
0
        return fillIn;
1992
0
    }
1993
0
1994
0
    int32_t type = RES_GET_TYPE(resB->fRes);
1995
0
    if(URES_IS_TABLE(type)) {
1996
0
        int32_t t;
1997
0
        res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
1998
0
        if(res == RES_BOGUS) {
1999
0
            key = inKey;
2000
0
            if(resB->fHasFallback == TRUE) {
2001
0
                const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2002
0
                if(U_SUCCESS(*status)) {
2003
0
                  /* check if resB->fResPath gives the right name here */
2004
0
                    return init_resb_result(rd, res, key, -1, realData, resB, 0, fillIn, status);
2005
0
                } else {
2006
0
                    *status = U_MISSING_RESOURCE_ERROR;
2007
0
                }
2008
0
            } else {
2009
0
                *status = U_MISSING_RESOURCE_ERROR;
2010
0
            }
2011
0
        } else {
2012
0
            return init_resb_result(&(resB->fResData), res, key, -1, resB->fData, resB, 0, fillIn, status);
2013
0
        }
2014
0
    } 
2015
#if 0
2016
    /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2017
    /* not currently */
2018
    else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2019
        /* here should go a first attempt to locate the key using index table */
2020
        const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2021
        if(U_SUCCESS(*status)) {
2022
            return init_resb_result(rd, res, key, realData, resB, fillIn, status);
2023
        } else {
2024
            *status = U_MISSING_RESOURCE_ERROR;
2025
        }
2026
    }
2027
#endif    
2028
0
    else {
2029
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2030
0
    }
2031
0
    return fillIn;
2032
0
}
2033
2034
12
U_CAPI const UChar* U_EXPORT2 ures_getStringByKey(const UResourceBundle *resB, const char* inKey, int32_t* len, UErrorCode *status) {
2035
12
    Resource res = RES_BOGUS;
2036
12
    UResourceDataEntry *realData = NULL;
2037
12
    const char* key = inKey;
2038
12
2039
12
    if (status==NULL || U_FAILURE(*status)) {
2040
0
        return NULL;
2041
0
    }
2042
12
    if(resB == NULL) {
2043
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2044
0
        return NULL;
2045
0
    }
2046
12
2047
12
    int32_t type = RES_GET_TYPE(resB->fRes);
2048
12
    if(URES_IS_TABLE(type)) {
2049
12
        int32_t t=0;
2050
12
2051
12
        res = res_getTableItemByKey(&(resB->fResData), resB->fRes, &t, &key);
2052
12
2053
12
        if(res == RES_BOGUS) {
2054
6
            key = inKey;
2055
6
            if(resB->fHasFallback == TRUE) {
2056
0
                const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2057
0
                if(U_SUCCESS(*status)) {
2058
0
                    switch (RES_GET_TYPE(res)) {
2059
0
                    case URES_STRING:
2060
0
                    case URES_STRING_V2:
2061
0
                        return res_getString(rd, res, len);
2062
0
                    case URES_ALIAS:
2063
0
                      {
2064
0
                        const UChar* result = 0;
2065
0
                        UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2066
0
                        result = ures_getString(tempRes, len, status);
2067
0
                        ures_close(tempRes);
2068
0
                        return result;
2069
0
                      }
2070
0
                    default:
2071
0
                        *status = U_RESOURCE_TYPE_MISMATCH;
2072
0
                    }
2073
0
                } else {
2074
0
                    *status = U_MISSING_RESOURCE_ERROR;
2075
0
                }
2076
6
            } else {
2077
6
                *status = U_MISSING_RESOURCE_ERROR;
2078
6
            }
2079
6
        } else {
2080
6
            switch (RES_GET_TYPE(res)) {
2081
6
            case URES_STRING:
2082
6
            case URES_STRING_V2:
2083
6
                return res_getString(&(resB->fResData), res, len);
2084
6
            case URES_ALIAS:
2085
0
              {
2086
0
                const UChar* result = 0;
2087
0
                UResourceBundle *tempRes = ures_getByKey(resB, inKey, NULL, status);
2088
0
                result = ures_getString(tempRes, len, status);
2089
0
                ures_close(tempRes);
2090
0
                return result;
2091
6
              }
2092
6
            default:
2093
0
                *status = U_RESOURCE_TYPE_MISMATCH;
2094
6
            }
2095
6
        }
2096
12
    } 
2097
#if 0 
2098
    /* this is a kind of TODO item. If we have an array with an index table, we could do this. */
2099
    /* not currently */   
2100
    else if(RES_GET_TYPE(resB->fRes) == URES_ARRAY && resB->fHasFallback == TRUE) {
2101
        /* here should go a first attempt to locate the key using index table */
2102
        const ResourceData *rd = getFallbackData(resB, &key, &realData, &res, status);
2103
        if(U_SUCCESS(*status)) {
2104
            return res_getString(rd, res, len);
2105
        } else {
2106
            *status = U_MISSING_RESOURCE_ERROR;
2107
        }
2108
    } 
2109
#endif    
2110
0
    else {
2111
0
        *status = U_RESOURCE_TYPE_MISMATCH;
2112
0
    }
2113
12
    return NULL;
2114
12
}
2115
2116
U_CAPI const char * U_EXPORT2
2117
ures_getUTF8StringByKey(const UResourceBundle *resB,
2118
                        const char *key,
2119
                        char *dest, int32_t *pLength,
2120
                        UBool forceCopy,
2121
0
                        UErrorCode *status) {
2122
0
    int32_t length16;
2123
0
    const UChar *s16 = ures_getStringByKey(resB, key, &length16, status);
2124
0
    return ures_toUTF8String(s16, length16, dest, pLength, forceCopy, status);
2125
0
}
2126
2127
/* TODO: clean from here down */
2128
2129
/**
2130
 *  INTERNAL: Get the name of the first real locale (not placeholder) 
2131
 *  that has resource bundle data.
2132
 */
2133
U_INTERNAL const char*  U_EXPORT2
2134
ures_getLocaleInternal(const UResourceBundle* resourceBundle, UErrorCode* status)
2135
0
{
2136
0
    if (status==NULL || U_FAILURE(*status)) {
2137
0
        return NULL;
2138
0
    }
2139
0
    if (!resourceBundle) {
2140
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2141
0
        return NULL;
2142
0
    } else {
2143
0
      return resourceBundle->fData->fName;
2144
0
    }
2145
0
}
2146
2147
U_CAPI const char* U_EXPORT2 
2148
ures_getLocale(const UResourceBundle* resourceBundle, 
2149
               UErrorCode* status)
2150
0
{
2151
0
  return ures_getLocaleInternal(resourceBundle, status);
2152
0
}
2153
2154
2155
U_CAPI const char* U_EXPORT2 
2156
ures_getLocaleByType(const UResourceBundle* resourceBundle, 
2157
                     ULocDataLocaleType type, 
2158
0
                     UErrorCode* status) {
2159
0
    if (status==NULL || U_FAILURE(*status)) {
2160
0
        return NULL;
2161
0
    }
2162
0
    if (!resourceBundle) {
2163
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2164
0
        return NULL;
2165
0
    } else {
2166
0
        switch(type) {
2167
0
        case ULOC_ACTUAL_LOCALE:
2168
0
            return resourceBundle->fData->fName;
2169
0
        case ULOC_VALID_LOCALE:
2170
0
            return resourceBundle->fTopLevelData->fName;
2171
0
        case ULOC_REQUESTED_LOCALE:
2172
0
        default:
2173
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2174
0
            return NULL;
2175
0
        }
2176
0
    }
2177
0
}
2178
2179
0
U_CFUNC const char* ures_getName(const UResourceBundle* resB) {
2180
0
  if(resB == NULL) {
2181
0
    return NULL;
2182
0
  }
2183
0
2184
0
  return resB->fData->fName;
2185
0
}
2186
2187
#ifdef URES_DEBUG
2188
U_CFUNC const char* ures_getPath(const UResourceBundle* resB) {
2189
  if(resB == NULL) {
2190
    return NULL;
2191
  }
2192
2193
  return resB->fData->fPath;
2194
}
2195
#endif
2196
2197
static UResourceBundle*
2198
ures_openWithType(UResourceBundle *r, const char* path, const char* localeID,
2199
12
                  UResOpenType openType, UErrorCode* status) {
2200
12
    if(U_FAILURE(*status)) {
2201
0
        return NULL;
2202
0
    }
2203
12
2204
12
    UResourceDataEntry *entry;
2205
12
    if(openType != URES_OPEN_DIRECT) {
2206
0
        /* first "canonicalize" the locale ID */
2207
0
        char canonLocaleID[ULOC_FULLNAME_CAPACITY];
2208
0
        uloc_getBaseName(localeID, canonLocaleID, UPRV_LENGTHOF(canonLocaleID), status);
2209
0
        if(U_FAILURE(*status) || *status == U_STRING_NOT_TERMINATED_WARNING) {
2210
0
            *status = U_ILLEGAL_ARGUMENT_ERROR;
2211
0
            return NULL;
2212
0
        }
2213
0
        entry = entryOpen(path, canonLocaleID, openType, status);
2214
12
    } else {
2215
12
        entry = entryOpenDirect(path, localeID, status);
2216
12
    }
2217
12
    if(U_FAILURE(*status)) {
2218
0
        return NULL;
2219
0
    }
2220
12
    if(entry == NULL) {
2221
0
        *status = U_MISSING_RESOURCE_ERROR;
2222
0
        return NULL;
2223
0
    }
2224
12
2225
12
    UBool isStackObject;
2226
12
    if(r == NULL) {
2227
12
        r = (UResourceBundle *)uprv_malloc(sizeof(UResourceBundle));
2228
12
        if(r == NULL) {
2229
0
            entryClose(entry);
2230
0
            *status = U_MEMORY_ALLOCATION_ERROR;
2231
0
            return NULL;
2232
0
        }
2233
12
        isStackObject = FALSE;
2234
12
    } else {  // fill-in
2235
0
        isStackObject = ures_isStackObject(r);
2236
0
        ures_closeBundle(r, FALSE);
2237
0
    }
2238
12
    uprv_memset(r, 0, sizeof(UResourceBundle));
2239
12
    ures_setIsStackObject(r, isStackObject);
2240
12
2241
12
    r->fTopLevelData = r->fData = entry;
2242
12
    uprv_memcpy(&r->fResData, &entry->fData, sizeof(ResourceData));
2243
12
    r->fHasFallback = openType != URES_OPEN_DIRECT && !r->fResData.noFallback;
2244
12
    r->fIsTopLevel = TRUE;
2245
12
    r->fRes = r->fResData.rootRes;
2246
12
    r->fSize = res_countArrayItems(&(r->fResData), r->fRes);
2247
12
    r->fIndex = -1;
2248
12
2249
12
    return r;
2250
12
}
2251
2252
U_CAPI UResourceBundle* U_EXPORT2
2253
0
ures_open(const char* path, const char* localeID, UErrorCode* status) {
2254
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2255
0
}
2256
2257
U_CAPI UResourceBundle* U_EXPORT2
2258
0
ures_openNoDefault(const char* path, const char* localeID, UErrorCode* status) {
2259
0
    return ures_openWithType(NULL, path, localeID, URES_OPEN_LOCALE_ROOT, status);
2260
0
}
2261
2262
/**
2263
 *  Opens a resource bundle without "canonicalizing" the locale name. No fallback will be performed 
2264
 *  or sought. However, alias substitution will happen!
2265
 */
2266
U_CAPI UResourceBundle*  U_EXPORT2
2267
12
ures_openDirect(const char* path, const char* localeID, UErrorCode* status) {
2268
12
    return ures_openWithType(NULL, path, localeID, URES_OPEN_DIRECT, status);
2269
12
}
2270
2271
/**
2272
 *  API: This function is used to open a resource bundle 
2273
 *  proper fallback chaining is executed while initialization. 
2274
 *  The result is stored in cache for later fallback search.
2275
 */
2276
U_CAPI void U_EXPORT2
2277
ures_openFillIn(UResourceBundle *r, const char* path,
2278
0
                const char* localeID, UErrorCode* status) {
2279
0
    if(U_SUCCESS(*status) && r == NULL) {
2280
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2281
0
        return;
2282
0
    }
2283
0
    ures_openWithType(r, path, localeID, URES_OPEN_LOCALE_DEFAULT_ROOT, status);
2284
0
}
2285
2286
/**
2287
 *  API: Counts members. For arrays and tables, returns number of resources.
2288
 *  For strings, returns 1.
2289
 */
2290
U_CAPI int32_t  U_EXPORT2
2291
ures_countArrayItems(const UResourceBundle* resourceBundle,
2292
                  const char* resourceKey,
2293
                  UErrorCode* status)
2294
0
{
2295
0
    UResourceBundle resData;
2296
0
    ures_initStackObject(&resData);
2297
0
    if (status==NULL || U_FAILURE(*status)) {
2298
0
        return 0;
2299
0
    }
2300
0
    if(resourceBundle == NULL) {
2301
0
        *status = U_ILLEGAL_ARGUMENT_ERROR;
2302
0
        return 0;
2303
0
    }
2304
0
    ures_getByKey(resourceBundle, resourceKey, &resData, status);
2305
0
    
2306
0
    if(resData.fResData.data != NULL) {
2307
0
        int32_t result = res_countArrayItems(&resData.fResData, resData.fRes);
2308
0
        ures_close(&resData);
2309
0
        return result;
2310
0
    } else {
2311
0
        *status = U_MISSING_RESOURCE_ERROR;
2312
0
        ures_close(&resData);
2313
0
        return 0;
2314
0
    }
2315
0
}
2316
2317
/**
2318
 * Internal function.
2319
 * Return the version number associated with this ResourceBundle as a string.
2320
 *
2321
 * @param resourceBundle The resource bundle for which the version is checked.
2322
 * @return  A version number string as specified in the resource bundle or its parent.
2323
 *          The caller does not own this string.
2324
 * @see ures_getVersion
2325
 * @internal
2326
 */
2327
U_INTERNAL const char* U_EXPORT2 
2328
ures_getVersionNumberInternal(const UResourceBundle *resourceBundle)
2329
0
{
2330
0
    if (!resourceBundle) return NULL;
2331
0
2332
0
    if(resourceBundle->fVersion == NULL) {
2333
0
2334
0
        /* If the version ID has not been built yet, then do so.  Retrieve */
2335
0
        /* the minor version from the file. */
2336
0
        UErrorCode status = U_ZERO_ERROR;
2337
0
        int32_t minor_len = 0;
2338
0
        int32_t len;
2339
0
2340
0
        const UChar* minor_version = ures_getStringByKey(resourceBundle, kVersionTag, &minor_len, &status);
2341
0
        
2342
0
        /* Determine the length of of the final version string.  This is */
2343
0
        /* the length of the major part + the length of the separator */
2344
0
        /* (==1) + the length of the minor part (+ 1 for the zero byte at */
2345
0
        /* the end). */
2346
0
2347
0
        len = (minor_len > 0) ? minor_len : 1;
2348
0
    
2349
0
        /* Allocate the string, and build it up. */
2350
0
        /* + 1 for zero byte */
2351
0
2352
0
2353
0
        ((UResourceBundle *)resourceBundle)->fVersion = (char *)uprv_malloc(1 + len); 
2354
0
        /* Check for null pointer. */
2355
0
        if (((UResourceBundle *)resourceBundle)->fVersion == NULL) {
2356
0
            return NULL;
2357
0
        }
2358
0
       
2359
0
        if(minor_len > 0) {
2360
0
            u_UCharsToChars(minor_version, resourceBundle->fVersion , minor_len);
2361
0
            resourceBundle->fVersion[len] =  '\0';
2362
0
        }
2363
0
        else {
2364
0
            uprv_strcpy(resourceBundle->fVersion, kDefaultMinorVersion);
2365
0
        }
2366
0
    }
2367
0
2368
0
    return resourceBundle->fVersion;
2369
0
}
2370
2371
U_CAPI const char*  U_EXPORT2
2372
ures_getVersionNumber(const UResourceBundle*   resourceBundle)
2373
0
{
2374
0
    return ures_getVersionNumberInternal(resourceBundle);
2375
0
}
2376
2377
0
U_CAPI void U_EXPORT2 ures_getVersion(const UResourceBundle* resB, UVersionInfo versionInfo) {
2378
0
    if (!resB) return;
2379
0
2380
0
    u_versionFromString(versionInfo, ures_getVersionNumberInternal(resB));
2381
0
}
2382
2383
/** Tree support functions *******************************/
2384
0
#define INDEX_LOCALE_NAME "res_index"
2385
0
#define INDEX_TAG         "InstalledLocales"
2386
0
#define DEFAULT_TAG       "default"
2387
2388
#if defined(URES_TREE_DEBUG)
2389
#include <stdio.h>
2390
#endif
2391
2392
typedef struct ULocalesContext {
2393
    UResourceBundle installed;
2394
    UResourceBundle curr;
2395
} ULocalesContext;
2396
2397
static void U_CALLCONV
2398
0
ures_loc_closeLocales(UEnumeration *enumerator) {
2399
0
    ULocalesContext *ctx = (ULocalesContext *)enumerator->context;
2400
0
    ures_close(&ctx->curr);
2401
0
    ures_close(&ctx->installed);
2402
0
    uprv_free(ctx);
2403
0
    uprv_free(enumerator);
2404
0
}
2405
2406
static int32_t U_CALLCONV
2407
0
ures_loc_countLocales(UEnumeration *en, UErrorCode * /*status*/) {
2408
0
    ULocalesContext *ctx = (ULocalesContext *)en->context;
2409
0
    return ures_getSize(&ctx->installed);
2410
0
}
2411
2412
U_CDECL_BEGIN
2413
2414
2415
static const char * U_CALLCONV
2416
ures_loc_nextLocale(UEnumeration* en,
2417
                    int32_t* resultLength,
2418
0
                    UErrorCode* status) {
2419
0
    ULocalesContext *ctx = (ULocalesContext *)en->context;
2420
0
    UResourceBundle *res = &(ctx->installed);
2421
0
    UResourceBundle *k = NULL;
2422
0
    const char *result = NULL;
2423
0
    int32_t len = 0;
2424
0
    if(ures_hasNext(res) && (k = ures_getNextResource(res, &ctx->curr, status))) {
2425
0
        result = ures_getKey(k);
2426
0
        len = (int32_t)uprv_strlen(result);
2427
0
    }
2428
0
    if (resultLength) {
2429
0
        *resultLength = len;
2430
0
    }
2431
0
    return result;
2432
0
}
2433
2434
static void U_CALLCONV 
2435
ures_loc_resetLocales(UEnumeration* en, 
2436
0
                      UErrorCode* /*status*/) {
2437
0
    UResourceBundle *res = &((ULocalesContext *)en->context)->installed;
2438
0
    ures_resetIterator(res);
2439
0
}
2440
2441
U_CDECL_END
2442
2443
static const UEnumeration gLocalesEnum = {
2444
    NULL,
2445
        NULL,
2446
        ures_loc_closeLocales,
2447
        ures_loc_countLocales,
2448
        uenum_unextDefault,
2449
        ures_loc_nextLocale,
2450
        ures_loc_resetLocales
2451
};
2452
2453
2454
U_CAPI UEnumeration* U_EXPORT2
2455
ures_openAvailableLocales(const char *path, UErrorCode *status)
2456
0
{
2457
0
    UResourceBundle *idx = NULL;
2458
0
    UEnumeration *en = NULL;
2459
0
    ULocalesContext *myContext = NULL;
2460
0
2461
0
    if(U_FAILURE(*status)) {
2462
0
        return NULL;
2463
0
    }
2464
0
    myContext = static_cast<ULocalesContext *>(uprv_malloc(sizeof(ULocalesContext)));
2465
0
    en =  (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2466
0
    if(!en || !myContext) {
2467
0
        *status = U_MEMORY_ALLOCATION_ERROR;
2468
0
        uprv_free(en);
2469
0
        uprv_free(myContext);
2470
0
        return NULL;
2471
0
    }
2472
0
    uprv_memcpy(en, &gLocalesEnum, sizeof(UEnumeration));
2473
0
2474
0
    ures_initStackObject(&myContext->installed);
2475
0
    ures_initStackObject(&myContext->curr);
2476
0
    idx = ures_openDirect(path, INDEX_LOCALE_NAME, status);
2477
0
    ures_getByKey(idx, INDEX_TAG, &myContext->installed, status);
2478
0
    if(U_SUCCESS(*status)) {
2479
#if defined(URES_TREE_DEBUG)
2480
        fprintf(stderr, "Got %s::%s::[%s] : %s\n", 
2481
            path, INDEX_LOCALE_NAME, INDEX_TAG, ures_getKey(&myContext->installed));
2482
#endif
2483
        en->context = myContext;
2484
0
    } else {
2485
#if defined(URES_TREE_DEBUG)
2486
        fprintf(stderr, "%s open failed - %s\n", path, u_errorName(*status));
2487
#endif
2488
0
        ures_close(&myContext->installed);
2489
0
        uprv_free(myContext);
2490
0
        uprv_free(en);
2491
0
        en = NULL;
2492
0
    }
2493
0
    
2494
0
    ures_close(idx);
2495
0
    
2496
0
    return en;
2497
0
}
2498
2499
0
static UBool isLocaleInList(UEnumeration *locEnum, const char *locToSearch, UErrorCode *status) {
2500
0
    const char *loc;
2501
0
    while ((loc = uenum_next(locEnum, NULL, status)) != NULL) {
2502
0
        if (uprv_strcmp(loc, locToSearch) == 0) {
2503
0
            return TRUE;
2504
0
        }
2505
0
    }
2506
0
    return FALSE;
2507
0
}
2508
2509
U_CAPI int32_t U_EXPORT2
2510
ures_getFunctionalEquivalent(char *result, int32_t resultCapacity,
2511
                             const char *path, const char *resName, const char *keyword, const char *locid,
2512
                             UBool *isAvailable, UBool omitDefault, UErrorCode *status)
2513
0
{
2514
0
    char kwVal[1024] = ""; /* value of keyword 'keyword' */
2515
0
    char defVal[1024] = ""; /* default value for given locale */
2516
0
    char defLoc[1024] = ""; /* default value for given locale */
2517
0
    char base[1024] = ""; /* base locale */
2518
0
    char found[1024];
2519
0
    char parent[1024];
2520
0
    char full[1024] = "";
2521
0
    UResourceBundle bund1, bund2;
2522
0
    UResourceBundle *res = NULL;
2523
0
    UErrorCode subStatus = U_ZERO_ERROR;
2524
0
    int32_t length = 0;
2525
0
    if(U_FAILURE(*status)) return 0;
2526
0
    uloc_getKeywordValue(locid, keyword, kwVal, 1024-1,&subStatus);
2527
0
    if(!uprv_strcmp(kwVal, DEFAULT_TAG)) {
2528
0
        kwVal[0]=0;
2529
0
    }
2530
0
    uloc_getBaseName(locid, base, 1024-1,&subStatus);
2531
#if defined(URES_TREE_DEBUG)
2532
    fprintf(stderr, "getFunctionalEquivalent: \"%s\" [%s=%s] in %s - %s\n", 
2533
            locid, keyword, kwVal, base, u_errorName(subStatus));
2534
#endif
2535
0
    ures_initStackObject(&bund1);
2536
0
    ures_initStackObject(&bund2);
2537
0
    
2538
0
    
2539
0
    uprv_strcpy(parent, base);
2540
0
    uprv_strcpy(found, base);
2541
0
2542
0
    if(isAvailable) { 
2543
0
        UEnumeration *locEnum = ures_openAvailableLocales(path, &subStatus);
2544
0
        *isAvailable = TRUE;
2545
0
        if (U_SUCCESS(subStatus)) {
2546
0
            *isAvailable = isLocaleInList(locEnum, parent, &subStatus);
2547
0
        }
2548
0
        uenum_close(locEnum);
2549
0
    }
2550
0
2551
0
    if(U_FAILURE(subStatus)) {
2552
0
        *status = subStatus;
2553
0
        return 0;
2554
0
    }
2555
0
    
2556
0
    do {
2557
0
        subStatus = U_ZERO_ERROR;
2558
0
        res = ures_open(path, parent, &subStatus);
2559
0
        if(((subStatus == U_USING_FALLBACK_WARNING) ||
2560
0
            (subStatus == U_USING_DEFAULT_WARNING)) && isAvailable)
2561
0
        {
2562
0
            *isAvailable = FALSE;
2563
0
        }
2564
0
        isAvailable = NULL; /* only want to set this the first time around */
2565
0
        
2566
#if defined(URES_TREE_DEBUG)
2567
        fprintf(stderr, "%s;%s -> %s [%s]\n", path?path:"ICUDATA", parent, u_errorName(subStatus), ures_getLocale(res, &subStatus));
2568
#endif
2569
0
        if(U_FAILURE(subStatus)) {
2570
0
            *status = subStatus;
2571
0
        } else if(subStatus == U_ZERO_ERROR) {
2572
0
            ures_getByKey(res,resName,&bund1, &subStatus);
2573
0
            if(subStatus == U_ZERO_ERROR) {
2574
0
                const UChar *defUstr;
2575
0
                int32_t defLen;
2576
0
                /* look for default item */
2577
#if defined(URES_TREE_DEBUG)
2578
                fprintf(stderr, "%s;%s : loaded default -> %s\n",
2579
                    path?path:"ICUDATA", parent, u_errorName(subStatus));
2580
#endif
2581
0
                defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2582
0
                if(U_SUCCESS(subStatus) && defLen) {
2583
0
                    u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2584
#if defined(URES_TREE_DEBUG)
2585
                    fprintf(stderr, "%s;%s -> default %s=%s,  %s\n", 
2586
                        path?path:"ICUDATA", parent, keyword, defVal, u_errorName(subStatus));
2587
#endif
2588
0
                    uprv_strcpy(defLoc, parent);
2589
0
                    if(kwVal[0]==0) {
2590
0
                        uprv_strcpy(kwVal, defVal);
2591
#if defined(URES_TREE_DEBUG)
2592
                        fprintf(stderr, "%s;%s -> kwVal =  %s\n", 
2593
                            path?path:"ICUDATA", parent, keyword, kwVal);
2594
#endif
2595
                    }
2596
0
                }
2597
0
            }
2598
0
        }
2599
0
        
2600
0
        subStatus = U_ZERO_ERROR;
2601
0
2602
0
        if (res != NULL) {
2603
0
            uprv_strcpy(found, ures_getLocaleByType(res, ULOC_VALID_LOCALE, &subStatus));
2604
0
        }
2605
0
2606
0
        uloc_getParent(found,parent,sizeof(parent),&subStatus);
2607
0
        ures_close(res);
2608
0
    } while(!defVal[0] && *found && uprv_strcmp(found, "root") != 0 && U_SUCCESS(*status));
2609
0
    
2610
0
    /* Now, see if we can find the kwVal collator.. start the search over.. */
2611
0
    uprv_strcpy(parent, base);
2612
0
    uprv_strcpy(found, base);
2613
0
    
2614
0
    do {
2615
0
        subStatus = U_ZERO_ERROR;
2616
0
        res = ures_open(path, parent, &subStatus);
2617
0
        if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2618
0
            *isAvailable = FALSE;
2619
0
        }
2620
0
        isAvailable = NULL; /* only want to set this the first time around */
2621
0
        
2622
#if defined(URES_TREE_DEBUG)
2623
        fprintf(stderr, "%s;%s -> %s (looking for %s)\n", 
2624
            path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2625
#endif
2626
0
        if(U_FAILURE(subStatus)) {
2627
0
            *status = subStatus;
2628
0
        } else if(subStatus == U_ZERO_ERROR) {
2629
0
            ures_getByKey(res,resName,&bund1, &subStatus);
2630
#if defined(URES_TREE_DEBUG)
2631
/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, resName, u_errorName(subStatus));
2632
#endif
2633
0
            if(subStatus == U_ZERO_ERROR) {
2634
0
                ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2635
#if defined(URES_TREE_DEBUG)
2636
/**/ fprintf(stderr,"@%d [%s] %s\n", __LINE__, kwVal, u_errorName(subStatus));
2637
#endif
2638
0
                if(subStatus == U_ZERO_ERROR) {
2639
#if defined(URES_TREE_DEBUG)
2640
                    fprintf(stderr, "%s;%s -> full0 %s=%s,  %s\n", 
2641
                        path?path:"ICUDATA", parent, keyword, kwVal, u_errorName(subStatus));
2642
#endif
2643
0
                    uprv_strcpy(full, parent);
2644
0
                    if(*full == 0) {
2645
0
                        uprv_strcpy(full, "root");
2646
0
                    }
2647
0
                        /* now, recalculate default kw if need be */
2648
0
                        if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2649
0
                          const UChar *defUstr;
2650
0
                          int32_t defLen;
2651
0
                          /* look for default item */
2652
#if defined(URES_TREE_DEBUG)
2653
                            fprintf(stderr, "%s;%s -> recalculating Default0\n", 
2654
                                    path?path:"ICUDATA", full);
2655
#endif
2656
0
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2657
0
                          if(U_SUCCESS(subStatus) && defLen) {
2658
0
                            u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2659
#if defined(URES_TREE_DEBUG)
2660
                            fprintf(stderr, "%s;%s -> default0 %s=%s,  %s\n", 
2661
                                    path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2662
#endif
2663
0
                            uprv_strcpy(defLoc, full);
2664
0
                          }
2665
0
                        } /* end of recalculate default KW */
2666
#if defined(URES_TREE_DEBUG)
2667
                        else {
2668
                          fprintf(stderr, "No trim0,  %s <= %s\n", defLoc, full);
2669
                        }
2670
#endif
2671
0
                } else {
2672
#if defined(URES_TREE_DEBUG)
2673
                    fprintf(stderr, "err=%s in %s looking for %s\n", 
2674
                        u_errorName(subStatus), parent, kwVal);
2675
#endif
2676
                }
2677
0
            }
2678
0
        }
2679
0
        
2680
0
        subStatus = U_ZERO_ERROR;
2681
0
        
2682
0
        uprv_strcpy(found, parent);
2683
0
        uloc_getParent(found,parent,1023,&subStatus);
2684
0
        ures_close(res);
2685
0
    } while(!full[0] && *found && U_SUCCESS(*status));
2686
0
    
2687
0
    if((full[0]==0) && uprv_strcmp(kwVal, defVal)) {
2688
#if defined(URES_TREE_DEBUG)
2689
        fprintf(stderr, "Failed to locate kw %s - try default %s\n", kwVal, defVal);
2690
#endif
2691
0
        uprv_strcpy(kwVal, defVal);
2692
0
        uprv_strcpy(parent, base);
2693
0
        uprv_strcpy(found, base);
2694
0
        
2695
0
        do { /* search for 'default' named item */
2696
0
            subStatus = U_ZERO_ERROR;
2697
0
            res = ures_open(path, parent, &subStatus);
2698
0
            if((subStatus == U_USING_FALLBACK_WARNING) && isAvailable) {
2699
0
                *isAvailable = FALSE;
2700
0
            }
2701
0
            isAvailable = NULL; /* only want to set this the first time around */
2702
0
            
2703
#if defined(URES_TREE_DEBUG)
2704
            fprintf(stderr, "%s;%s -> %s (looking for default %s)\n",
2705
                path?path:"ICUDATA", parent, u_errorName(subStatus), kwVal);
2706
#endif
2707
0
            if(U_FAILURE(subStatus)) {
2708
0
                *status = subStatus;
2709
0
            } else if(subStatus == U_ZERO_ERROR) {
2710
0
                ures_getByKey(res,resName,&bund1, &subStatus);
2711
0
                if(subStatus == U_ZERO_ERROR) {
2712
0
                    ures_getByKey(&bund1, kwVal, &bund2, &subStatus);
2713
0
                    if(subStatus == U_ZERO_ERROR) {
2714
#if defined(URES_TREE_DEBUG)
2715
                        fprintf(stderr, "%s;%s -> full1 %s=%s,  %s\n", path?path:"ICUDATA",
2716
                            parent, keyword, kwVal, u_errorName(subStatus));
2717
#endif
2718
0
                        uprv_strcpy(full, parent);
2719
0
                        if(*full == 0) {
2720
0
                            uprv_strcpy(full, "root");
2721
0
                        }
2722
0
                        
2723
0
                        /* now, recalculate default kw if need be */
2724
0
                        if(uprv_strlen(defLoc) > uprv_strlen(full)) {
2725
0
                          const UChar *defUstr;
2726
0
                          int32_t defLen;
2727
0
                          /* look for default item */
2728
#if defined(URES_TREE_DEBUG)
2729
                            fprintf(stderr, "%s;%s -> recalculating Default1\n", 
2730
                                    path?path:"ICUDATA", full);
2731
#endif
2732
0
                          defUstr = ures_getStringByKey(&bund1, DEFAULT_TAG, &defLen, &subStatus);
2733
0
                          if(U_SUCCESS(subStatus) && defLen) {
2734
0
                            u_UCharsToChars(defUstr, defVal, u_strlen(defUstr));
2735
#if defined(URES_TREE_DEBUG)
2736
                            fprintf(stderr, "%s;%s -> default %s=%s,  %s\n", 
2737
                                    path?path:"ICUDATA", full, keyword, defVal, u_errorName(subStatus));
2738
#endif
2739
0
                            uprv_strcpy(defLoc, full);
2740
0
                          }
2741
0
                        } /* end of recalculate default KW */
2742
#if defined(URES_TREE_DEBUG)
2743
                        else {
2744
                          fprintf(stderr, "No trim1,  %s <= %s\n", defLoc, full);
2745
                        }
2746
#endif
2747
                    }
2748
0
                }
2749
0
            }
2750
0
            subStatus = U_ZERO_ERROR;
2751
0
            
2752
0
            uprv_strcpy(found, parent);
2753
0
            uloc_getParent(found,parent,1023,&subStatus);
2754
0
            ures_close(res);
2755
0
        } while(!full[0] && *found && U_SUCCESS(*status));
2756
0
    }
2757
0
    
2758
0
    if(U_SUCCESS(*status)) {
2759
0
        if(!full[0]) {
2760
#if defined(URES_TREE_DEBUG)
2761
          fprintf(stderr, "Still could not load keyword %s=%s\n", keyword, kwVal);
2762
#endif
2763
          *status = U_MISSING_RESOURCE_ERROR;
2764
0
        } else if(omitDefault) {
2765
#if defined(URES_TREE_DEBUG)
2766
          fprintf(stderr,"Trim? full=%s, defLoc=%s, found=%s\n", full, defLoc, found);
2767
#endif        
2768
0
          if(uprv_strlen(defLoc) <= uprv_strlen(full)) {
2769
0
            /* found the keyword in a *child* of where the default tag was present. */
2770
0
            if(!uprv_strcmp(kwVal, defVal)) { /* if the requested kw is default, */
2771
0
              /* and the default is in or in an ancestor of the current locale */
2772
#if defined(URES_TREE_DEBUG)
2773
              fprintf(stderr, "Removing unneeded var %s=%s\n", keyword, kwVal);
2774
#endif
2775
              kwVal[0]=0;
2776
0
            }
2777
0
          }
2778
0
        }
2779
0
        uprv_strcpy(found, full);
2780
0
        if(kwVal[0]) {
2781
0
            uprv_strcat(found, "@");
2782
0
            uprv_strcat(found, keyword);
2783
0
            uprv_strcat(found, "=");
2784
0
            uprv_strcat(found, kwVal);
2785
0
        } else if(!omitDefault) {
2786
0
            uprv_strcat(found, "@");
2787
0
            uprv_strcat(found, keyword);
2788
0
            uprv_strcat(found, "=");
2789
0
            uprv_strcat(found, defVal);
2790
0
        }
2791
0
    }
2792
0
    /* we found the default locale - no need to repeat it.*/
2793
0
    
2794
0
    ures_close(&bund1);
2795
0
    ures_close(&bund2);
2796
0
    
2797
0
    length = (int32_t)uprv_strlen(found);
2798
0
2799
0
    if(U_SUCCESS(*status)) {
2800
0
        int32_t copyLength = uprv_min(length, resultCapacity);
2801
0
        if(copyLength>0) {
2802
0
            uprv_strncpy(result, found, copyLength);
2803
0
        }
2804
0
        if(length == 0) {
2805
0
          *status = U_MISSING_RESOURCE_ERROR; 
2806
0
        }
2807
0
    } else {
2808
0
        length = 0;
2809
0
        result[0]=0;
2810
0
    }
2811
0
    return u_terminateChars(result, resultCapacity, length, status);
2812
0
}
2813
2814
U_CAPI UEnumeration* U_EXPORT2
2815
ures_getKeywordValues(const char *path, const char *keyword, UErrorCode *status)
2816
0
{
2817
0
#define VALUES_BUF_SIZE 2048
2818
0
#define VALUES_LIST_SIZE 512
2819
0
    
2820
0
    char       valuesBuf[VALUES_BUF_SIZE];
2821
0
    int32_t    valuesIndex = 0;
2822
0
    const char *valuesList[VALUES_LIST_SIZE];
2823
0
    int32_t    valuesCount = 0;
2824
0
    
2825
0
    const char *locale;
2826
0
    int32_t     locLen;
2827
0
    
2828
0
    UEnumeration *locs = NULL;
2829
0
    
2830
0
    UResourceBundle    item;
2831
0
    UResourceBundle    subItem;
2832
0
    
2833
0
    ures_initStackObject(&item);
2834
0
    ures_initStackObject(&subItem);
2835
0
    locs = ures_openAvailableLocales(path, status);
2836
0
    
2837
0
    if(U_FAILURE(*status)) {
2838
0
        ures_close(&item);
2839
0
        ures_close(&subItem);
2840
0
        return NULL;
2841
0
    }
2842
0
    
2843
0
    valuesBuf[0]=0;
2844
0
    valuesBuf[1]=0;
2845
0
    
2846
0
    while((locale = uenum_next(locs, &locLen, status))) {
2847
0
        UResourceBundle   *bund = NULL;
2848
0
        UResourceBundle   *subPtr = NULL;
2849
0
        UErrorCode subStatus = U_ZERO_ERROR; /* don't fail if a bundle is unopenable */
2850
0
        bund = ures_openDirect(path, locale, &subStatus);
2851
0
        
2852
#if defined(URES_TREE_DEBUG)
2853
        if(!bund || U_FAILURE(subStatus)) {
2854
            fprintf(stderr, "%s-%s values: Can't open %s locale - skipping. (%s)\n", 
2855
                path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2856
        }
2857
#endif
2858
        
2859
0
        ures_getByKey(bund, keyword, &item, &subStatus);
2860
0
        
2861
0
        if(!bund || U_FAILURE(subStatus)) {
2862
#if defined(URES_TREE_DEBUG)
2863
            fprintf(stderr, "%s-%s values: Can't find in %s - skipping. (%s)\n", 
2864
                path?path:"<ICUDATA>", keyword, locale, u_errorName(subStatus));
2865
#endif
2866
0
            ures_close(bund);
2867
0
            bund = NULL;
2868
0
            continue;
2869
0
        }
2870
0
        
2871
0
        while((subPtr = ures_getNextResource(&item,&subItem,&subStatus))
2872
0
            && U_SUCCESS(subStatus)) {
2873
0
            const char *k;
2874
0
            int32_t i;
2875
0
            k = ures_getKey(subPtr);
2876
0
2877
#if defined(URES_TREE_DEBUG)
2878
            /* fprintf(stderr, "%s | %s | %s | %s\n", path?path:"<ICUDATA>", keyword, locale, k); */
2879
#endif
2880
0
            if(k == NULL || *k == 0 ||
2881
0
                    uprv_strcmp(k, DEFAULT_TAG) == 0 || uprv_strncmp(k, "private-", 8) == 0) {
2882
0
                // empty or "default" or unlisted type
2883
0
                continue;
2884
0
            }
2885
0
            for(i=0; i<valuesCount; i++) {
2886
0
                if(!uprv_strcmp(valuesList[i],k)) {
2887
0
                    k = NULL; /* found duplicate */
2888
0
                    break;
2889
0
                }
2890
0
            }
2891
0
            if(k != NULL) {
2892
0
                int32_t kLen = (int32_t)uprv_strlen(k);
2893
0
                if((valuesCount >= (VALUES_LIST_SIZE-1)) ||       /* no more space in list .. */
2894
0
                    ((valuesIndex+kLen+1+1) >= VALUES_BUF_SIZE)) { /* no more space in buffer (string + 2 nulls) */
2895
0
                    *status = U_ILLEGAL_ARGUMENT_ERROR; /* out of space.. */
2896
0
                } else {
2897
0
                    uprv_strcpy(valuesBuf+valuesIndex, k);
2898
0
                    valuesList[valuesCount++] = valuesBuf+valuesIndex;
2899
0
                    valuesIndex += kLen;
2900
#if defined(URES_TREE_DEBUG)
2901
                    fprintf(stderr, "%s | %s | %s | [%s]   (UNIQUE)\n",
2902
                        path?path:"<ICUDATA>", keyword, locale, k);
2903
#endif
2904
                    valuesBuf[valuesIndex++] = 0; /* terminate */
2905
0
                }
2906
0
            }
2907
0
        }
2908
0
        ures_close(bund);
2909
0
    }
2910
0
    valuesBuf[valuesIndex++] = 0; /* terminate */
2911
0
    
2912
0
    ures_close(&item);
2913
0
    ures_close(&subItem);
2914
0
    uenum_close(locs);
2915
#if defined(URES_TREE_DEBUG)
2916
    fprintf(stderr, "%s:  size %d, #%d\n", u_errorName(*status), 
2917
        valuesIndex, valuesCount);
2918
#endif
2919
0
    return uloc_openKeywordList(valuesBuf, valuesIndex, status);
2920
0
}
2921
#if 0
2922
/* This code isn't needed, and given the documentation warnings the implementation is suspect */
2923
U_INTERNAL UBool U_EXPORT2
2924
ures_equal(const UResourceBundle* res1, const UResourceBundle* res2){
2925
    if(res1==NULL || res2==NULL){
2926
        return res1==res2; /* pointer comparision */
2927
    }
2928
    if(res1->fKey==NULL||  res2->fKey==NULL){
2929
        return (res1->fKey==res2->fKey);
2930
    }else{
2931
        if(uprv_strcmp(res1->fKey, res2->fKey)!=0){
2932
            return FALSE;
2933
        }
2934
    }
2935
    if(uprv_strcmp(res1->fData->fName, res2->fData->fName)!=0){
2936
        return FALSE;
2937
    }
2938
    if(res1->fData->fPath == NULL||  res2->fData->fPath==NULL){
2939
        return (res1->fData->fPath == res2->fData->fPath);
2940
    }else{
2941
        if(uprv_strcmp(res1->fData->fPath, res2->fData->fPath)!=0){
2942
            return FALSE;
2943
        }
2944
    }
2945
    if(uprv_strcmp(res1->fData->fParent->fName, res2->fData->fParent->fName)!=0){
2946
        return FALSE;
2947
    }
2948
    if(uprv_strcmp(res1->fData->fParent->fPath, res2->fData->fParent->fPath)!=0){
2949
        return FALSE;
2950
    }
2951
    if(uprv_strncmp(res1->fResPath, res2->fResPath, res1->fResPathLen)!=0){
2952
        return FALSE;
2953
    }
2954
    if(res1->fRes != res2->fRes){
2955
        return FALSE;
2956
    }
2957
    return TRUE;
2958
}
2959
U_INTERNAL UResourceBundle* U_EXPORT2
2960
ures_clone(const UResourceBundle* res, UErrorCode* status){
2961
    UResourceBundle* bundle = NULL;
2962
    UResourceBundle* ret = NULL;
2963
    if(U_FAILURE(*status) || res == NULL){
2964
        return NULL;
2965
    }
2966
    bundle = ures_open(res->fData->fPath, res->fData->fName, status);
2967
    if(res->fResPath!=NULL){
2968
        ret = ures_findSubResource(bundle, res->fResPath, NULL, status);
2969
        ures_close(bundle);
2970
    }else{
2971
        ret = bundle;
2972
    }
2973
    return ret;
2974
}
2975
U_INTERNAL const UResourceBundle* U_EXPORT2
2976
ures_getParentBundle(const UResourceBundle* res){
2977
    if(res==NULL){
2978
        return NULL;
2979
    }
2980
    return res->fParentRes;
2981
}
2982
#endif
2983
2984
U_INTERNAL void U_EXPORT2
2985
0
ures_getVersionByKey(const UResourceBundle* res, const char *key, UVersionInfo ver, UErrorCode *status) {
2986
0
  const UChar *str;
2987
0
  int32_t len;
2988
0
  str = ures_getStringByKey(res, key, &len, status);
2989
0
  if(U_SUCCESS(*status)) {
2990
0
    u_versionFromUString(ver, str);
2991
0
  } 
2992
0
}
2993
2994
/* eof */