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