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