intl/icu/source/common/uresbund.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

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

mercurial