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