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