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