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