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