Coverage Report

Created: 2018-09-25 14:53

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