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