1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 **********************************************************************
5 * Copyright (c) 2002-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
8 */
9
10 #include "unicode/utypes.h"
11
12 #if !UCONFIG_NO_FORMATTING
13
14 #include <utility>
15
16 #include "unicode/ucurr.h"
17 #include "unicode/locid.h"
18 #include "unicode/ures.h"
19 #include "unicode/ustring.h"
20 #include "unicode/parsepos.h"
21 #include "unicode/uniset.h"
22 #include "unicode/usetiter.h"
23 #include "unicode/utf16.h"
24 #include "ustr_imp.h"
25 #include "charstr.h"
26 #include "cmemory.h"
27 #include "cstring.h"
28 #include "static_unicode_sets.h"
29 #include "uassert.h"
30 #include "umutex.h"
31 #include "ucln_cmn.h"
32 #include "uenumimp.h"
33 #include "uhash.h"
34 #include "hash.h"
35 #include "uinvchar.h"
36 #include "uresimp.h"
37 #include "ulist.h"
38 #include "uresimp.h"
39 #include "ureslocs.h"
40 #include "ulocimp.h"
41
42 using namespace icu;
43
44 //#define UCURR_DEBUG_EQUIV 1
45 #ifdef UCURR_DEBUG_EQUIV
46 #include "stdio.h"
47 #endif
48 //#define UCURR_DEBUG 1
49 #ifdef UCURR_DEBUG
50 #include "stdio.h"
51 #endif
52
53 typedef struct IsoCodeEntry {
54 const char16_t *isoCode; /* const because it's a reference to a resource bundle string. */
55 UDate from;
56 UDate to;
57 } IsoCodeEntry;
58
59 //------------------------------------------------------------
60 // Constants
61
62 // Default currency meta data of last resort. We try to use the
63 // defaults encoded in the meta data resource bundle. If there is a
64 // configuration/build error and these are not available, we use these
65 // hard-coded defaults (which should be identical).
66 static const int32_t LAST_RESORT_DATA[] = { 2, 0, 2, 0 };
67
68 // POW10[i] = 10^i, i=0..MAX_POW10
69 static const int32_t POW10[] = { 1, 10, 100, 1000, 10000, 100000,
70 1000000, 10000000, 100000000, 1000000000 };
71
72 static const int32_t MAX_POW10 = UPRV_LENGTHOF(POW10) - 1;
73
74 #define ISO_CURRENCY_CODE_LENGTH 3
75
76 //------------------------------------------------------------
77 // Resource tags
78 //
79
80 static const char CURRENCY_DATA[] = "supplementalData";
81 // Tag for meta-data, in root.
82 static const char CURRENCY_META[] = "CurrencyMeta";
83
84 // Tag for map from countries to currencies, in root.
85 static const char CURRENCY_MAP[] = "CurrencyMap";
86
87 // Tag for default meta-data, in CURRENCY_META
88 static const char DEFAULT_META[] = "DEFAULT";
89
90 // Variant delimiter
91 static const char VAR_DELIM = '_';
92
93 // Tag for localized display names (symbols) of currencies
94 static const char CURRENCIES[] = "Currencies";
95 static const char CURRENCIES_NARROW[] = "Currencies%narrow";
96 static const char CURRENCIES_FORMAL[] = "Currencies%formal";
97 static const char CURRENCIES_VARIANT[] = "Currencies%variant";
98 static const char CURRENCYPLURALS[] = "CurrencyPlurals";
99
100 // ISO codes mapping table
101 static const UHashtable* gIsoCodes = nullptr;
102 static icu::UInitOnce gIsoCodesInitOnce {};
103
104 // Currency symbol equivalances
105 static const icu::Hashtable* gCurrSymbolsEquiv = nullptr;
106 static icu::UInitOnce gCurrSymbolsEquivInitOnce {};
107
108 U_NAMESPACE_BEGIN
109
110 // EquivIterator iterates over all strings that are equivalent to a given
111 // string, s. Note that EquivIterator will never yield s itself.
112 class EquivIterator : public icu::UMemory {
113 public:
114 // Constructor. hash stores the equivalence relationships; s is the string
115 // for which we find equivalent strings.
EquivIterator(const icu::Hashtable & hash,const icu::UnicodeString & s)116 inline EquivIterator(const icu::Hashtable& hash, const icu::UnicodeString& s)
117 : _hash(hash) {
118 _start = _current = &s;
119 }
~EquivIterator()120 inline ~EquivIterator() { }
121
122 // next returns the next equivalent string or nullptr if there are no more.
123 // If s has no equivalent strings, next returns nullptr on the first call.
124 const icu::UnicodeString *next();
125 private:
126 const icu::Hashtable& _hash;
127 const icu::UnicodeString* _start;
128 const icu::UnicodeString* _current;
129 };
130
131 const icu::UnicodeString *
next()132 EquivIterator::next() {
133 const icu::UnicodeString* _next = (const icu::UnicodeString*) _hash.get(*_current);
134 if (_next == nullptr) {
135 U_ASSERT(_current == _start);
136 return nullptr;
137 }
138 if (*_next == *_start) {
139 return nullptr;
140 }
141 _current = _next;
142 return _next;
143 }
144
145 U_NAMESPACE_END
146
147 // makeEquivalent makes lhs and rhs equivalent by updating the equivalence
148 // relations in hash accordingly.
makeEquivalent(const icu::UnicodeString & lhs,const icu::UnicodeString & rhs,icu::Hashtable * hash,UErrorCode & status)149 static void makeEquivalent(
150 const icu::UnicodeString &lhs,
151 const icu::UnicodeString &rhs,
152 icu::Hashtable* hash, UErrorCode &status) {
153 if (U_FAILURE(status)) {
154 return;
155 }
156 if (lhs == rhs) {
157 // already equivalent
158 return;
159 }
160 icu::EquivIterator leftIter(*hash, lhs);
161 icu::EquivIterator rightIter(*hash, rhs);
162 const icu::UnicodeString *firstLeft = leftIter.next();
163 const icu::UnicodeString *firstRight = rightIter.next();
164 const icu::UnicodeString *nextLeft = firstLeft;
165 const icu::UnicodeString *nextRight = firstRight;
166 while (nextLeft != nullptr && nextRight != nullptr) {
167 if (*nextLeft == rhs || *nextRight == lhs) {
168 // Already equivalent
169 return;
170 }
171 nextLeft = leftIter.next();
172 nextRight = rightIter.next();
173 }
174 // Not equivalent. Must join.
175 icu::UnicodeString *newFirstLeft;
176 icu::UnicodeString *newFirstRight;
177 if (firstRight == nullptr && firstLeft == nullptr) {
178 // Neither lhs or rhs belong to an equivalence circle, so we form
179 // a new equivalnce circle of just lhs and rhs.
180 newFirstLeft = new icu::UnicodeString(rhs);
181 newFirstRight = new icu::UnicodeString(lhs);
182 } else if (firstRight == nullptr) {
183 // lhs belongs to an equivalence circle, but rhs does not, so we link
184 // rhs into lhs' circle.
185 newFirstLeft = new icu::UnicodeString(rhs);
186 newFirstRight = new icu::UnicodeString(*firstLeft);
187 } else if (firstLeft == nullptr) {
188 // rhs belongs to an equivlance circle, but lhs does not, so we link
189 // lhs into rhs' circle.
190 newFirstLeft = new icu::UnicodeString(*firstRight);
191 newFirstRight = new icu::UnicodeString(lhs);
192 } else {
193 // Both lhs and rhs belong to different equivalnce circles. We link
194 // them together to form one single, larger equivalnce circle.
195 newFirstLeft = new icu::UnicodeString(*firstRight);
196 newFirstRight = new icu::UnicodeString(*firstLeft);
197 }
198 if (newFirstLeft == nullptr || newFirstRight == nullptr) {
199 delete newFirstLeft;
200 delete newFirstRight;
201 status = U_MEMORY_ALLOCATION_ERROR;
202 return;
203 }
204 hash->put(lhs, (void *) newFirstLeft, status);
205 hash->put(rhs, (void *) newFirstRight, status);
206 }
207
208 // countEquivalent counts how many strings are equivalent to s.
209 // hash stores all the equivalnce relations.
210 // countEquivalent does not include s itself in the count.
countEquivalent(const icu::Hashtable & hash,const icu::UnicodeString & s)211 static int32_t countEquivalent(const icu::Hashtable &hash, const icu::UnicodeString &s) {
212 int32_t result = 0;
213 icu::EquivIterator iter(hash, s);
214 while (iter.next() != nullptr) {
215 ++result;
216 }
217 #ifdef UCURR_DEBUG_EQUIV
218 {
219 char tmp[200];
220 s.extract(0,s.length(),tmp, "UTF-8");
221 printf("CountEquivalent('%s') = %d\n", tmp, result);
222 }
223 #endif
224 return result;
225 }
226
227 static const icu::Hashtable* getCurrSymbolsEquiv();
228
229 //------------------------------------------------------------
230 // Code
231
232 /**
233 * Cleanup callback func
234 */
235 static UBool U_CALLCONV
isoCodes_cleanup()236 isoCodes_cleanup()
237 {
238 if (gIsoCodes != nullptr) {
239 uhash_close(const_cast<UHashtable *>(gIsoCodes));
240 gIsoCodes = nullptr;
241 }
242 gIsoCodesInitOnce.reset();
243 return true;
244 }
245
246 /**
247 * Cleanup callback func
248 */
249 static UBool U_CALLCONV
currSymbolsEquiv_cleanup()250 currSymbolsEquiv_cleanup()
251 {
252 delete const_cast<icu::Hashtable *>(gCurrSymbolsEquiv);
253 gCurrSymbolsEquiv = nullptr;
254 gCurrSymbolsEquivInitOnce.reset();
255 return true;
256 }
257
258 /**
259 * Deleter for IsoCodeEntry
260 */
261 static void U_CALLCONV
deleteIsoCodeEntry(void * obj)262 deleteIsoCodeEntry(void *obj) {
263 IsoCodeEntry *entry = (IsoCodeEntry*)obj;
264 uprv_free(entry);
265 }
266
267 /**
268 * Deleter for gCurrSymbolsEquiv.
269 */
270 static void U_CALLCONV
deleteUnicode(void * obj)271 deleteUnicode(void *obj) {
272 icu::UnicodeString *entry = (icu::UnicodeString*)obj;
273 delete entry;
274 }
275
276 /**
277 * Unfortunately, we have to convert the char16_t* currency code to char*
278 * to use it as a resource key.
279 */
280 static inline char*
myUCharsToChars(char * resultOfLen4,const char16_t * currency)281 myUCharsToChars(char* resultOfLen4, const char16_t* currency) {
282 u_UCharsToChars(currency, resultOfLen4, ISO_CURRENCY_CODE_LENGTH);
283 resultOfLen4[ISO_CURRENCY_CODE_LENGTH] = 0;
284 return resultOfLen4;
285 }
286
287 /**
288 * Internal function to look up currency data. Result is an array of
289 * four integers. The first is the fraction digits. The second is the
290 * rounding increment, or 0 if none. The rounding increment is in
291 * units of 10^(-fraction_digits). The third and fourth are the same
292 * except that they are those used in cash transactions ( cashDigits
293 * and cashRounding ).
294 */
295 static const int32_t*
_findMetaData(const char16_t * currency,UErrorCode & ec)296 _findMetaData(const char16_t* currency, UErrorCode& ec) {
297
298 if (currency == nullptr || *currency == 0) {
299 if (U_SUCCESS(ec)) {
300 ec = U_ILLEGAL_ARGUMENT_ERROR;
301 }
302 return LAST_RESORT_DATA;
303 }
304
305 // Get CurrencyMeta resource out of root locale file. [This may
306 // move out of the root locale file later; if it does, update this
307 // code.]
308 UResourceBundle* currencyData = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &ec);
309 UResourceBundle* currencyMeta = ures_getByKey(currencyData, CURRENCY_META, currencyData, &ec);
310
311 if (U_FAILURE(ec)) {
312 ures_close(currencyMeta);
313 // Config/build error; return hard-coded defaults
314 return LAST_RESORT_DATA;
315 }
316
317 // Look up our currency, or if that's not available, then DEFAULT
318 char buf[ISO_CURRENCY_CODE_LENGTH+1];
319 UErrorCode ec2 = U_ZERO_ERROR; // local error code: soft failure
320 UResourceBundle* rb = ures_getByKey(currencyMeta, myUCharsToChars(buf, currency), nullptr, &ec2);
321 if (U_FAILURE(ec2)) {
322 ures_close(rb);
323 rb = ures_getByKey(currencyMeta,DEFAULT_META, nullptr, &ec);
324 if (U_FAILURE(ec)) {
325 ures_close(currencyMeta);
326 ures_close(rb);
327 // Config/build error; return hard-coded defaults
328 return LAST_RESORT_DATA;
329 }
330 }
331
332 int32_t len;
333 const int32_t *data = ures_getIntVector(rb, &len, &ec);
334 if (U_FAILURE(ec) || len != 4) {
335 // Config/build error; return hard-coded defaults
336 if (U_SUCCESS(ec)) {
337 ec = U_INVALID_FORMAT_ERROR;
338 }
339 ures_close(currencyMeta);
340 ures_close(rb);
341 return LAST_RESORT_DATA;
342 }
343
344 ures_close(currencyMeta);
345 ures_close(rb);
346 return data;
347 }
348
349 // -------------------------------------
350
351 static CharString
idForLocale(const char * locale,UErrorCode * ec)352 idForLocale(const char* locale, UErrorCode* ec)
353 {
354 return ulocimp_getRegionForSupplementalData(locale, false, *ec);
355 }
356
357 // ------------------------------------------
358 //
359 // Registration
360 //
361 //-------------------------------------------
362
363 // don't use ICUService since we don't need fallback
364
365 U_CDECL_BEGIN
366 static UBool U_CALLCONV currency_cleanup();
367 U_CDECL_END
368
369 #if !UCONFIG_NO_SERVICE
370 struct CReg;
371
372 static UMutex gCRegLock;
373 static CReg* gCRegHead = nullptr;
374
375 struct CReg : public icu::UMemory {
376 CReg *next;
377 char16_t iso[ISO_CURRENCY_CODE_LENGTH+1];
378 char id[ULOC_FULLNAME_CAPACITY];
379
CRegCReg380 CReg(const char16_t* _iso, const char* _id)
381 : next(nullptr)
382 {
383 int32_t len = (int32_t)uprv_strlen(_id);
384 if (len > (int32_t)(sizeof(id)-1)) {
385 len = (sizeof(id)-1);
386 }
387 uprv_strncpy(id, _id, len);
388 id[len] = 0;
389 u_memcpy(iso, _iso, ISO_CURRENCY_CODE_LENGTH);
390 iso[ISO_CURRENCY_CODE_LENGTH] = 0;
391 }
392
regCReg393 static UCurrRegistryKey reg(const char16_t* _iso, const char* _id, UErrorCode* status)
394 {
395 if (status && U_SUCCESS(*status) && _iso && _id) {
396 CReg* n = new CReg(_iso, _id);
397 if (n) {
398 umtx_lock(&gCRegLock);
399 if (!gCRegHead) {
400 /* register for the first time */
401 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
402 }
403 n->next = gCRegHead;
404 gCRegHead = n;
405 umtx_unlock(&gCRegLock);
406 return n;
407 }
408 *status = U_MEMORY_ALLOCATION_ERROR;
409 }
410 return nullptr;
411 }
412
unregCReg413 static UBool unreg(UCurrRegistryKey key) {
414 UBool found = false;
415 umtx_lock(&gCRegLock);
416
417 CReg** p = &gCRegHead;
418 while (*p) {
419 if (*p == key) {
420 *p = ((CReg*)key)->next;
421 delete (CReg*)key;
422 found = true;
423 break;
424 }
425 p = &((*p)->next);
426 }
427
428 umtx_unlock(&gCRegLock);
429 return found;
430 }
431
getCReg432 static const char16_t* get(const char* id) {
433 const char16_t* result = nullptr;
434 umtx_lock(&gCRegLock);
435 CReg* p = gCRegHead;
436
437 /* register cleanup of the mutex */
438 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
439 while (p) {
440 if (uprv_strcmp(id, p->id) == 0) {
441 result = p->iso;
442 break;
443 }
444 p = p->next;
445 }
446 umtx_unlock(&gCRegLock);
447 return result;
448 }
449
450 /* This doesn't need to be thread safe. It's for u_cleanup only. */
cleanupCReg451 static void cleanup() {
452 while (gCRegHead) {
453 CReg* n = gCRegHead;
454 gCRegHead = gCRegHead->next;
455 delete n;
456 }
457 }
458 };
459
460 // -------------------------------------
461
462 U_CAPI UCurrRegistryKey U_EXPORT2
ucurr_register(const char16_t * isoCode,const char * locale,UErrorCode * status)463 ucurr_register(const char16_t* isoCode, const char* locale, UErrorCode *status)
464 {
465 if (status && U_SUCCESS(*status)) {
466 CharString id = idForLocale(locale, status);
467 return CReg::reg(isoCode, id.data(), status);
468 }
469 return nullptr;
470 }
471
472 // -------------------------------------
473
474 U_CAPI UBool U_EXPORT2
ucurr_unregister(UCurrRegistryKey key,UErrorCode * status)475 ucurr_unregister(UCurrRegistryKey key, UErrorCode* status)
476 {
477 if (status && U_SUCCESS(*status)) {
478 return CReg::unreg(key);
479 }
480 return false;
481 }
482 #endif /* UCONFIG_NO_SERVICE */
483
484 // -------------------------------------
485
486 /**
487 * Release all static memory held by currency.
488 */
489 /*The declaration here is needed so currency_cleanup()
490 * can call this function.
491 */
492 static UBool U_CALLCONV
493 currency_cache_cleanup();
494
495 U_CDECL_BEGIN
currency_cleanup()496 static UBool U_CALLCONV currency_cleanup() {
497 #if !UCONFIG_NO_SERVICE
498 CReg::cleanup();
499 #endif
500 /*
501 * There might be some cached currency data or isoCodes data.
502 */
503 currency_cache_cleanup();
504 isoCodes_cleanup();
505 currSymbolsEquiv_cleanup();
506
507 return true;
508 }
509 U_CDECL_END
510
511 // -------------------------------------
512
513 U_CAPI int32_t U_EXPORT2
ucurr_forLocale(const char * locale,char16_t * buff,int32_t buffCapacity,UErrorCode * ec)514 ucurr_forLocale(const char* locale,
515 char16_t* buff,
516 int32_t buffCapacity,
517 UErrorCode* ec) {
518 if (U_FAILURE(*ec)) { return 0; }
519 if (buffCapacity < 0 || (buff == nullptr && buffCapacity > 0)) {
520 *ec = U_ILLEGAL_ARGUMENT_ERROR;
521 return 0;
522 }
523
524 UErrorCode localStatus = U_ZERO_ERROR;
525 CharString currency = ulocimp_getKeywordValue(locale, "currency", localStatus);
526 int32_t resLen = currency.length();
527
528 if (U_SUCCESS(localStatus) && resLen == 3 && uprv_isInvariantString(currency.data(), resLen)) {
529 if (resLen < buffCapacity) {
530 T_CString_toUpperCase(currency.data());
531 u_charsToUChars(currency.data(), buff, resLen);
532 }
533 return u_terminateUChars(buff, buffCapacity, resLen, ec);
534 }
535
536 // get country or country_variant in `id'
537 CharString id = idForLocale(locale, ec);
538 if (U_FAILURE(*ec)) {
539 return 0;
540 }
541
542 #if !UCONFIG_NO_SERVICE
543 const char16_t* result = CReg::get(id.data());
544 if (result) {
545 if(buffCapacity > u_strlen(result)) {
546 u_strcpy(buff, result);
547 }
548 resLen = u_strlen(result);
549 return u_terminateUChars(buff, buffCapacity, resLen, ec);
550 }
551 #endif
552 // Remove variants, which is only needed for registration.
553 char *idDelim = uprv_strchr(id.data(), VAR_DELIM);
554 if (idDelim) {
555 id.truncate(idDelim - id.data());
556 }
557
558 const char16_t* s = nullptr; // Currency code from data file.
559 if (id.isEmpty()) {
560 // No point looking in the data for an empty string.
561 // This is what we would get.
562 localStatus = U_MISSING_RESOURCE_ERROR;
563 } else {
564 // Look up the CurrencyMap element in the root bundle.
565 localStatus = U_ZERO_ERROR;
566 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
567 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
568 UResourceBundle *countryArray = ures_getByKey(rb, id.data(), cm, &localStatus);
569 // https://unicode-org.atlassian.net/browse/ICU-21997
570 // Prefer to use currencies that are legal tender.
571 if (U_SUCCESS(localStatus)) {
572 int32_t arrayLength = ures_getSize(countryArray);
573 for (int32_t i = 0; i < arrayLength; ++i) {
574 LocalUResourceBundlePointer currencyReq(
575 ures_getByIndex(countryArray, i, nullptr, &localStatus));
576 // The currency is legal tender if it is *not* marked with tender{"false"}.
577 UErrorCode tenderStatus = localStatus;
578 const char16_t *tender =
579 ures_getStringByKey(currencyReq.getAlias(), "tender", nullptr, &tenderStatus);
580 bool isTender = U_FAILURE(tenderStatus) || u_strcmp(tender, u"false") != 0;
581 if (!isTender && s != nullptr) {
582 // We already have a non-tender currency. Ignore all following non-tender ones.
583 continue;
584 }
585 // Fetch the currency code.
586 s = ures_getStringByKey(currencyReq.getAlias(), "id", &resLen, &localStatus);
587 if (isTender) {
588 break;
589 }
590 }
591 if (U_SUCCESS(localStatus) && s == nullptr) {
592 localStatus = U_MISSING_RESOURCE_ERROR;
593 }
594 }
595 ures_close(countryArray);
596 }
597
598 if ((U_FAILURE(localStatus)) && strchr(id.data(), '_') != nullptr) {
599 // We don't know about it. Check to see if we support the variant.
600 CharString parent = ulocimp_getParent(locale, *ec);
601 *ec = U_USING_FALLBACK_WARNING;
602 // TODO: Loop over the parent rather than recursing and
603 // looking again for a currency keyword.
604 return ucurr_forLocale(parent.data(), buff, buffCapacity, ec);
605 }
606 if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR) {
607 // There is nothing to fallback to. Report the failure/warning if possible.
608 *ec = localStatus;
609 }
610 if (U_SUCCESS(*ec)) {
611 if(buffCapacity > resLen) {
612 u_strcpy(buff, s);
613 }
614 }
615 return u_terminateUChars(buff, buffCapacity, resLen, ec);
616 }
617
618 // end registration
619
620 /**
621 * Modify the given locale name by removing the rightmost _-delimited
622 * element. If there is none, empty the string ("" == root).
623 * NOTE: The string "root" is not recognized; do not use it.
624 * @return true if the fallback happened; false if locale is already
625 * root ("").
626 */
fallback(CharString & loc)627 static UBool fallback(CharString& loc) {
628 if (loc.isEmpty()) {
629 return false;
630 }
631 UErrorCode status = U_ZERO_ERROR;
632 if (loc == "en_GB") {
633 // HACK: See #13368. We need "en_GB" to fall back to "en_001" instead of "en"
634 // in order to consume the correct data strings. This hack will be removed
635 // when proper data sink loading is implemented here.
636 loc.truncate(3);
637 loc.append("001", status);
638 } else {
639 loc = ulocimp_getParent(loc.data(), status);
640 }
641 /*
642 char *i = uprv_strrchr(loc, '_');
643 if (i == nullptr) {
644 i = loc;
645 }
646 *i = 0;
647 */
648 return true;
649 }
650
651
652 U_CAPI const char16_t* U_EXPORT2
ucurr_getName(const char16_t * currency,const char * locale,UCurrNameStyle nameStyle,UBool * isChoiceFormat,int32_t * len,UErrorCode * ec)653 ucurr_getName(const char16_t* currency,
654 const char* locale,
655 UCurrNameStyle nameStyle,
656 UBool* isChoiceFormat, // fillin
657 int32_t* len, // fillin
658 UErrorCode* ec) {
659
660 // Look up the Currencies resource for the given locale. The
661 // Currencies locale data looks like this:
662 //|en {
663 //| Currencies {
664 //| USD { "US$", "US Dollar" }
665 //| CHF { "Sw F", "Swiss Franc" }
666 //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" }
667 //| //...
668 //| }
669 //|}
670
671 if (U_FAILURE(*ec)) {
672 return nullptr;
673 }
674
675 int32_t choice = (int32_t) nameStyle;
676 if (choice < 0 || choice > 4) {
677 *ec = U_ILLEGAL_ARGUMENT_ERROR;
678 return nullptr;
679 }
680
681 // In the future, resource bundles may implement multi-level
682 // fallback. That is, if a currency is not found in the en_US
683 // Currencies data, then the en Currencies data will be searched.
684 // Currently, if a Currencies datum exists in en_US and en, the
685 // en_US entry hides that in en.
686
687 // We want multi-level fallback for this resource, so we implement
688 // it manually.
689
690 // Use a separate UErrorCode here that does not propagate out of
691 // this function.
692 UErrorCode ec2 = U_ZERO_ERROR;
693
694 CharString loc = ulocimp_getName(locale, ec2);
695 if (U_FAILURE(ec2)) {
696 *ec = U_ILLEGAL_ARGUMENT_ERROR;
697 return nullptr;
698 }
699
700 char buf[ISO_CURRENCY_CODE_LENGTH+1];
701 myUCharsToChars(buf, currency);
702
703 /* Normalize the keyword value to uppercase */
704 T_CString_toUpperCase(buf);
705
706 const char16_t* s = nullptr;
707 ec2 = U_ZERO_ERROR;
708 LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc.data(), &ec2));
709
710 if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) {
711 CharString key;
712 switch (nameStyle) {
713 case UCURR_NARROW_SYMBOL_NAME:
714 key.append(CURRENCIES_NARROW, ec2);
715 break;
716 case UCURR_FORMAL_SYMBOL_NAME:
717 key.append(CURRENCIES_FORMAL, ec2);
718 break;
719 case UCURR_VARIANT_SYMBOL_NAME:
720 key.append(CURRENCIES_VARIANT, ec2);
721 break;
722 default:
723 *ec = U_UNSUPPORTED_ERROR;
724 return nullptr;
725 }
726 key.append("/", ec2);
727 key.append(buf, ec2);
728 s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2);
729 if (ec2 == U_MISSING_RESOURCE_ERROR) {
730 *ec = U_USING_FALLBACK_WARNING;
731 ec2 = U_ZERO_ERROR;
732 choice = UCURR_SYMBOL_NAME;
733 }
734 }
735 if (s == nullptr) {
736 ures_getByKey(rb.getAlias(), CURRENCIES, rb.getAlias(), &ec2);
737 ures_getByKeyWithFallback(rb.getAlias(), buf, rb.getAlias(), &ec2);
738 s = ures_getStringByIndex(rb.getAlias(), choice, len, &ec2);
739 }
740
741 // If we've succeeded we're done. Otherwise, try to fallback.
742 // If that fails (because we are already at root) then exit.
743 if (U_SUCCESS(ec2)) {
744 if (ec2 == U_USING_DEFAULT_WARNING
745 || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
746 *ec = ec2;
747 }
748 }
749
750 // We no longer support choice format data in names. Data should not contain
751 // choice patterns.
752 if (isChoiceFormat != nullptr) {
753 *isChoiceFormat = false;
754 }
755 if (U_SUCCESS(ec2)) {
756 U_ASSERT(s != nullptr);
757 return s;
758 }
759
760 // If we fail to find a match, use the ISO 4217 code
761 *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
762 *ec = U_USING_DEFAULT_WARNING;
763 return currency;
764 }
765
766 U_CAPI const char16_t* U_EXPORT2
ucurr_getPluralName(const char16_t * currency,const char * locale,UBool * isChoiceFormat,const char * pluralCount,int32_t * len,UErrorCode * ec)767 ucurr_getPluralName(const char16_t* currency,
768 const char* locale,
769 UBool* isChoiceFormat,
770 const char* pluralCount,
771 int32_t* len, // fillin
772 UErrorCode* ec) {
773 // Look up the Currencies resource for the given locale. The
774 // Currencies locale data looks like this:
775 //|en {
776 //| CurrencyPlurals {
777 //| USD{
778 //| one{"US dollar"}
779 //| other{"US dollars"}
780 //| }
781 //| }
782 //|}
783
784 if (U_FAILURE(*ec)) {
785 return nullptr;
786 }
787
788 // Use a separate UErrorCode here that does not propagate out of
789 // this function.
790 UErrorCode ec2 = U_ZERO_ERROR;
791
792 CharString loc = ulocimp_getName(locale, ec2);
793 if (U_FAILURE(ec2)) {
794 *ec = U_ILLEGAL_ARGUMENT_ERROR;
795 return nullptr;
796 }
797
798 char buf[ISO_CURRENCY_CODE_LENGTH+1];
799 myUCharsToChars(buf, currency);
800
801 const char16_t* s = nullptr;
802 ec2 = U_ZERO_ERROR;
803 UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc.data(), &ec2);
804
805 rb = ures_getByKey(rb, CURRENCYPLURALS, rb, &ec2);
806
807 // Fetch resource with multi-level resource inheritance fallback
808 rb = ures_getByKeyWithFallback(rb, buf, rb, &ec2);
809
810 s = ures_getStringByKeyWithFallback(rb, pluralCount, len, &ec2);
811 if (U_FAILURE(ec2)) {
812 // fall back to "other"
813 ec2 = U_ZERO_ERROR;
814 s = ures_getStringByKeyWithFallback(rb, "other", len, &ec2);
815 if (U_FAILURE(ec2)) {
816 ures_close(rb);
817 // fall back to long name in Currencies
818 return ucurr_getName(currency, locale, UCURR_LONG_NAME,
819 isChoiceFormat, len, ec);
820 }
821 }
822 ures_close(rb);
823
824 // If we've succeeded we're done. Otherwise, try to fallback.
825 // If that fails (because we are already at root) then exit.
826 if (U_SUCCESS(ec2)) {
827 if (ec2 == U_USING_DEFAULT_WARNING
828 || (ec2 == U_USING_FALLBACK_WARNING && *ec != U_USING_DEFAULT_WARNING)) {
829 *ec = ec2;
830 }
831 U_ASSERT(s != nullptr);
832 return s;
833 }
834
835 // If we fail to find a match, use the ISO 4217 code
836 *len = u_strlen(currency); // Should == ISO_CURRENCY_CODE_LENGTH, but maybe not...?
837 *ec = U_USING_DEFAULT_WARNING;
838 return currency;
839 }
840
841
842 //========================================================================
843 // Following are structure and function for parsing currency names
844
845 #define NEED_TO_BE_DELETED 0x1
846
847 // TODO: a better way to define this?
848 #define MAX_CURRENCY_NAME_LEN 100
849
850 typedef struct {
851 const char* IsoCode; // key
852 char16_t* currencyName; // value
853 int32_t currencyNameLen; // value length
854 int32_t flag; // flags
855 } CurrencyNameStruct;
856
857
858 #ifndef MIN
859 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
860 #endif
861
862 #ifndef MAX
863 #define MAX(a,b) (((a)<(b)) ? (b) : (a))
864 #endif
865
866
867 // Comparison function used in quick sort.
currencyNameComparator(const void * a,const void * b)868 static int U_CALLCONV currencyNameComparator(const void* a, const void* b) {
869 const CurrencyNameStruct* currName_1 = (const CurrencyNameStruct*)a;
870 const CurrencyNameStruct* currName_2 = (const CurrencyNameStruct*)b;
871 for (int32_t i = 0;
872 i < MIN(currName_1->currencyNameLen, currName_2->currencyNameLen);
873 ++i) {
874 if (currName_1->currencyName[i] < currName_2->currencyName[i]) {
875 return -1;
876 }
877 if (currName_1->currencyName[i] > currName_2->currencyName[i]) {
878 return 1;
879 }
880 }
881 if (currName_1->currencyNameLen < currName_2->currencyNameLen) {
882 return -1;
883 } else if (currName_1->currencyNameLen > currName_2->currencyNameLen) {
884 return 1;
885 }
886 return 0;
887 }
888
889
890 // Give a locale, return the maximum number of currency names associated with
891 // this locale.
892 // It gets currency names from resource bundles using fallback.
893 // It is the maximum number because in the fallback chain, some of the
894 // currency names are duplicated.
895 // For example, given locale as "en_US", the currency names get from resource
896 // bundle in "en_US" and "en" are duplicated. The fallback mechanism will count
897 // all currency names in "en_US" and "en".
898 static void
getCurrencyNameCount(const char * loc,int32_t * total_currency_name_count,int32_t * total_currency_symbol_count)899 getCurrencyNameCount(const char* loc, int32_t* total_currency_name_count, int32_t* total_currency_symbol_count) {
900 U_NAMESPACE_USE
901 *total_currency_name_count = 0;
902 *total_currency_symbol_count = 0;
903 const char16_t* s = nullptr;
904 CharString locale;
905 {
906 UErrorCode status = U_ZERO_ERROR;
907 locale.append(loc, status);
908 if (U_FAILURE(status)) { return; }
909 }
910 const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
911 for (;;) {
912 UErrorCode ec2 = U_ZERO_ERROR;
913 // TODO: ures_openDirect?
914 UResourceBundle* rb = ures_open(U_ICUDATA_CURR, locale.data(), &ec2);
915 UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, nullptr, &ec2);
916 int32_t n = ures_getSize(curr);
917 for (int32_t i=0; i<n; ++i) {
918 UResourceBundle* names = ures_getByIndex(curr, i, nullptr, &ec2);
919 int32_t len;
920 s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
921 ++(*total_currency_symbol_count); // currency symbol
922 if (currencySymbolsEquiv != nullptr) {
923 *total_currency_symbol_count += countEquivalent(*currencySymbolsEquiv, UnicodeString(true, s, len));
924 }
925 ++(*total_currency_symbol_count); // iso code
926 ++(*total_currency_name_count); // long name
927 ures_close(names);
928 }
929
930 // currency plurals
931 UErrorCode ec3 = U_ZERO_ERROR;
932 UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, nullptr, &ec3);
933 n = ures_getSize(curr_p);
934 for (int32_t i=0; i<n; ++i) {
935 UResourceBundle* names = ures_getByIndex(curr_p, i, nullptr, &ec3);
936 *total_currency_name_count += ures_getSize(names);
937 ures_close(names);
938 }
939 ures_close(curr_p);
940 ures_close(curr);
941 ures_close(rb);
942
943 if (!fallback(locale)) {
944 break;
945 }
946 }
947 }
948
949 static char16_t*
toUpperCase(const char16_t * source,int32_t len,const char * locale)950 toUpperCase(const char16_t* source, int32_t len, const char* locale) {
951 char16_t* dest = nullptr;
952 UErrorCode ec = U_ZERO_ERROR;
953 int32_t destLen = u_strToUpper(dest, 0, source, len, locale, &ec);
954
955 ec = U_ZERO_ERROR;
956 dest = (char16_t*)uprv_malloc(sizeof(char16_t) * MAX(destLen, len));
957 u_strToUpper(dest, destLen, source, len, locale, &ec);
958 if (U_FAILURE(ec)) {
959 u_memcpy(dest, source, len);
960 }
961 return dest;
962 }
963
964
965 // Collect all available currency names associated with the given locale
966 // (enable fallback chain).
967 // Read currenc names defined in resource bundle "Currencies" and
968 // "CurrencyPlural", enable fallback chain.
969 // return the malloc-ed currency name arrays and the total number of currency
970 // names in the array.
971 static void
collectCurrencyNames(const char * locale,CurrencyNameStruct ** currencyNames,int32_t * total_currency_name_count,CurrencyNameStruct ** currencySymbols,int32_t * total_currency_symbol_count,UErrorCode & ec)972 collectCurrencyNames(const char* locale,
973 CurrencyNameStruct** currencyNames,
974 int32_t* total_currency_name_count,
975 CurrencyNameStruct** currencySymbols,
976 int32_t* total_currency_symbol_count,
977 UErrorCode& ec) {
978 U_NAMESPACE_USE
979 const icu::Hashtable *currencySymbolsEquiv = getCurrSymbolsEquiv();
980 // Look up the Currencies resource for the given locale.
981 UErrorCode ec2 = U_ZERO_ERROR;
982
983 CharString loc = ulocimp_getName(locale, ec2);
984 if (U_FAILURE(ec2)) {
985 ec = U_ILLEGAL_ARGUMENT_ERROR;
986 }
987
988 // Get maximum currency name count first.
989 getCurrencyNameCount(loc.data(), total_currency_name_count, total_currency_symbol_count);
990
991 *currencyNames = (CurrencyNameStruct*)uprv_malloc
992 (sizeof(CurrencyNameStruct) * (*total_currency_name_count));
993 *currencySymbols = (CurrencyNameStruct*)uprv_malloc
994 (sizeof(CurrencyNameStruct) * (*total_currency_symbol_count));
995
996 if(currencyNames == nullptr || currencySymbols == nullptr) {
997 ec = U_MEMORY_ALLOCATION_ERROR;
998 }
999
1000 if (U_FAILURE(ec)) return;
1001
1002 const char16_t* s = nullptr; // currency name
1003 char* iso = nullptr; // currency ISO code
1004
1005 *total_currency_name_count = 0;
1006 *total_currency_symbol_count = 0;
1007
1008 UErrorCode ec3 = U_ZERO_ERROR;
1009 UErrorCode ec4 = U_ZERO_ERROR;
1010
1011 // Using hash to remove duplicates caused by locale fallback
1012 UHashtable* currencyIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &ec3);
1013 UHashtable* currencyPluralIsoCodes = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &ec4);
1014 for (int32_t localeLevel = 0; ; ++localeLevel) {
1015 ec2 = U_ZERO_ERROR;
1016 // TODO: ures_openDirect
1017 UResourceBundle* rb = ures_open(U_ICUDATA_CURR, loc.data(), &ec2);
1018 UResourceBundle* curr = ures_getByKey(rb, CURRENCIES, nullptr, &ec2);
1019 int32_t n = ures_getSize(curr);
1020 for (int32_t i=0; i<n; ++i) {
1021 UResourceBundle* names = ures_getByIndex(curr, i, nullptr, &ec2);
1022 int32_t len;
1023 s = ures_getStringByIndex(names, UCURR_SYMBOL_NAME, &len, &ec2);
1024 // TODO: uhash_put wont change key/value?
1025 iso = (char*)ures_getKey(names);
1026 if (localeLevel == 0) {
1027 uhash_put(currencyIsoCodes, iso, iso, &ec3);
1028 } else {
1029 if (uhash_get(currencyIsoCodes, iso) != nullptr) {
1030 ures_close(names);
1031 continue;
1032 } else {
1033 uhash_put(currencyIsoCodes, iso, iso, &ec3);
1034 }
1035 }
1036 // Add currency symbol.
1037 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
1038 (*currencySymbols)[*total_currency_symbol_count].currencyName = (char16_t*)s;
1039 (*currencySymbols)[*total_currency_symbol_count].flag = 0;
1040 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = len;
1041 // Add equivalent symbols
1042 if (currencySymbolsEquiv != nullptr) {
1043 UnicodeString str(true, s, len);
1044 icu::EquivIterator iter(*currencySymbolsEquiv, str);
1045 const UnicodeString *symbol;
1046 while ((symbol = iter.next()) != nullptr) {
1047 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
1048 (*currencySymbols)[*total_currency_symbol_count].currencyName =
1049 const_cast<char16_t*>(symbol->getBuffer());
1050 (*currencySymbols)[*total_currency_symbol_count].flag = 0;
1051 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = symbol->length();
1052 }
1053 }
1054
1055 // Add currency long name.
1056 s = ures_getStringByIndex(names, UCURR_LONG_NAME, &len, &ec2);
1057 (*currencyNames)[*total_currency_name_count].IsoCode = iso;
1058 char16_t* upperName = toUpperCase(s, len, locale);
1059 (*currencyNames)[*total_currency_name_count].currencyName = upperName;
1060 (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
1061 (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
1062
1063 // put (iso, 3, and iso) in to array
1064 // Add currency ISO code.
1065 (*currencySymbols)[*total_currency_symbol_count].IsoCode = iso;
1066 (*currencySymbols)[*total_currency_symbol_count].currencyName = (char16_t*)uprv_malloc(sizeof(char16_t)*3);
1067 // Must convert iso[] into Unicode
1068 u_charsToUChars(iso, (*currencySymbols)[*total_currency_symbol_count].currencyName, 3);
1069 (*currencySymbols)[*total_currency_symbol_count].flag = NEED_TO_BE_DELETED;
1070 (*currencySymbols)[(*total_currency_symbol_count)++].currencyNameLen = 3;
1071
1072 ures_close(names);
1073 }
1074
1075 // currency plurals
1076 UErrorCode ec5 = U_ZERO_ERROR;
1077 UResourceBundle* curr_p = ures_getByKey(rb, CURRENCYPLURALS, nullptr, &ec5);
1078 n = ures_getSize(curr_p);
1079 for (int32_t i=0; i<n; ++i) {
1080 UResourceBundle* names = ures_getByIndex(curr_p, i, nullptr, &ec5);
1081 iso = (char*)ures_getKey(names);
1082 // Using hash to remove duplicated ISO codes in fallback chain.
1083 if (localeLevel == 0) {
1084 uhash_put(currencyPluralIsoCodes, iso, iso, &ec4);
1085 } else {
1086 if (uhash_get(currencyPluralIsoCodes, iso) != nullptr) {
1087 ures_close(names);
1088 continue;
1089 } else {
1090 uhash_put(currencyPluralIsoCodes, iso, iso, &ec4);
1091 }
1092 }
1093 int32_t num = ures_getSize(names);
1094 int32_t len;
1095 for (int32_t j = 0; j < num; ++j) {
1096 // TODO: remove duplicates between singular name and
1097 // currency long name?
1098 s = ures_getStringByIndex(names, j, &len, &ec5);
1099 (*currencyNames)[*total_currency_name_count].IsoCode = iso;
1100 char16_t* upperName = toUpperCase(s, len, locale);
1101 (*currencyNames)[*total_currency_name_count].currencyName = upperName;
1102 (*currencyNames)[*total_currency_name_count].flag = NEED_TO_BE_DELETED;
1103 (*currencyNames)[(*total_currency_name_count)++].currencyNameLen = len;
1104 }
1105 ures_close(names);
1106 }
1107 ures_close(curr_p);
1108 ures_close(curr);
1109 ures_close(rb);
1110
1111 if (!fallback(loc)) {
1112 break;
1113 }
1114 }
1115
1116 uhash_close(currencyIsoCodes);
1117 uhash_close(currencyPluralIsoCodes);
1118
1119 // quick sort the struct
1120 qsort(*currencyNames, *total_currency_name_count,
1121 sizeof(CurrencyNameStruct), currencyNameComparator);
1122 qsort(*currencySymbols, *total_currency_symbol_count,
1123 sizeof(CurrencyNameStruct), currencyNameComparator);
1124
1125 #ifdef UCURR_DEBUG
1126 printf("currency name count: %d\n", *total_currency_name_count);
1127 for (int32_t index = 0; index < *total_currency_name_count; ++index) {
1128 printf("index: %d\n", index);
1129 printf("iso: %s\n", (*currencyNames)[index].IsoCode);
1130 char curNameBuf[1024];
1131 memset(curNameBuf, 0, 1024);
1132 u_austrncpy(curNameBuf, (*currencyNames)[index].currencyName, (*currencyNames)[index].currencyNameLen);
1133 printf("currencyName: %s\n", curNameBuf);
1134 printf("len: %d\n", (*currencyNames)[index].currencyNameLen);
1135 }
1136 printf("currency symbol count: %d\n", *total_currency_symbol_count);
1137 for (int32_t index = 0; index < *total_currency_symbol_count; ++index) {
1138 printf("index: %d\n", index);
1139 printf("iso: %s\n", (*currencySymbols)[index].IsoCode);
1140 char curNameBuf[1024];
1141 memset(curNameBuf, 0, 1024);
1142 u_austrncpy(curNameBuf, (*currencySymbols)[index].currencyName, (*currencySymbols)[index].currencyNameLen);
1143 printf("currencySymbol: %s\n", curNameBuf);
1144 printf("len: %d\n", (*currencySymbols)[index].currencyNameLen);
1145 }
1146 #endif
1147 // fail on hashtable errors
1148 if (U_FAILURE(ec3)) {
1149 ec = ec3;
1150 return;
1151 }
1152 if (U_FAILURE(ec4)) {
1153 ec = ec4;
1154 return;
1155 }
1156 }
1157
1158 // @param currencyNames: currency names array
1159 // @param indexInCurrencyNames: the index of the character in currency names
1160 // array against which the comparison is done
1161 // @param key: input text char to compare against
1162 // @param begin(IN/OUT): the begin index of matching range in currency names array
1163 // @param end(IN/OUT): the end index of matching range in currency names array.
1164 static int32_t
binarySearch(const CurrencyNameStruct * currencyNames,int32_t indexInCurrencyNames,const char16_t key,int32_t * begin,int32_t * end)1165 binarySearch(const CurrencyNameStruct* currencyNames,
1166 int32_t indexInCurrencyNames,
1167 const char16_t key,
1168 int32_t* begin, int32_t* end) {
1169 #ifdef UCURR_DEBUG
1170 printf("key = %x\n", key);
1171 #endif
1172 int32_t first = *begin;
1173 int32_t last = *end;
1174 while (first <= last) {
1175 int32_t mid = (first + last) / 2; // compute mid point.
1176 if (indexInCurrencyNames >= currencyNames[mid].currencyNameLen) {
1177 first = mid + 1;
1178 } else {
1179 if (key > currencyNames[mid].currencyName[indexInCurrencyNames]) {
1180 first = mid + 1;
1181 }
1182 else if (key < currencyNames[mid].currencyName[indexInCurrencyNames]) {
1183 last = mid - 1;
1184 }
1185 else {
1186 // Find a match, and looking for ranges
1187 // Now do two more binary searches. First, on the left side for
1188 // the greatest L such that CurrencyNameStruct[L] < key.
1189 int32_t L = *begin;
1190 int32_t R = mid;
1191
1192 #ifdef UCURR_DEBUG
1193 printf("mid = %d\n", mid);
1194 #endif
1195 while (L < R) {
1196 int32_t M = (L + R) / 2;
1197 #ifdef UCURR_DEBUG
1198 printf("L = %d, R = %d, M = %d\n", L, R, M);
1199 #endif
1200 if (indexInCurrencyNames >= currencyNames[M].currencyNameLen) {
1201 L = M + 1;
1202 } else {
1203 if (currencyNames[M].currencyName[indexInCurrencyNames] < key) {
1204 L = M + 1;
1205 } else {
1206 #ifdef UCURR_DEBUG
1207 U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
1208 #endif
1209 R = M;
1210 }
1211 }
1212 }
1213 #ifdef UCURR_DEBUG
1214 U_ASSERT(L == R);
1215 #endif
1216 *begin = L;
1217 #ifdef UCURR_DEBUG
1218 printf("begin = %d\n", *begin);
1219 U_ASSERT(currencyNames[*begin].currencyName[indexInCurrencyNames] == key);
1220 #endif
1221
1222 // Now for the second search, finding the least R such that
1223 // key < CurrencyNameStruct[R].
1224 L = mid;
1225 R = *end;
1226 while (L < R) {
1227 int32_t M = (L + R) / 2;
1228 #ifdef UCURR_DEBUG
1229 printf("L = %d, R = %d, M = %d\n", L, R, M);
1230 #endif
1231 if (currencyNames[M].currencyNameLen < indexInCurrencyNames) {
1232 L = M + 1;
1233 } else {
1234 if (currencyNames[M].currencyName[indexInCurrencyNames] > key) {
1235 R = M;
1236 } else {
1237 #ifdef UCURR_DEBUG
1238 U_ASSERT(currencyNames[M].currencyName[indexInCurrencyNames] == key);
1239 #endif
1240 L = M + 1;
1241 }
1242 }
1243 }
1244 #ifdef UCURR_DEBUG
1245 U_ASSERT(L == R);
1246 #endif
1247 if (currencyNames[R].currencyName[indexInCurrencyNames] > key) {
1248 *end = R - 1;
1249 } else {
1250 *end = R;
1251 }
1252 #ifdef UCURR_DEBUG
1253 printf("end = %d\n", *end);
1254 #endif
1255
1256 // now, found the range. check whether there is exact match
1257 if (currencyNames[*begin].currencyNameLen == indexInCurrencyNames + 1) {
1258 return *begin; // find range and exact match.
1259 }
1260 return -1; // find range, but no exact match.
1261 }
1262 }
1263 }
1264 *begin = -1;
1265 *end = -1;
1266 return -1; // failed to find range.
1267 }
1268
1269
1270 // Linear search "text" in "currencyNames".
1271 // @param begin, end: the begin and end index in currencyNames, within which
1272 // range should the search be performed.
1273 // @param textLen: the length of the text to be compared
1274 // @param maxMatchLen(IN/OUT): passing in the computed max matching length
1275 // pass out the new max matching length
1276 // @param maxMatchIndex: the index in currencyName which has the longest
1277 // match with input text.
1278 static void
linearSearch(const CurrencyNameStruct * currencyNames,int32_t begin,int32_t end,const char16_t * text,int32_t textLen,int32_t * partialMatchLen,int32_t * maxMatchLen,int32_t * maxMatchIndex)1279 linearSearch(const CurrencyNameStruct* currencyNames,
1280 int32_t begin, int32_t end,
1281 const char16_t* text, int32_t textLen,
1282 int32_t *partialMatchLen,
1283 int32_t *maxMatchLen, int32_t* maxMatchIndex) {
1284 int32_t initialPartialMatchLen = *partialMatchLen;
1285 for (int32_t index = begin; index <= end; ++index) {
1286 int32_t len = currencyNames[index].currencyNameLen;
1287 if (len > *maxMatchLen && len <= textLen &&
1288 uprv_memcmp(currencyNames[index].currencyName, text, len * sizeof(char16_t)) == 0) {
1289 *partialMatchLen = MAX(*partialMatchLen, len);
1290 *maxMatchIndex = index;
1291 *maxMatchLen = len;
1292 #ifdef UCURR_DEBUG
1293 printf("maxMatchIndex = %d, maxMatchLen = %d\n",
1294 *maxMatchIndex, *maxMatchLen);
1295 #endif
1296 } else {
1297 // Check for partial matches.
1298 for (int32_t i=initialPartialMatchLen; i<MIN(len, textLen); i++) {
1299 if (currencyNames[index].currencyName[i] != text[i]) {
1300 break;
1301 }
1302 *partialMatchLen = MAX(*partialMatchLen, i + 1);
1303 }
1304 }
1305 }
1306 }
1307
1308 #define LINEAR_SEARCH_THRESHOLD 10
1309
1310 // Find longest match between "text" and currency names in "currencyNames".
1311 // @param total_currency_count: total number of currency names in CurrencyNames.
1312 // @param textLen: the length of the text to be compared
1313 // @param maxMatchLen: passing in the computed max matching length
1314 // pass out the new max matching length
1315 // @param maxMatchIndex: the index in currencyName which has the longest
1316 // match with input text.
1317 static void
searchCurrencyName(const CurrencyNameStruct * currencyNames,int32_t total_currency_count,const char16_t * text,int32_t textLen,int32_t * partialMatchLen,int32_t * maxMatchLen,int32_t * maxMatchIndex)1318 searchCurrencyName(const CurrencyNameStruct* currencyNames,
1319 int32_t total_currency_count,
1320 const char16_t* text, int32_t textLen,
1321 int32_t *partialMatchLen,
1322 int32_t* maxMatchLen, int32_t* maxMatchIndex) {
1323 *maxMatchIndex = -1;
1324 *maxMatchLen = 0;
1325 int32_t matchIndex = -1;
1326 int32_t binarySearchBegin = 0;
1327 int32_t binarySearchEnd = total_currency_count - 1;
1328 // It is a variant of binary search.
1329 // For example, given the currency names in currencyNames array are:
1330 // A AB ABC AD AZ B BB BBEX BBEXYZ BS C D E....
1331 // and the input text is BBEXST
1332 // The first round binary search search "B" in the text against
1333 // the first char in currency names, and find the first char matching range
1334 // to be "B BB BBEX BBEXYZ BS" (and the maximum matching "B").
1335 // The 2nd round binary search search the second "B" in the text against
1336 // the 2nd char in currency names, and narrow the matching range to
1337 // "BB BBEX BBEXYZ" (and the maximum matching "BB").
1338 // The 3rd round returns the range as "BBEX BBEXYZ" (without changing
1339 // maximum matching).
1340 // The 4th round returns the same range (the maximum matching is "BBEX").
1341 // The 5th round returns no matching range.
1342 for (int32_t index = 0; index < textLen; ++index) {
1343 // matchIndex saves the one with exact match till the current point.
1344 // [binarySearchBegin, binarySearchEnd] saves the matching range.
1345 matchIndex = binarySearch(currencyNames, index,
1346 text[index],
1347 &binarySearchBegin, &binarySearchEnd);
1348 if (binarySearchBegin == -1) { // did not find the range
1349 break;
1350 }
1351 *partialMatchLen = MAX(*partialMatchLen, index + 1);
1352 if (matchIndex != -1) {
1353 // find an exact match for text from text[0] to text[index]
1354 // in currencyNames array.
1355 *maxMatchLen = index + 1;
1356 *maxMatchIndex = matchIndex;
1357 }
1358 if (binarySearchEnd - binarySearchBegin < LINEAR_SEARCH_THRESHOLD) {
1359 // linear search if within threshold.
1360 linearSearch(currencyNames, binarySearchBegin, binarySearchEnd,
1361 text, textLen,
1362 partialMatchLen,
1363 maxMatchLen, maxMatchIndex);
1364 break;
1365 }
1366 }
1367 }
1368
1369 //========================= currency name cache =====================
1370 typedef struct {
1371 char locale[ULOC_FULLNAME_CAPACITY]; //key
1372 // currency names, case insensitive
1373 CurrencyNameStruct* currencyNames; // value
1374 int32_t totalCurrencyNameCount; // currency name count
1375 // currency symbols and ISO code, case sensitive
1376 CurrencyNameStruct* currencySymbols; // value
1377 int32_t totalCurrencySymbolCount; // count
1378 // reference count.
1379 // reference count is set to 1 when an entry is put to cache.
1380 // it increases by 1 before accessing, and decreased by 1 after accessing.
1381 // The entry is deleted when ref count is zero, which means
1382 // the entry is replaced out of cache and no process is accessing it.
1383 int32_t refCount;
1384 } CurrencyNameCacheEntry;
1385
1386
1387 #define CURRENCY_NAME_CACHE_NUM 10
1388
1389 // Reserve 10 cache entries.
1390 static CurrencyNameCacheEntry* currCache[CURRENCY_NAME_CACHE_NUM] = {nullptr};
1391 // Using an index to indicate which entry to be replaced when cache is full.
1392 // It is a simple round-robin replacement strategy.
1393 static int8_t currentCacheEntryIndex = 0;
1394
1395 static UMutex gCurrencyCacheMutex;
1396
1397 // Cache deletion
1398 static void
deleteCurrencyNames(CurrencyNameStruct * currencyNames,int32_t count)1399 deleteCurrencyNames(CurrencyNameStruct* currencyNames, int32_t count) {
1400 for (int32_t index = 0; index < count; ++index) {
1401 if ( (currencyNames[index].flag & NEED_TO_BE_DELETED) ) {
1402 uprv_free(currencyNames[index].currencyName);
1403 }
1404 }
1405 uprv_free(currencyNames);
1406 }
1407
1408
1409 static void
deleteCacheEntry(CurrencyNameCacheEntry * entry)1410 deleteCacheEntry(CurrencyNameCacheEntry* entry) {
1411 deleteCurrencyNames(entry->currencyNames, entry->totalCurrencyNameCount);
1412 deleteCurrencyNames(entry->currencySymbols, entry->totalCurrencySymbolCount);
1413 uprv_free(entry);
1414 }
1415
1416
1417 // Cache clean up
1418 static UBool U_CALLCONV
currency_cache_cleanup()1419 currency_cache_cleanup() {
1420 for (int32_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
1421 if (currCache[i]) {
1422 deleteCacheEntry(currCache[i]);
1423 currCache[i] = nullptr;
1424 }
1425 }
1426 return true;
1427 }
1428
1429
1430 /**
1431 * Loads the currency name data from the cache, or from resource bundles if necessary.
1432 * The refCount is automatically incremented. It is the caller's responsibility
1433 * to decrement it when done!
1434 */
1435 static CurrencyNameCacheEntry*
getCacheEntry(const char * locale,UErrorCode & ec)1436 getCacheEntry(const char* locale, UErrorCode& ec) {
1437
1438 int32_t total_currency_name_count = 0;
1439 CurrencyNameStruct* currencyNames = nullptr;
1440 int32_t total_currency_symbol_count = 0;
1441 CurrencyNameStruct* currencySymbols = nullptr;
1442 CurrencyNameCacheEntry* cacheEntry = nullptr;
1443
1444 umtx_lock(&gCurrencyCacheMutex);
1445 // in order to handle racing correctly,
1446 // not putting 'search' in a separate function.
1447 int8_t found = -1;
1448 for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
1449 if (currCache[i]!= nullptr &&
1450 uprv_strcmp(locale, currCache[i]->locale) == 0) {
1451 found = i;
1452 break;
1453 }
1454 }
1455 if (found != -1) {
1456 cacheEntry = currCache[found];
1457 ++(cacheEntry->refCount);
1458 }
1459 umtx_unlock(&gCurrencyCacheMutex);
1460 if (found == -1) {
1461 collectCurrencyNames(locale, ¤cyNames, &total_currency_name_count, ¤cySymbols, &total_currency_symbol_count, ec);
1462 if (U_FAILURE(ec)) {
1463 return nullptr;
1464 }
1465 umtx_lock(&gCurrencyCacheMutex);
1466 // check again.
1467 for (int8_t i = 0; i < CURRENCY_NAME_CACHE_NUM; ++i) {
1468 if (currCache[i]!= nullptr &&
1469 uprv_strcmp(locale, currCache[i]->locale) == 0) {
1470 found = i;
1471 break;
1472 }
1473 }
1474 if (found == -1) {
1475 // insert new entry to
1476 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1477 // and remove the existing entry
1478 // currentCacheEntryIndex % CURRENCY_NAME_CACHE_NUM
1479 // from cache.
1480 cacheEntry = currCache[currentCacheEntryIndex];
1481 if (cacheEntry) {
1482 --(cacheEntry->refCount);
1483 // delete if the ref count is zero
1484 if (cacheEntry->refCount == 0) {
1485 deleteCacheEntry(cacheEntry);
1486 }
1487 }
1488 cacheEntry = (CurrencyNameCacheEntry*)uprv_malloc(sizeof(CurrencyNameCacheEntry));
1489 currCache[currentCacheEntryIndex] = cacheEntry;
1490 uprv_strcpy(cacheEntry->locale, locale);
1491 cacheEntry->currencyNames = currencyNames;
1492 cacheEntry->totalCurrencyNameCount = total_currency_name_count;
1493 cacheEntry->currencySymbols = currencySymbols;
1494 cacheEntry->totalCurrencySymbolCount = total_currency_symbol_count;
1495 cacheEntry->refCount = 2; // one for cache, one for reference
1496 currentCacheEntryIndex = (currentCacheEntryIndex + 1) % CURRENCY_NAME_CACHE_NUM;
1497 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
1498 } else {
1499 deleteCurrencyNames(currencyNames, total_currency_name_count);
1500 deleteCurrencyNames(currencySymbols, total_currency_symbol_count);
1501 cacheEntry = currCache[found];
1502 ++(cacheEntry->refCount);
1503 }
1504 umtx_unlock(&gCurrencyCacheMutex);
1505 }
1506
1507 return cacheEntry;
1508 }
1509
releaseCacheEntry(CurrencyNameCacheEntry * cacheEntry)1510 static void releaseCacheEntry(CurrencyNameCacheEntry* cacheEntry) {
1511 umtx_lock(&gCurrencyCacheMutex);
1512 --(cacheEntry->refCount);
1513 if (cacheEntry->refCount == 0) { // remove
1514 deleteCacheEntry(cacheEntry);
1515 }
1516 umtx_unlock(&gCurrencyCacheMutex);
1517 }
1518
1519 U_CAPI void
uprv_parseCurrency(const char * locale,const icu::UnicodeString & text,icu::ParsePosition & pos,int8_t type,int32_t * partialMatchLen,char16_t * result,UErrorCode & ec)1520 uprv_parseCurrency(const char* locale,
1521 const icu::UnicodeString& text,
1522 icu::ParsePosition& pos,
1523 int8_t type,
1524 int32_t* partialMatchLen,
1525 char16_t* result,
1526 UErrorCode& ec) {
1527 U_NAMESPACE_USE
1528 if (U_FAILURE(ec)) {
1529 return;
1530 }
1531 CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
1532 if (U_FAILURE(ec)) {
1533 return;
1534 }
1535
1536 int32_t total_currency_name_count = cacheEntry->totalCurrencyNameCount;
1537 CurrencyNameStruct* currencyNames = cacheEntry->currencyNames;
1538 int32_t total_currency_symbol_count = cacheEntry->totalCurrencySymbolCount;
1539 CurrencyNameStruct* currencySymbols = cacheEntry->currencySymbols;
1540
1541 int32_t start = pos.getIndex();
1542
1543 char16_t inputText[MAX_CURRENCY_NAME_LEN];
1544 char16_t upperText[MAX_CURRENCY_NAME_LEN];
1545 int32_t textLen = MIN(MAX_CURRENCY_NAME_LEN, text.length() - start);
1546 text.extract(start, textLen, inputText);
1547 UErrorCode ec1 = U_ZERO_ERROR;
1548 textLen = u_strToUpper(upperText, MAX_CURRENCY_NAME_LEN, inputText, textLen, locale, &ec1);
1549
1550 // Make sure partialMatchLen is initialized
1551 *partialMatchLen = 0;
1552
1553 int32_t max = 0;
1554 int32_t matchIndex = -1;
1555 // case in-sensitive comparison against currency names
1556 searchCurrencyName(currencyNames, total_currency_name_count,
1557 upperText, textLen, partialMatchLen, &max, &matchIndex);
1558
1559 #ifdef UCURR_DEBUG
1560 printf("search in names, max = %d, matchIndex = %d\n", max, matchIndex);
1561 #endif
1562
1563 int32_t maxInSymbol = 0;
1564 int32_t matchIndexInSymbol = -1;
1565 if (type != UCURR_LONG_NAME) { // not name only
1566 // case sensitive comparison against currency symbols and ISO code.
1567 searchCurrencyName(currencySymbols, total_currency_symbol_count,
1568 inputText, textLen,
1569 partialMatchLen,
1570 &maxInSymbol, &matchIndexInSymbol);
1571 }
1572
1573 #ifdef UCURR_DEBUG
1574 printf("search in symbols, maxInSymbol = %d, matchIndexInSymbol = %d\n", maxInSymbol, matchIndexInSymbol);
1575 if(matchIndexInSymbol != -1) {
1576 printf("== ISO=%s\n", currencySymbols[matchIndexInSymbol].IsoCode);
1577 }
1578 #endif
1579
1580 if (max >= maxInSymbol && matchIndex != -1) {
1581 u_charsToUChars(currencyNames[matchIndex].IsoCode, result, 4);
1582 pos.setIndex(start + max);
1583 } else if (maxInSymbol >= max && matchIndexInSymbol != -1) {
1584 u_charsToUChars(currencySymbols[matchIndexInSymbol].IsoCode, result, 4);
1585 pos.setIndex(start + maxInSymbol);
1586 }
1587
1588 // decrease reference count
1589 releaseCacheEntry(cacheEntry);
1590 }
1591
uprv_currencyLeads(const char * locale,icu::UnicodeSet & result,UErrorCode & ec)1592 void uprv_currencyLeads(const char* locale, icu::UnicodeSet& result, UErrorCode& ec) {
1593 U_NAMESPACE_USE
1594 if (U_FAILURE(ec)) {
1595 return;
1596 }
1597 CurrencyNameCacheEntry* cacheEntry = getCacheEntry(locale, ec);
1598 if (U_FAILURE(ec)) {
1599 return;
1600 }
1601
1602 for (int32_t i=0; i<cacheEntry->totalCurrencySymbolCount; i++) {
1603 const CurrencyNameStruct& info = cacheEntry->currencySymbols[i];
1604 UChar32 cp;
1605 U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
1606 result.add(cp);
1607 }
1608
1609 for (int32_t i=0; i<cacheEntry->totalCurrencyNameCount; i++) {
1610 const CurrencyNameStruct& info = cacheEntry->currencyNames[i];
1611 UChar32 cp;
1612 U16_GET(info.currencyName, 0, 0, info.currencyNameLen, cp);
1613 result.add(cp);
1614 }
1615
1616 // decrease reference count
1617 releaseCacheEntry(cacheEntry);
1618 }
1619
1620
1621 /**
1622 * Internal method. Given a currency ISO code and a locale, return
1623 * the "static" currency name. This is usually the same as the
1624 * UCURR_SYMBOL_NAME, but if the latter is a choice format, then the
1625 * format is applied to the number 2.0 (to yield the more common
1626 * plural) to return a static name.
1627 *
1628 * This is used for backward compatibility with old currency logic in
1629 * DecimalFormat and DecimalFormatSymbols.
1630 */
1631 U_CAPI void
uprv_getStaticCurrencyName(const char16_t * iso,const char * loc,icu::UnicodeString & result,UErrorCode & ec)1632 uprv_getStaticCurrencyName(const char16_t* iso, const char* loc,
1633 icu::UnicodeString& result, UErrorCode& ec)
1634 {
1635 U_NAMESPACE_USE
1636
1637 int32_t len;
1638 const char16_t* currname = ucurr_getName(iso, loc, UCURR_SYMBOL_NAME,
1639 nullptr /* isChoiceFormat */, &len, &ec);
1640 if (U_SUCCESS(ec)) {
1641 result.setTo(currname, len);
1642 }
1643 }
1644
1645 U_CAPI int32_t U_EXPORT2
ucurr_getDefaultFractionDigits(const char16_t * currency,UErrorCode * ec)1646 ucurr_getDefaultFractionDigits(const char16_t* currency, UErrorCode* ec) {
1647 return ucurr_getDefaultFractionDigitsForUsage(currency,UCURR_USAGE_STANDARD,ec);
1648 }
1649
1650 U_CAPI int32_t U_EXPORT2
ucurr_getDefaultFractionDigitsForUsage(const char16_t * currency,const UCurrencyUsage usage,UErrorCode * ec)1651 ucurr_getDefaultFractionDigitsForUsage(const char16_t* currency, const UCurrencyUsage usage, UErrorCode* ec) {
1652 int32_t fracDigits = 0;
1653 if (U_SUCCESS(*ec)) {
1654 switch (usage) {
1655 case UCURR_USAGE_STANDARD:
1656 fracDigits = (_findMetaData(currency, *ec))[0];
1657 break;
1658 case UCURR_USAGE_CASH:
1659 fracDigits = (_findMetaData(currency, *ec))[2];
1660 break;
1661 default:
1662 *ec = U_UNSUPPORTED_ERROR;
1663 }
1664 }
1665 return fracDigits;
1666 }
1667
1668 U_CAPI double U_EXPORT2
ucurr_getRoundingIncrement(const char16_t * currency,UErrorCode * ec)1669 ucurr_getRoundingIncrement(const char16_t* currency, UErrorCode* ec) {
1670 return ucurr_getRoundingIncrementForUsage(currency, UCURR_USAGE_STANDARD, ec);
1671 }
1672
1673 U_CAPI double U_EXPORT2
ucurr_getRoundingIncrementForUsage(const char16_t * currency,const UCurrencyUsage usage,UErrorCode * ec)1674 ucurr_getRoundingIncrementForUsage(const char16_t* currency, const UCurrencyUsage usage, UErrorCode* ec) {
1675 double result = 0.0;
1676
1677 const int32_t *data = _findMetaData(currency, *ec);
1678 if (U_SUCCESS(*ec)) {
1679 int32_t fracDigits;
1680 int32_t increment;
1681 switch (usage) {
1682 case UCURR_USAGE_STANDARD:
1683 fracDigits = data[0];
1684 increment = data[1];
1685 break;
1686 case UCURR_USAGE_CASH:
1687 fracDigits = data[2];
1688 increment = data[3];
1689 break;
1690 default:
1691 *ec = U_UNSUPPORTED_ERROR;
1692 return result;
1693 }
1694
1695 // If the meta data is invalid, return 0.0
1696 if (fracDigits < 0 || fracDigits > MAX_POW10) {
1697 *ec = U_INVALID_FORMAT_ERROR;
1698 } else {
1699 // A rounding value of 0 or 1 indicates no rounding.
1700 if (increment >= 2) {
1701 // Return (increment) / 10^(fracDigits). The only actual rounding data,
1702 // as of this writing, is CHF { 2, 5 }.
1703 result = double(increment) / POW10[fracDigits];
1704 }
1705 }
1706 }
1707
1708 return result;
1709 }
1710
1711 U_CDECL_BEGIN
1712
1713 typedef struct UCurrencyContext {
1714 uint32_t currType; /* UCurrCurrencyType */
1715 uint32_t listIdx;
1716 } UCurrencyContext;
1717
1718 /*
1719 Please keep this list in alphabetical order.
1720 You can look at the CLDR supplemental data or ISO-4217 for the meaning of some
1721 of these items.
1722 ISO-4217: http://www.iso.org/iso/en/prods-services/popstds/currencycodeslist.html
1723 */
1724 static const struct CurrencyList {
1725 const char *currency;
1726 uint32_t currType;
1727 } gCurrencyList[] = {
1728 {"ADP", UCURR_COMMON|UCURR_DEPRECATED},
1729 {"AED", UCURR_COMMON|UCURR_NON_DEPRECATED},
1730 {"AFA", UCURR_COMMON|UCURR_DEPRECATED},
1731 {"AFN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1732 {"ALK", UCURR_COMMON|UCURR_DEPRECATED},
1733 {"ALL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1734 {"AMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1735 {"ANG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1736 {"AOA", UCURR_COMMON|UCURR_NON_DEPRECATED},
1737 {"AOK", UCURR_COMMON|UCURR_DEPRECATED},
1738 {"AON", UCURR_COMMON|UCURR_DEPRECATED},
1739 {"AOR", UCURR_COMMON|UCURR_DEPRECATED},
1740 {"ARA", UCURR_COMMON|UCURR_DEPRECATED},
1741 {"ARL", UCURR_COMMON|UCURR_DEPRECATED},
1742 {"ARM", UCURR_COMMON|UCURR_DEPRECATED},
1743 {"ARP", UCURR_COMMON|UCURR_DEPRECATED},
1744 {"ARS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1745 {"ATS", UCURR_COMMON|UCURR_DEPRECATED},
1746 {"AUD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1747 {"AWG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1748 {"AZM", UCURR_COMMON|UCURR_DEPRECATED},
1749 {"AZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1750 {"BAD", UCURR_COMMON|UCURR_DEPRECATED},
1751 {"BAM", UCURR_COMMON|UCURR_NON_DEPRECATED},
1752 {"BAN", UCURR_COMMON|UCURR_DEPRECATED},
1753 {"BBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1754 {"BDT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1755 {"BEC", UCURR_UNCOMMON|UCURR_DEPRECATED},
1756 {"BEF", UCURR_COMMON|UCURR_DEPRECATED},
1757 {"BEL", UCURR_UNCOMMON|UCURR_DEPRECATED},
1758 {"BGL", UCURR_COMMON|UCURR_DEPRECATED},
1759 {"BGM", UCURR_COMMON|UCURR_DEPRECATED},
1760 {"BGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1761 {"BGO", UCURR_COMMON|UCURR_DEPRECATED},
1762 {"BHD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1763 {"BIF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1764 {"BMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1765 {"BND", UCURR_COMMON|UCURR_NON_DEPRECATED},
1766 {"BOB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1767 {"BOL", UCURR_COMMON|UCURR_DEPRECATED},
1768 {"BOP", UCURR_COMMON|UCURR_DEPRECATED},
1769 {"BOV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1770 {"BRB", UCURR_COMMON|UCURR_DEPRECATED},
1771 {"BRC", UCURR_COMMON|UCURR_DEPRECATED},
1772 {"BRE", UCURR_COMMON|UCURR_DEPRECATED},
1773 {"BRL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1774 {"BRN", UCURR_COMMON|UCURR_DEPRECATED},
1775 {"BRR", UCURR_COMMON|UCURR_DEPRECATED},
1776 {"BRZ", UCURR_COMMON|UCURR_DEPRECATED},
1777 {"BSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1778 {"BTN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1779 {"BUK", UCURR_COMMON|UCURR_DEPRECATED},
1780 {"BWP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1781 {"BYB", UCURR_COMMON|UCURR_DEPRECATED},
1782 {"BYN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1783 {"BYR", UCURR_COMMON|UCURR_DEPRECATED},
1784 {"BZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1785 {"CAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1786 {"CDF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1787 {"CHE", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1788 {"CHF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1789 {"CHW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1790 {"CLE", UCURR_COMMON|UCURR_DEPRECATED},
1791 {"CLF", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1792 {"CLP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1793 {"CNH", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1794 {"CNX", UCURR_UNCOMMON|UCURR_DEPRECATED},
1795 {"CNY", UCURR_COMMON|UCURR_NON_DEPRECATED},
1796 {"COP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1797 {"COU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1798 {"CRC", UCURR_COMMON|UCURR_NON_DEPRECATED},
1799 {"CSD", UCURR_COMMON|UCURR_DEPRECATED},
1800 {"CSK", UCURR_COMMON|UCURR_DEPRECATED},
1801 {"CUC", UCURR_COMMON|UCURR_NON_DEPRECATED},
1802 {"CUP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1803 {"CVE", UCURR_COMMON|UCURR_NON_DEPRECATED},
1804 {"CYP", UCURR_COMMON|UCURR_DEPRECATED},
1805 {"CZK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1806 {"DDM", UCURR_COMMON|UCURR_DEPRECATED},
1807 {"DEM", UCURR_COMMON|UCURR_DEPRECATED},
1808 {"DJF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1809 {"DKK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1810 {"DOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1811 {"DZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1812 {"ECS", UCURR_COMMON|UCURR_DEPRECATED},
1813 {"ECV", UCURR_UNCOMMON|UCURR_DEPRECATED},
1814 {"EEK", UCURR_COMMON|UCURR_DEPRECATED},
1815 {"EGP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1816 {"ERN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1817 {"ESA", UCURR_UNCOMMON|UCURR_DEPRECATED},
1818 {"ESB", UCURR_UNCOMMON|UCURR_DEPRECATED},
1819 {"ESP", UCURR_COMMON|UCURR_DEPRECATED},
1820 {"ETB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1821 {"EUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1822 {"FIM", UCURR_COMMON|UCURR_DEPRECATED},
1823 {"FJD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1824 {"FKP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1825 {"FRF", UCURR_COMMON|UCURR_DEPRECATED},
1826 {"GBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1827 {"GEK", UCURR_COMMON|UCURR_DEPRECATED},
1828 {"GEL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1829 {"GHC", UCURR_COMMON|UCURR_DEPRECATED},
1830 {"GHS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1831 {"GIP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1832 {"GMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1833 {"GNF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1834 {"GNS", UCURR_COMMON|UCURR_DEPRECATED},
1835 {"GQE", UCURR_COMMON|UCURR_DEPRECATED},
1836 {"GRD", UCURR_COMMON|UCURR_DEPRECATED},
1837 {"GTQ", UCURR_COMMON|UCURR_NON_DEPRECATED},
1838 {"GWE", UCURR_COMMON|UCURR_DEPRECATED},
1839 {"GWP", UCURR_COMMON|UCURR_DEPRECATED},
1840 {"GYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1841 {"HKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1842 {"HNL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1843 {"HRD", UCURR_COMMON|UCURR_DEPRECATED},
1844 {"HRK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1845 {"HTG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1846 {"HUF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1847 {"IDR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1848 {"IEP", UCURR_COMMON|UCURR_DEPRECATED},
1849 {"ILP", UCURR_COMMON|UCURR_DEPRECATED},
1850 {"ILR", UCURR_COMMON|UCURR_DEPRECATED},
1851 {"ILS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1852 {"INR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1853 {"IQD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1854 {"IRR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1855 {"ISJ", UCURR_COMMON|UCURR_DEPRECATED},
1856 {"ISK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1857 {"ITL", UCURR_COMMON|UCURR_DEPRECATED},
1858 {"JMD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1859 {"JOD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1860 {"JPY", UCURR_COMMON|UCURR_NON_DEPRECATED},
1861 {"KES", UCURR_COMMON|UCURR_NON_DEPRECATED},
1862 {"KGS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1863 {"KHR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1864 {"KMF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1865 {"KPW", UCURR_COMMON|UCURR_NON_DEPRECATED},
1866 {"KRH", UCURR_COMMON|UCURR_DEPRECATED},
1867 {"KRO", UCURR_COMMON|UCURR_DEPRECATED},
1868 {"KRW", UCURR_COMMON|UCURR_NON_DEPRECATED},
1869 {"KWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1870 {"KYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1871 {"KZT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1872 {"LAK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1873 {"LBP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1874 {"LKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1875 {"LRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1876 {"LSL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1877 {"LSM", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
1878 {"LTL", UCURR_COMMON|UCURR_DEPRECATED},
1879 {"LTT", UCURR_COMMON|UCURR_DEPRECATED},
1880 {"LUC", UCURR_UNCOMMON|UCURR_DEPRECATED},
1881 {"LUF", UCURR_COMMON|UCURR_DEPRECATED},
1882 {"LUL", UCURR_UNCOMMON|UCURR_DEPRECATED},
1883 {"LVL", UCURR_COMMON|UCURR_DEPRECATED},
1884 {"LVR", UCURR_COMMON|UCURR_DEPRECATED},
1885 {"LYD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1886 {"MAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1887 {"MAF", UCURR_COMMON|UCURR_DEPRECATED},
1888 {"MCF", UCURR_COMMON|UCURR_DEPRECATED},
1889 {"MDC", UCURR_COMMON|UCURR_DEPRECATED},
1890 {"MDL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1891 {"MGA", UCURR_COMMON|UCURR_NON_DEPRECATED},
1892 {"MGF", UCURR_COMMON|UCURR_DEPRECATED},
1893 {"MKD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1894 {"MKN", UCURR_COMMON|UCURR_DEPRECATED},
1895 {"MLF", UCURR_COMMON|UCURR_DEPRECATED},
1896 {"MMK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1897 {"MNT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1898 {"MOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1899 {"MRO", UCURR_COMMON|UCURR_DEPRECATED},
1900 {"MRU", UCURR_COMMON|UCURR_NON_DEPRECATED},
1901 {"MTL", UCURR_COMMON|UCURR_DEPRECATED},
1902 {"MTP", UCURR_COMMON|UCURR_DEPRECATED},
1903 {"MUR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1904 {"MVP", UCURR_COMMON|UCURR_DEPRECATED}, // questionable, remove?
1905 {"MVR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1906 {"MWK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1907 {"MXN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1908 {"MXP", UCURR_COMMON|UCURR_DEPRECATED},
1909 {"MXV", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1910 {"MYR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1911 {"MZE", UCURR_COMMON|UCURR_DEPRECATED},
1912 {"MZM", UCURR_COMMON|UCURR_DEPRECATED},
1913 {"MZN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1914 {"NAD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1915 {"NGN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1916 {"NIC", UCURR_COMMON|UCURR_DEPRECATED},
1917 {"NIO", UCURR_COMMON|UCURR_NON_DEPRECATED},
1918 {"NLG", UCURR_COMMON|UCURR_DEPRECATED},
1919 {"NOK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1920 {"NPR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1921 {"NZD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1922 {"OMR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1923 {"PAB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1924 {"PEI", UCURR_COMMON|UCURR_DEPRECATED},
1925 {"PEN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1926 {"PES", UCURR_COMMON|UCURR_DEPRECATED},
1927 {"PGK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1928 {"PHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1929 {"PKR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1930 {"PLN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1931 {"PLZ", UCURR_COMMON|UCURR_DEPRECATED},
1932 {"PTE", UCURR_COMMON|UCURR_DEPRECATED},
1933 {"PYG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1934 {"QAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1935 {"RHD", UCURR_COMMON|UCURR_DEPRECATED},
1936 {"ROL", UCURR_COMMON|UCURR_DEPRECATED},
1937 {"RON", UCURR_COMMON|UCURR_NON_DEPRECATED},
1938 {"RSD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1939 {"RUB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1940 {"RUR", UCURR_COMMON|UCURR_DEPRECATED},
1941 {"RWF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1942 {"SAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1943 {"SBD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1944 {"SCR", UCURR_COMMON|UCURR_NON_DEPRECATED},
1945 {"SDD", UCURR_COMMON|UCURR_DEPRECATED},
1946 {"SDG", UCURR_COMMON|UCURR_NON_DEPRECATED},
1947 {"SDP", UCURR_COMMON|UCURR_DEPRECATED},
1948 {"SEK", UCURR_COMMON|UCURR_NON_DEPRECATED},
1949 {"SGD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1950 {"SHP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1951 {"SIT", UCURR_COMMON|UCURR_DEPRECATED},
1952 {"SKK", UCURR_COMMON|UCURR_DEPRECATED},
1953 {"SLE", UCURR_COMMON|UCURR_NON_DEPRECATED},
1954 {"SLL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1955 {"SOS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1956 {"SRD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1957 {"SRG", UCURR_COMMON|UCURR_DEPRECATED},
1958 {"SSP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1959 {"STD", UCURR_COMMON|UCURR_DEPRECATED},
1960 {"STN", UCURR_COMMON|UCURR_NON_DEPRECATED},
1961 {"SUR", UCURR_COMMON|UCURR_DEPRECATED},
1962 {"SVC", UCURR_COMMON|UCURR_DEPRECATED},
1963 {"SYP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1964 {"SZL", UCURR_COMMON|UCURR_NON_DEPRECATED},
1965 {"THB", UCURR_COMMON|UCURR_NON_DEPRECATED},
1966 {"TJR", UCURR_COMMON|UCURR_DEPRECATED},
1967 {"TJS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1968 {"TMM", UCURR_COMMON|UCURR_DEPRECATED},
1969 {"TMT", UCURR_COMMON|UCURR_NON_DEPRECATED},
1970 {"TND", UCURR_COMMON|UCURR_NON_DEPRECATED},
1971 {"TOP", UCURR_COMMON|UCURR_NON_DEPRECATED},
1972 {"TPE", UCURR_COMMON|UCURR_DEPRECATED},
1973 {"TRL", UCURR_COMMON|UCURR_DEPRECATED},
1974 {"TRY", UCURR_COMMON|UCURR_NON_DEPRECATED},
1975 {"TTD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1976 {"TWD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1977 {"TZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1978 {"UAH", UCURR_COMMON|UCURR_NON_DEPRECATED},
1979 {"UAK", UCURR_COMMON|UCURR_DEPRECATED},
1980 {"UGS", UCURR_COMMON|UCURR_DEPRECATED},
1981 {"UGX", UCURR_COMMON|UCURR_NON_DEPRECATED},
1982 {"USD", UCURR_COMMON|UCURR_NON_DEPRECATED},
1983 {"USN", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1984 {"USS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1985 {"UYI", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1986 {"UYP", UCURR_COMMON|UCURR_DEPRECATED},
1987 {"UYU", UCURR_COMMON|UCURR_NON_DEPRECATED},
1988 {"UYW", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1989 {"UZS", UCURR_COMMON|UCURR_NON_DEPRECATED},
1990 {"VEB", UCURR_COMMON|UCURR_DEPRECATED},
1991 {"VED", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
1992 {"VEF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1993 {"VES", UCURR_COMMON|UCURR_NON_DEPRECATED},
1994 {"VND", UCURR_COMMON|UCURR_NON_DEPRECATED},
1995 {"VNN", UCURR_COMMON|UCURR_DEPRECATED},
1996 {"VUV", UCURR_COMMON|UCURR_NON_DEPRECATED},
1997 {"WST", UCURR_COMMON|UCURR_NON_DEPRECATED},
1998 {"XAF", UCURR_COMMON|UCURR_NON_DEPRECATED},
1999 {"XAG", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2000 {"XAU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2001 {"XBA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2002 {"XBB", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2003 {"XBC", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2004 {"XBD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2005 {"XCD", UCURR_COMMON|UCURR_NON_DEPRECATED},
2006 {"XCG", UCURR_COMMON|UCURR_NON_DEPRECATED},
2007 {"XDR", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2008 {"XEU", UCURR_UNCOMMON|UCURR_DEPRECATED},
2009 {"XFO", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2010 {"XFU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2011 {"XOF", UCURR_COMMON|UCURR_NON_DEPRECATED},
2012 {"XPD", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2013 {"XPF", UCURR_COMMON|UCURR_NON_DEPRECATED},
2014 {"XPT", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2015 {"XRE", UCURR_UNCOMMON|UCURR_DEPRECATED},
2016 {"XSU", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2017 {"XTS", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2018 {"XUA", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2019 {"XXX", UCURR_UNCOMMON|UCURR_NON_DEPRECATED},
2020 {"YDD", UCURR_COMMON|UCURR_DEPRECATED},
2021 {"YER", UCURR_COMMON|UCURR_NON_DEPRECATED},
2022 {"YUD", UCURR_COMMON|UCURR_DEPRECATED},
2023 {"YUM", UCURR_COMMON|UCURR_DEPRECATED},
2024 {"YUN", UCURR_COMMON|UCURR_DEPRECATED},
2025 {"YUR", UCURR_COMMON|UCURR_DEPRECATED},
2026 {"ZAL", UCURR_UNCOMMON|UCURR_DEPRECATED},
2027 {"ZAR", UCURR_COMMON|UCURR_NON_DEPRECATED},
2028 {"ZMK", UCURR_COMMON|UCURR_DEPRECATED},
2029 {"ZMW", UCURR_COMMON|UCURR_NON_DEPRECATED},
2030 {"ZRN", UCURR_COMMON|UCURR_DEPRECATED},
2031 {"ZRZ", UCURR_COMMON|UCURR_DEPRECATED},
2032 {"ZWD", UCURR_COMMON|UCURR_DEPRECATED},
2033 {"ZWL", UCURR_COMMON|UCURR_DEPRECATED},
2034 {"ZWR", UCURR_COMMON|UCURR_DEPRECATED},
2035 { nullptr, 0 } // Leave here to denote the end of the list.
2036 };
2037
2038 #define UCURR_MATCHES_BITMASK(variable, typeToMatch) \
2039 ((typeToMatch) == UCURR_ALL || ((variable) & (typeToMatch)) == (typeToMatch))
2040
2041 static int32_t U_CALLCONV
ucurr_countCurrencyList(UEnumeration * enumerator,UErrorCode *)2042 ucurr_countCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
2043 UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
2044 uint32_t currType = myContext->currType;
2045 int32_t count = 0;
2046
2047 /* Count the number of items matching the type we are looking for. */
2048 for (int32_t idx = 0; gCurrencyList[idx].currency != nullptr; idx++) {
2049 if (UCURR_MATCHES_BITMASK(gCurrencyList[idx].currType, currType)) {
2050 count++;
2051 }
2052 }
2053 return count;
2054 }
2055
2056 static const char* U_CALLCONV
ucurr_nextCurrencyList(UEnumeration * enumerator,int32_t * resultLength,UErrorCode *)2057 ucurr_nextCurrencyList(UEnumeration *enumerator,
2058 int32_t* resultLength,
2059 UErrorCode * /*pErrorCode*/)
2060 {
2061 UCurrencyContext *myContext = (UCurrencyContext *)(enumerator->context);
2062
2063 /* Find the next in the list that matches the type we are looking for. */
2064 while (myContext->listIdx < UPRV_LENGTHOF(gCurrencyList)-1) {
2065 const struct CurrencyList *currItem = &gCurrencyList[myContext->listIdx++];
2066 if (UCURR_MATCHES_BITMASK(currItem->currType, myContext->currType))
2067 {
2068 if (resultLength) {
2069 *resultLength = 3; /* Currency codes are only 3 chars long */
2070 }
2071 return currItem->currency;
2072 }
2073 }
2074 /* We enumerated too far. */
2075 if (resultLength) {
2076 *resultLength = 0;
2077 }
2078 return nullptr;
2079 }
2080
2081 static void U_CALLCONV
ucurr_resetCurrencyList(UEnumeration * enumerator,UErrorCode *)2082 ucurr_resetCurrencyList(UEnumeration *enumerator, UErrorCode * /*pErrorCode*/) {
2083 ((UCurrencyContext *)(enumerator->context))->listIdx = 0;
2084 }
2085
2086 static void U_CALLCONV
ucurr_closeCurrencyList(UEnumeration * enumerator)2087 ucurr_closeCurrencyList(UEnumeration *enumerator) {
2088 uprv_free(enumerator->context);
2089 uprv_free(enumerator);
2090 }
2091
2092 static void U_CALLCONV
ucurr_createCurrencyList(UHashtable * isoCodes,UErrorCode * status)2093 ucurr_createCurrencyList(UHashtable *isoCodes, UErrorCode* status){
2094 UErrorCode localStatus = U_ZERO_ERROR;
2095
2096 // Look up the CurrencyMap element in the root bundle.
2097 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
2098 UResourceBundle *currencyMapArray = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
2099
2100 if (U_SUCCESS(localStatus)) {
2101 // process each entry in currency map
2102 for (int32_t i=0; i<ures_getSize(currencyMapArray); i++) {
2103 // get the currency resource
2104 UResourceBundle *currencyArray = ures_getByIndex(currencyMapArray, i, nullptr, &localStatus);
2105 // process each currency
2106 if (U_SUCCESS(localStatus)) {
2107 for (int32_t j=0; j<ures_getSize(currencyArray); j++) {
2108 // get the currency resource
2109 UResourceBundle *currencyRes = ures_getByIndex(currencyArray, j, nullptr, &localStatus);
2110 IsoCodeEntry *entry = (IsoCodeEntry*)uprv_malloc(sizeof(IsoCodeEntry));
2111 if (entry == nullptr) {
2112 *status = U_MEMORY_ALLOCATION_ERROR;
2113 return;
2114 }
2115
2116 // get the ISO code
2117 int32_t isoLength = 0;
2118 UResourceBundle *idRes = ures_getByKey(currencyRes, "id", nullptr, &localStatus);
2119 if (idRes == nullptr) {
2120 continue;
2121 }
2122 const char16_t *isoCode = ures_getString(idRes, &isoLength, &localStatus);
2123
2124 // get from date
2125 UDate fromDate = U_DATE_MIN;
2126 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", nullptr, &localStatus);
2127
2128 if (U_SUCCESS(localStatus)) {
2129 int32_t fromLength = 0;
2130 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
2131 int64_t currDate64 = ((uint64_t)fromArray[0]) << 32;
2132 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2133 fromDate = (UDate)currDate64;
2134 }
2135 ures_close(fromRes);
2136
2137 // get to date
2138 UDate toDate = U_DATE_MAX;
2139 localStatus = U_ZERO_ERROR;
2140 UResourceBundle *toRes = ures_getByKey(currencyRes, "to", nullptr, &localStatus);
2141
2142 if (U_SUCCESS(localStatus)) {
2143 int32_t toLength = 0;
2144 const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
2145 int64_t currDate64 = (uint64_t)toArray[0] << 32;
2146 currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2147 toDate = (UDate)currDate64;
2148 }
2149 ures_close(toRes);
2150
2151 ures_close(idRes);
2152 ures_close(currencyRes);
2153
2154 entry->isoCode = isoCode;
2155 entry->from = fromDate;
2156 entry->to = toDate;
2157
2158 localStatus = U_ZERO_ERROR;
2159 uhash_put(isoCodes, (char16_t *)isoCode, entry, &localStatus);
2160 }
2161 } else {
2162 *status = localStatus;
2163 }
2164 ures_close(currencyArray);
2165 }
2166 } else {
2167 *status = localStatus;
2168 }
2169
2170 ures_close(currencyMapArray);
2171 }
2172
2173 static const UEnumeration gEnumCurrencyList = {
2174 nullptr,
2175 nullptr,
2176 ucurr_closeCurrencyList,
2177 ucurr_countCurrencyList,
2178 uenum_unextDefault,
2179 ucurr_nextCurrencyList,
2180 ucurr_resetCurrencyList
2181 };
2182 U_CDECL_END
2183
2184
initIsoCodes(UErrorCode & status)2185 static void U_CALLCONV initIsoCodes(UErrorCode &status) {
2186 U_ASSERT(gIsoCodes == nullptr);
2187 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
2188
2189 UHashtable *isoCodes = uhash_open(uhash_hashUChars, uhash_compareUChars, nullptr, &status);
2190 if (U_FAILURE(status)) {
2191 return;
2192 }
2193 uhash_setValueDeleter(isoCodes, deleteIsoCodeEntry);
2194
2195 ucurr_createCurrencyList(isoCodes, &status);
2196 if (U_FAILURE(status)) {
2197 uhash_close(isoCodes);
2198 return;
2199 }
2200 gIsoCodes = isoCodes; // Note: gIsoCodes is const. Once set up here it is never altered,
2201 // and read only access is safe without synchronization.
2202 }
2203
populateCurrSymbolsEquiv(icu::Hashtable * hash,UErrorCode & status)2204 static void populateCurrSymbolsEquiv(icu::Hashtable *hash, UErrorCode &status) {
2205 if (U_FAILURE(status)) { return; }
2206 for (const auto& entry : unisets::kCurrencyEntries) {
2207 UnicodeString exemplar(entry.exemplar);
2208 const UnicodeSet* set = unisets::get(entry.key);
2209 if (set == nullptr) { return; }
2210 UnicodeSetIterator it(*set);
2211 while (it.next()) {
2212 UnicodeString value = it.getString();
2213 if (value == exemplar) {
2214 // No need to mark the exemplar character as an equivalent
2215 continue;
2216 }
2217 makeEquivalent(exemplar, value, hash, status);
2218 if (U_FAILURE(status)) { return; }
2219 }
2220 }
2221 }
2222
initCurrSymbolsEquiv()2223 static void U_CALLCONV initCurrSymbolsEquiv() {
2224 U_ASSERT(gCurrSymbolsEquiv == nullptr);
2225 UErrorCode status = U_ZERO_ERROR;
2226 ucln_common_registerCleanup(UCLN_COMMON_CURRENCY, currency_cleanup);
2227 icu::Hashtable *temp = new icu::Hashtable(status);
2228 if (temp == nullptr) {
2229 return;
2230 }
2231 if (U_FAILURE(status)) {
2232 delete temp;
2233 return;
2234 }
2235 temp->setValueDeleter(deleteUnicode);
2236 populateCurrSymbolsEquiv(temp, status);
2237 if (U_FAILURE(status)) {
2238 delete temp;
2239 return;
2240 }
2241 gCurrSymbolsEquiv = temp;
2242 }
2243
2244 U_CAPI UBool U_EXPORT2
ucurr_isAvailable(const char16_t * isoCode,UDate from,UDate to,UErrorCode * eErrorCode)2245 ucurr_isAvailable(const char16_t* isoCode, UDate from, UDate to, UErrorCode* eErrorCode) {
2246 umtx_initOnce(gIsoCodesInitOnce, &initIsoCodes, *eErrorCode);
2247 if (U_FAILURE(*eErrorCode)) {
2248 return false;
2249 }
2250
2251 IsoCodeEntry* result = (IsoCodeEntry *) uhash_get(gIsoCodes, isoCode);
2252 if (result == nullptr) {
2253 return false;
2254 } else if (from > to) {
2255 *eErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
2256 return false;
2257 } else if ((from > result->to) || (to < result->from)) {
2258 return false;
2259 }
2260 return true;
2261 }
2262
getCurrSymbolsEquiv()2263 static const icu::Hashtable* getCurrSymbolsEquiv() {
2264 umtx_initOnce(gCurrSymbolsEquivInitOnce, &initCurrSymbolsEquiv);
2265 return gCurrSymbolsEquiv;
2266 }
2267
2268 U_CAPI UEnumeration * U_EXPORT2
ucurr_openISOCurrencies(uint32_t currType,UErrorCode * pErrorCode)2269 ucurr_openISOCurrencies(uint32_t currType, UErrorCode *pErrorCode) {
2270 UEnumeration *myEnum = nullptr;
2271 UCurrencyContext *myContext;
2272
2273 myEnum = (UEnumeration*)uprv_malloc(sizeof(UEnumeration));
2274 if (myEnum == nullptr) {
2275 *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
2276 return nullptr;
2277 }
2278 uprv_memcpy(myEnum, &gEnumCurrencyList, sizeof(UEnumeration));
2279 myContext = (UCurrencyContext*)uprv_malloc(sizeof(UCurrencyContext));
2280 if (myContext == nullptr) {
2281 *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
2282 uprv_free(myEnum);
2283 return nullptr;
2284 }
2285 myContext->currType = currType;
2286 myContext->listIdx = 0;
2287 myEnum->context = myContext;
2288 return myEnum;
2289 }
2290
2291 U_CAPI int32_t U_EXPORT2
ucurr_countCurrencies(const char * locale,UDate date,UErrorCode * ec)2292 ucurr_countCurrencies(const char* locale,
2293 UDate date,
2294 UErrorCode* ec)
2295 {
2296 int32_t currCount = 0;
2297
2298 if (ec != nullptr && U_SUCCESS(*ec))
2299 {
2300 // local variables
2301 UErrorCode localStatus = U_ZERO_ERROR;
2302
2303 // get country or country_variant in `id'
2304 CharString id = idForLocale(locale, ec);
2305
2306 if (U_FAILURE(*ec))
2307 {
2308 return 0;
2309 }
2310
2311 // Remove variants, which is only needed for registration.
2312 char *idDelim = strchr(id.data(), VAR_DELIM);
2313 if (idDelim)
2314 {
2315 id.truncate(idDelim - id.data());
2316 }
2317
2318 // Look up the CurrencyMap element in the root bundle.
2319 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
2320 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
2321
2322 // Using the id derived from the local, get the currency data
2323 UResourceBundle *countryArray = ures_getByKey(rb, id.data(), cm, &localStatus);
2324
2325 // process each currency to see which one is valid for the given date
2326 if (U_SUCCESS(localStatus))
2327 {
2328 for (int32_t i=0; i<ures_getSize(countryArray); i++)
2329 {
2330 // get the currency resource
2331 UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, nullptr, &localStatus);
2332
2333 // get the from date
2334 int32_t fromLength = 0;
2335 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", nullptr, &localStatus);
2336 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
2337
2338 int64_t currDate64 = (int64_t)((uint64_t)(fromArray[0]) << 32);
2339 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2340 UDate fromDate = (UDate)currDate64;
2341
2342 if (ures_getSize(currencyRes)> 2)
2343 {
2344 int32_t toLength = 0;
2345 UResourceBundle *toRes = ures_getByKey(currencyRes, "to", nullptr, &localStatus);
2346 const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
2347
2348 currDate64 = (int64_t)toArray[0] << 32;
2349 currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2350 UDate toDate = (UDate)currDate64;
2351
2352 if ((fromDate <= date) && (date < toDate))
2353 {
2354 currCount++;
2355 }
2356
2357 ures_close(toRes);
2358 }
2359 else
2360 {
2361 if (fromDate <= date)
2362 {
2363 currCount++;
2364 }
2365 }
2366
2367 // close open resources
2368 ures_close(currencyRes);
2369 ures_close(fromRes);
2370
2371 } // end For loop
2372 } // end if (U_SUCCESS(localStatus))
2373
2374 ures_close(countryArray);
2375
2376 // Check for errors
2377 if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
2378 {
2379 // There is nothing to fallback to.
2380 // Report the failure/warning if possible.
2381 *ec = localStatus;
2382 }
2383
2384 if (U_SUCCESS(*ec))
2385 {
2386 // no errors
2387 return currCount;
2388 }
2389
2390 }
2391
2392 // If we got here, either error code is invalid or
2393 // some argument passed is no good.
2394 return 0;
2395 }
2396
2397 U_CAPI int32_t U_EXPORT2
ucurr_forLocaleAndDate(const char * locale,UDate date,int32_t index,char16_t * buff,int32_t buffCapacity,UErrorCode * ec)2398 ucurr_forLocaleAndDate(const char* locale,
2399 UDate date,
2400 int32_t index,
2401 char16_t* buff,
2402 int32_t buffCapacity,
2403 UErrorCode* ec)
2404 {
2405 int32_t resLen = 0;
2406 int32_t currIndex = 0;
2407 const char16_t* s = nullptr;
2408
2409 if (ec != nullptr && U_SUCCESS(*ec))
2410 {
2411 // check the arguments passed
2412 if ((buff && buffCapacity) || !buffCapacity )
2413 {
2414 // local variables
2415 UErrorCode localStatus = U_ZERO_ERROR;
2416
2417 // get country or country_variant in `id'
2418 CharString id = idForLocale(locale, ec);
2419 if (U_FAILURE(*ec))
2420 {
2421 return 0;
2422 }
2423
2424 // Remove variants, which is only needed for registration.
2425 char *idDelim = strchr(id.data(), VAR_DELIM);
2426 if (idDelim)
2427 {
2428 id.truncate(idDelim - id.data());
2429 }
2430
2431 // Look up the CurrencyMap element in the root bundle.
2432 UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus);
2433 UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus);
2434
2435 // Using the id derived from the local, get the currency data
2436 UResourceBundle *countryArray = ures_getByKey(rb, id.data(), cm, &localStatus);
2437
2438 // process each currency to see which one is valid for the given date
2439 bool matchFound = false;
2440 if (U_SUCCESS(localStatus))
2441 {
2442 if ((index <= 0) || (index> ures_getSize(countryArray)))
2443 {
2444 // requested index is out of bounds
2445 ures_close(countryArray);
2446 return 0;
2447 }
2448
2449 for (int32_t i=0; i<ures_getSize(countryArray); i++)
2450 {
2451 // get the currency resource
2452 UResourceBundle *currencyRes = ures_getByIndex(countryArray, i, nullptr, &localStatus);
2453 s = ures_getStringByKey(currencyRes, "id", &resLen, &localStatus);
2454
2455 // get the from date
2456 int32_t fromLength = 0;
2457 UResourceBundle *fromRes = ures_getByKey(currencyRes, "from", nullptr, &localStatus);
2458 const int32_t *fromArray = ures_getIntVector(fromRes, &fromLength, &localStatus);
2459
2460 int64_t currDate64 = (int64_t)((uint64_t)fromArray[0] << 32);
2461 currDate64 |= ((int64_t)fromArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2462 UDate fromDate = (UDate)currDate64;
2463
2464 if (ures_getSize(currencyRes)> 2)
2465 {
2466 int32_t toLength = 0;
2467 UResourceBundle *toRes = ures_getByKey(currencyRes, "to", nullptr, &localStatus);
2468 const int32_t *toArray = ures_getIntVector(toRes, &toLength, &localStatus);
2469
2470 currDate64 = (int64_t)toArray[0] << 32;
2471 currDate64 |= ((int64_t)toArray[1] & (int64_t)INT64_C(0x00000000FFFFFFFF));
2472 UDate toDate = (UDate)currDate64;
2473
2474 if ((fromDate <= date) && (date < toDate))
2475 {
2476 currIndex++;
2477 if (currIndex == index)
2478 {
2479 matchFound = true;
2480 }
2481 }
2482
2483 ures_close(toRes);
2484 }
2485 else
2486 {
2487 if (fromDate <= date)
2488 {
2489 currIndex++;
2490 if (currIndex == index)
2491 {
2492 matchFound = true;
2493 }
2494 }
2495 }
2496
2497 // close open resources
2498 ures_close(currencyRes);
2499 ures_close(fromRes);
2500
2501 // check for loop exit
2502 if (matchFound)
2503 {
2504 break;
2505 }
2506
2507 } // end For loop
2508 }
2509
2510 ures_close(countryArray);
2511
2512 // Check for errors
2513 if (*ec == U_ZERO_ERROR || localStatus != U_ZERO_ERROR)
2514 {
2515 // There is nothing to fallback to.
2516 // Report the failure/warning if possible.
2517 *ec = localStatus;
2518 }
2519
2520 if (U_SUCCESS(*ec))
2521 {
2522 // no errors
2523 if((buffCapacity> resLen) && matchFound)
2524 {
2525 // write out the currency value
2526 u_strcpy(buff, s);
2527 }
2528 else
2529 {
2530 return 0;
2531 }
2532 }
2533
2534 // return null terminated currency string
2535 return u_terminateUChars(buff, buffCapacity, resLen, ec);
2536 }
2537 else
2538 {
2539 // illegal argument encountered
2540 *ec = U_ILLEGAL_ARGUMENT_ERROR;
2541 }
2542
2543 }
2544
2545 // If we got here, either error code is invalid or
2546 // some argument passed is no good.
2547 return resLen;
2548 }
2549
2550 static const UEnumeration defaultKeywordValues = {
2551 nullptr,
2552 nullptr,
2553 ulist_close_keyword_values_iterator,
2554 ulist_count_keyword_values,
2555 uenum_unextDefault,
2556 ulist_next_keyword_value,
2557 ulist_reset_keyword_values_iterator
2558 };
2559
ucurr_getKeywordValuesForLocale(const char * key,const char * locale,UBool commonlyUsed,UErrorCode * status)2560 U_CAPI UEnumeration *U_EXPORT2 ucurr_getKeywordValuesForLocale(const char *key, const char *locale, UBool commonlyUsed, UErrorCode* status) {
2561 // Resolve region
2562 CharString prefRegion = ulocimp_getRegionForSupplementalData(locale, true, *status);
2563
2564 // Read value from supplementalData
2565 UList *values = ulist_createEmptyList(status);
2566 UList *otherValues = ulist_createEmptyList(status);
2567 UEnumeration *en = (UEnumeration *)uprv_malloc(sizeof(UEnumeration));
2568 if (U_FAILURE(*status) || en == nullptr) {
2569 if (en == nullptr) {
2570 *status = U_MEMORY_ALLOCATION_ERROR;
2571 } else {
2572 uprv_free(en);
2573 }
2574 ulist_deleteList(values);
2575 ulist_deleteList(otherValues);
2576 return nullptr;
2577 }
2578 memcpy(en, &defaultKeywordValues, sizeof(UEnumeration));
2579 en->context = values;
2580
2581 UResourceBundle *bundle = ures_openDirect(U_ICUDATA_CURR, "supplementalData", status);
2582 ures_getByKey(bundle, "CurrencyMap", bundle, status);
2583 UResourceBundle bundlekey, regbndl, curbndl, to;
2584 ures_initStackObject(&bundlekey);
2585 ures_initStackObject(®bndl);
2586 ures_initStackObject(&curbndl);
2587 ures_initStackObject(&to);
2588
2589 while (U_SUCCESS(*status) && ures_hasNext(bundle)) {
2590 ures_getNextResource(bundle, &bundlekey, status);
2591 if (U_FAILURE(*status)) {
2592 break;
2593 }
2594 const char *region = ures_getKey(&bundlekey);
2595 UBool isPrefRegion = prefRegion == region;
2596 if (!isPrefRegion && commonlyUsed) {
2597 // With commonlyUsed=true, we do not put
2598 // currencies for other regions in the
2599 // result list.
2600 continue;
2601 }
2602 ures_getByKey(bundle, region, ®bndl, status);
2603 if (U_FAILURE(*status)) {
2604 break;
2605 }
2606 while (U_SUCCESS(*status) && ures_hasNext(®bndl)) {
2607 ures_getNextResource(®bndl, &curbndl, status);
2608 if (ures_getType(&curbndl) != URES_TABLE) {
2609 // Currently, an empty ARRAY is mixed in.
2610 continue;
2611 }
2612 char *curID = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
2613 int32_t curIDLength = ULOC_KEYWORDS_CAPACITY;
2614 if (curID == nullptr) {
2615 *status = U_MEMORY_ALLOCATION_ERROR;
2616 break;
2617 }
2618
2619 #if U_CHARSET_FAMILY==U_ASCII_FAMILY
2620 ures_getUTF8StringByKey(&curbndl, "id", curID, &curIDLength, true, status);
2621 /* optimize - use the utf-8 string */
2622 #else
2623 {
2624 const char16_t* defString = ures_getStringByKey(&curbndl, "id", &curIDLength, status);
2625 if(U_SUCCESS(*status)) {
2626 if(curIDLength+1 > ULOC_KEYWORDS_CAPACITY) {
2627 *status = U_BUFFER_OVERFLOW_ERROR;
2628 } else {
2629 u_UCharsToChars(defString, curID, curIDLength+1);
2630 }
2631 }
2632 }
2633 #endif
2634
2635 if (U_FAILURE(*status)) {
2636 break;
2637 }
2638 UBool hasTo = false;
2639 ures_getByKey(&curbndl, "to", &to, status);
2640 if (U_FAILURE(*status)) {
2641 // Do nothing here...
2642 *status = U_ZERO_ERROR;
2643 } else {
2644 hasTo = true;
2645 }
2646 if (isPrefRegion && !hasTo && !ulist_containsString(values, curID, (int32_t)uprv_strlen(curID))) {
2647 // Currently active currency for the target country
2648 ulist_addItemEndList(values, curID, true, status);
2649 } else if (!ulist_containsString(otherValues, curID, (int32_t)uprv_strlen(curID)) && !commonlyUsed) {
2650 ulist_addItemEndList(otherValues, curID, true, status);
2651 } else {
2652 uprv_free(curID);
2653 }
2654 }
2655
2656 }
2657 if (U_SUCCESS(*status)) {
2658 if (commonlyUsed) {
2659 if (ulist_getListSize(values) == 0) {
2660 // This could happen if no valid region is supplied in the input
2661 // locale. In this case, we use the CLDR's default.
2662 uenum_close(en);
2663 en = ucurr_getKeywordValuesForLocale(key, "und", true, status);
2664 }
2665 } else {
2666 // Consolidate the list
2667 char *value = nullptr;
2668 ulist_resetList(otherValues);
2669 while ((value = (char *)ulist_getNext(otherValues)) != nullptr) {
2670 if (!ulist_containsString(values, value, (int32_t)uprv_strlen(value))) {
2671 char *tmpValue = (char *)uprv_malloc(sizeof(char) * ULOC_KEYWORDS_CAPACITY);
2672 uprv_memcpy(tmpValue, value, uprv_strlen(value) + 1);
2673 ulist_addItemEndList(values, tmpValue, true, status);
2674 if (U_FAILURE(*status)) {
2675 break;
2676 }
2677 }
2678 }
2679 }
2680
2681 ulist_resetList((UList *)(en->context));
2682 } else {
2683 ulist_deleteList(values);
2684 uprv_free(en);
2685 values = nullptr;
2686 en = nullptr;
2687 }
2688 ures_close(&to);
2689 ures_close(&curbndl);
2690 ures_close(®bndl);
2691 ures_close(&bundlekey);
2692 ures_close(bundle);
2693
2694 ulist_deleteList(otherValues);
2695
2696 return en;
2697 }
2698
2699
2700 U_CAPI int32_t U_EXPORT2
ucurr_getNumericCode(const char16_t * currency)2701 ucurr_getNumericCode(const char16_t* currency) {
2702 int32_t code = 0;
2703 if (currency && u_strlen(currency) == ISO_CURRENCY_CODE_LENGTH) {
2704 UErrorCode status = U_ZERO_ERROR;
2705
2706 UResourceBundle *bundle = ures_openDirect(nullptr, "currencyNumericCodes", &status);
2707 ures_getByKey(bundle, "codeMap", bundle, &status);
2708 if (U_SUCCESS(status)) {
2709 char alphaCode[ISO_CURRENCY_CODE_LENGTH+1];
2710 myUCharsToChars(alphaCode, currency);
2711 T_CString_toUpperCase(alphaCode);
2712 ures_getByKey(bundle, alphaCode, bundle, &status);
2713 int tmpCode = ures_getInt(bundle, &status);
2714 if (U_SUCCESS(status)) {
2715 code = tmpCode;
2716 }
2717 }
2718 ures_close(bundle);
2719 }
2720 return code;
2721 }
2722 #endif /* #if !UCONFIG_NO_FORMATTING */
2723
2724 //eof
2725