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