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