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