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