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