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