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