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