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