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