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