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