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