Bug Summary

File:root/firefox-clang/intl/icu/source/common/uresbund.cpp
Warning:line 1940, column 12
2nd function call argument is an uninitialized value

Annotated Source Code

Press '?' to see keyboard shortcuts

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