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