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