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