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