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