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