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