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