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