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