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