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