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