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