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