Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/common/locid.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) 1997-2016, International Business Machines
6
 *   Corporation and others.  All Rights Reserved.
7
 **********************************************************************
8
*
9
* File locid.cpp
10
*
11
* Created by: Richard Gillam
12
*
13
* Modification History:
14
*
15
*   Date        Name        Description
16
*   02/11/97    aliu        Changed gLocPath to fgDataDirectory and added
17
*                           methods to get and set it.
18
*   04/02/97    aliu        Made operator!= inline; fixed return value
19
*                           of getName().
20
*   04/15/97    aliu        Cleanup for AIX/Win32.
21
*   04/24/97    aliu        Numerous changes per code review.
22
*   08/18/98    stephen     Changed getDisplayName()
23
*                           Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE
24
*                           Added getISOCountries(), getISOLanguages(),
25
*                           getLanguagesForCountry()
26
*   03/16/99    bertrand    rehaul.
27
*   07/21/99    stephen     Added U_CFUNC setDefault
28
*   11/09/99    weiv        Added const char * getName() const;
29
*   04/12/00    srl         removing unicodestring api's and cached hash code
30
*   08/10/01    grhoten     Change the static Locales to accessor functions
31
******************************************************************************
32
*/
33
34
#include <utility>
35
36
#include "unicode/bytestream.h"
37
#include "unicode/locid.h"
38
#include "unicode/localebuilder.h"
39
#include "unicode/strenum.h"
40
#include "unicode/stringpiece.h"
41
#include "unicode/uloc.h"
42
#include "unicode/ures.h"
43
44
#include "bytesinkutil.h"
45
#include "charstr.h"
46
#include "charstrmap.h"
47
#include "cmemory.h"
48
#include "cstring.h"
49
#include "mutex.h"
50
#include "putilimp.h"
51
#include "uassert.h"
52
#include "ucln_cmn.h"
53
#include "uhash.h"
54
#include "ulocimp.h"
55
#include "umutex.h"
56
#include "uniquecharstr.h"
57
#include "ustr_imp.h"
58
#include "uvector.h"
59
60
U_CDECL_BEGIN
61
static UBool U_CALLCONV locale_cleanup(void);
62
U_CDECL_END
63
64
U_NAMESPACE_BEGIN
65
66
static Locale   *gLocaleCache = NULL;
67
static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER;
68
69
// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale.
70
static UMutex gDefaultLocaleMutex;
71
static UHashtable *gDefaultLocalesHashT = NULL;
72
static Locale *gDefaultLocale = NULL;
73
74
/**
75
 * \def ULOC_STRING_LIMIT
76
 * strings beyond this value crash in CharString
77
 */
78
0
#define ULOC_STRING_LIMIT 357913941
79
80
U_NAMESPACE_END
81
82
typedef enum ELocalePos {
83
    eENGLISH,
84
    eFRENCH,
85
    eGERMAN,
86
    eITALIAN,
87
    eJAPANESE,
88
    eKOREAN,
89
    eCHINESE,
90
91
    eFRANCE,
92
    eGERMANY,
93
    eITALY,
94
    eJAPAN,
95
    eKOREA,
96
    eCHINA,      /* Alias for PRC */
97
    eTAIWAN,
98
    eUK,
99
    eUS,
100
    eCANADA,
101
    eCANADA_FRENCH,
102
    eROOT,
103
104
105
    //eDEFAULT,
106
    eMAX_LOCALES
107
} ELocalePos;
108
109
U_CDECL_BEGIN
110
//
111
// Deleter function for Locales owned by the default Locale hash table/
112
//
113
static void U_CALLCONV
114
0
deleteLocale(void *obj) {
115
0
    delete (icu::Locale *) obj;
116
0
}
117
118
static UBool U_CALLCONV locale_cleanup(void)
119
0
{
120
0
    U_NAMESPACE_USE
121
122
0
    delete [] gLocaleCache;
123
0
    gLocaleCache = NULL;
124
0
    gLocaleCacheInitOnce.reset();
125
126
0
    if (gDefaultLocalesHashT) {
127
0
        uhash_close(gDefaultLocalesHashT);   // Automatically deletes all elements, using deleter func.
128
0
        gDefaultLocalesHashT = NULL;
129
0
    }
130
0
    gDefaultLocale = NULL;
131
0
    return TRUE;
132
0
}
133
134
135
0
static void U_CALLCONV locale_init(UErrorCode &status) {
136
0
    U_NAMESPACE_USE
137
138
0
    U_ASSERT(gLocaleCache == NULL);
139
0
    gLocaleCache = new Locale[(int)eMAX_LOCALES];
140
0
    if (gLocaleCache == NULL) {
141
0
        status = U_MEMORY_ALLOCATION_ERROR;
142
0
        return;
143
0
    }
144
0
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
145
0
    gLocaleCache[eROOT]          = Locale("");
146
0
    gLocaleCache[eENGLISH]       = Locale("en");
147
0
    gLocaleCache[eFRENCH]        = Locale("fr");
148
0
    gLocaleCache[eGERMAN]        = Locale("de");
149
0
    gLocaleCache[eITALIAN]       = Locale("it");
150
0
    gLocaleCache[eJAPANESE]      = Locale("ja");
151
0
    gLocaleCache[eKOREAN]        = Locale("ko");
152
0
    gLocaleCache[eCHINESE]       = Locale("zh");
153
0
    gLocaleCache[eFRANCE]        = Locale("fr", "FR");
154
0
    gLocaleCache[eGERMANY]       = Locale("de", "DE");
155
0
    gLocaleCache[eITALY]         = Locale("it", "IT");
156
0
    gLocaleCache[eJAPAN]         = Locale("ja", "JP");
157
0
    gLocaleCache[eKOREA]         = Locale("ko", "KR");
158
0
    gLocaleCache[eCHINA]         = Locale("zh", "CN");
159
0
    gLocaleCache[eTAIWAN]        = Locale("zh", "TW");
160
0
    gLocaleCache[eUK]            = Locale("en", "GB");
161
0
    gLocaleCache[eUS]            = Locale("en", "US");
162
0
    gLocaleCache[eCANADA]        = Locale("en", "CA");
163
0
    gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA");
164
0
}
165
166
U_CDECL_END
167
168
U_NAMESPACE_BEGIN
169
170
0
Locale *locale_set_default_internal(const char *id, UErrorCode& status) {
171
    // Synchronize this entire function.
172
0
    Mutex lock(&gDefaultLocaleMutex);
173
174
0
    UBool canonicalize = FALSE;
175
176
    // If given a NULL string for the locale id, grab the default
177
    //   name from the system.
178
    //   (Different from most other locale APIs, where a null name means use
179
    //    the current ICU default locale.)
180
0
    if (id == NULL) {
181
0
        id = uprv_getDefaultLocaleID();   // This function not thread safe? TODO: verify.
182
0
        canonicalize = TRUE; // always canonicalize host ID
183
0
    }
184
185
0
    CharString localeNameBuf;
186
0
    {
187
0
        CharStringByteSink sink(&localeNameBuf);
188
0
        if (canonicalize) {
189
0
            ulocimp_canonicalize(id, sink, &status);
190
0
        } else {
191
0
            ulocimp_getName(id, sink, &status);
192
0
        }
193
0
    }
194
195
0
    if (U_FAILURE(status)) {
196
0
        return gDefaultLocale;
197
0
    }
198
199
0
    if (gDefaultLocalesHashT == NULL) {
200
0
        gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
201
0
        if (U_FAILURE(status)) {
202
0
            return gDefaultLocale;
203
0
        }
204
0
        uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale);
205
0
        ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
206
0
    }
207
208
0
    Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf.data());
209
0
    if (newDefault == NULL) {
210
0
        newDefault = new Locale(Locale::eBOGUS);
211
0
        if (newDefault == NULL) {
212
0
            status = U_MEMORY_ALLOCATION_ERROR;
213
0
            return gDefaultLocale;
214
0
        }
215
0
        newDefault->init(localeNameBuf.data(), FALSE);
216
0
        uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status);
217
0
        if (U_FAILURE(status)) {
218
0
            return gDefaultLocale;
219
0
        }
220
0
    }
221
0
    gDefaultLocale = newDefault;
222
0
    return gDefaultLocale;
223
0
}
224
225
U_NAMESPACE_END
226
227
/* sfb 07/21/99 */
228
U_CFUNC void
229
locale_set_default(const char *id)
230
0
{
231
0
    U_NAMESPACE_USE
232
0
    UErrorCode status = U_ZERO_ERROR;
233
0
    locale_set_default_internal(id, status);
234
0
}
235
/* end */
236
237
U_CFUNC const char *
238
locale_get_default(void)
239
0
{
240
0
    U_NAMESPACE_USE
241
0
    return Locale::getDefault().getName();
242
0
}
243
244
245
U_NAMESPACE_BEGIN
246
247
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)
248
249
/*Character separating the posix id fields*/
250
// '_'
251
// In the platform codepage.
252
0
#define SEP_CHAR '_'
253
0
#define NULL_CHAR '\0'
254
255
Locale::~Locale()
256
0
{
257
0
    if ((baseName != fullName) && (baseName != fullNameBuffer)) {
258
0
        uprv_free(baseName);
259
0
    }
260
0
    baseName = NULL;
261
    /*if fullName is on the heap, we free it*/
262
0
    if (fullName != fullNameBuffer)
263
0
    {
264
0
        uprv_free(fullName);
265
0
        fullName = NULL;
266
0
    }
267
0
}
268
269
Locale::Locale()
270
0
    : UObject(), fullName(fullNameBuffer), baseName(NULL)
271
0
{
272
0
    init(NULL, FALSE);
273
0
}
274
275
/*
276
 * Internal constructor to allow construction of a locale object with
277
 *   NO side effects.   (Default constructor tries to get
278
 *   the default locale.)
279
 */
280
Locale::Locale(Locale::ELocaleType)
281
0
    : UObject(), fullName(fullNameBuffer), baseName(NULL)
282
0
{
283
0
    setToBogus();
284
0
}
285
286
287
Locale::Locale( const   char * newLanguage,
288
                const   char * newCountry,
289
                const   char * newVariant,
290
                const   char * newKeywords)
291
0
    : UObject(), fullName(fullNameBuffer), baseName(NULL)
292
0
{
293
0
    if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) )
294
0
    {
295
0
        init(NULL, FALSE); /* shortcut */
296
0
    }
297
0
    else
298
0
    {
299
0
        UErrorCode status = U_ZERO_ERROR;
300
0
        int32_t lsize = 0;
301
0
        int32_t csize = 0;
302
0
        int32_t vsize = 0;
303
0
        int32_t ksize = 0;
304
305
        // Check the sizes of the input strings.
306
307
        // Language
308
0
        if ( newLanguage != NULL )
309
0
        {
310
0
            lsize = (int32_t)uprv_strlen(newLanguage);
311
0
            if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap
312
0
                setToBogus();
313
0
                return;
314
0
            }
315
0
        }
316
317
0
        CharString togo(newLanguage, lsize, status); // start with newLanguage
318
319
        // _Country
320
0
        if ( newCountry != NULL )
321
0
        {
322
0
            csize = (int32_t)uprv_strlen(newCountry);
323
0
            if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap
324
0
                setToBogus();
325
0
                return;
326
0
            }
327
0
        }
328
329
        // _Variant
330
0
        if ( newVariant != NULL )
331
0
        {
332
            // remove leading _'s
333
0
            while(newVariant[0] == SEP_CHAR)
334
0
            {
335
0
                newVariant++;
336
0
            }
337
338
            // remove trailing _'s
339
0
            vsize = (int32_t)uprv_strlen(newVariant);
340
0
            if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap
341
0
                setToBogus();
342
0
                return;
343
0
            }
344
0
            while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) )
345
0
            {
346
0
                vsize--;
347
0
            }
348
0
        }
349
350
0
        if ( newKeywords != NULL)
351
0
        {
352
0
            ksize = (int32_t)uprv_strlen(newKeywords);
353
0
            if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) {
354
0
              setToBogus();
355
0
              return;
356
0
            }
357
0
        }
358
359
        // We've checked the input sizes, now build up the full locale string..
360
361
        // newLanguage is already copied
362
363
0
        if ( ( vsize != 0 ) || (csize != 0) )  // at least:  __v
364
0
        {                                      //            ^
365
0
            togo.append(SEP_CHAR, status);
366
0
        }
367
368
0
        if ( csize != 0 )
369
0
        {
370
0
            togo.append(newCountry, status);
371
0
        }
372
373
0
        if ( vsize != 0)
374
0
        {
375
0
            togo.append(SEP_CHAR, status)
376
0
                .append(newVariant, vsize, status);
377
0
        }
378
379
0
        if ( ksize != 0)
380
0
        {
381
0
            if (uprv_strchr(newKeywords, '=')) {
382
0
                togo.append('@', status); /* keyword parsing */
383
0
            }
384
0
            else {
385
0
                togo.append('_', status); /* Variant parsing with a script */
386
0
                if ( vsize == 0) {
387
0
                    togo.append('_', status); /* No country found */
388
0
                }
389
0
            }
390
0
            togo.append(newKeywords, status);
391
0
        }
392
393
0
        if (U_FAILURE(status)) {
394
            // Something went wrong with appending, etc.
395
0
            setToBogus();
396
0
            return;
397
0
        }
398
        // Parse it, because for example 'language' might really be a complete
399
        // string.
400
0
        init(togo.data(), FALSE);
401
0
    }
402
0
}
403
404
Locale::Locale(const Locale &other)
405
0
    : UObject(other), fullName(fullNameBuffer), baseName(NULL)
406
0
{
407
0
    *this = other;
408
0
}
409
410
Locale::Locale(Locale&& other) U_NOEXCEPT
411
0
    : UObject(other), fullName(fullNameBuffer), baseName(fullName) {
412
0
  *this = std::move(other);
413
0
}
414
415
0
Locale& Locale::operator=(const Locale& other) {
416
0
    if (this == &other) {
417
0
        return *this;
418
0
    }
419
420
0
    setToBogus();
421
422
0
    if (other.fullName == other.fullNameBuffer) {
423
0
        uprv_strcpy(fullNameBuffer, other.fullNameBuffer);
424
0
    } else if (other.fullName == nullptr) {
425
0
        fullName = nullptr;
426
0
    } else {
427
0
        fullName = uprv_strdup(other.fullName);
428
0
        if (fullName == nullptr) return *this;
429
0
    }
430
431
0
    if (other.baseName == other.fullName) {
432
0
        baseName = fullName;
433
0
    } else if (other.baseName != nullptr) {
434
0
        baseName = uprv_strdup(other.baseName);
435
0
        if (baseName == nullptr) return *this;
436
0
    }
437
438
0
    uprv_strcpy(language, other.language);
439
0
    uprv_strcpy(script, other.script);
440
0
    uprv_strcpy(country, other.country);
441
442
0
    variantBegin = other.variantBegin;
443
0
    fIsBogus = other.fIsBogus;
444
445
0
    return *this;
446
0
}
447
448
0
Locale& Locale::operator=(Locale&& other) U_NOEXCEPT {
449
0
    if ((baseName != fullName) && (baseName != fullNameBuffer)) uprv_free(baseName);
450
0
    if (fullName != fullNameBuffer) uprv_free(fullName);
451
452
0
    if (other.fullName == other.fullNameBuffer || other.baseName == other.fullNameBuffer) {
453
0
        uprv_strcpy(fullNameBuffer, other.fullNameBuffer);
454
0
    }
455
0
    if (other.fullName == other.fullNameBuffer) {
456
0
        fullName = fullNameBuffer;
457
0
    } else {
458
0
        fullName = other.fullName;
459
0
    }
460
461
0
    if (other.baseName == other.fullNameBuffer) {
462
0
        baseName = fullNameBuffer;
463
0
    } else if (other.baseName == other.fullName) {
464
0
        baseName = fullName;
465
0
    } else {
466
0
        baseName = other.baseName;
467
0
    }
468
469
0
    uprv_strcpy(language, other.language);
470
0
    uprv_strcpy(script, other.script);
471
0
    uprv_strcpy(country, other.country);
472
473
0
    variantBegin = other.variantBegin;
474
0
    fIsBogus = other.fIsBogus;
475
476
0
    other.baseName = other.fullName = other.fullNameBuffer;
477
478
0
    return *this;
479
0
}
480
481
Locale *
482
0
Locale::clone() const {
483
0
    return new Locale(*this);
484
0
}
485
486
bool
487
Locale::operator==( const   Locale& other) const
488
0
{
489
0
    return (uprv_strcmp(other.fullName, fullName) == 0);
490
0
}
491
492
namespace {
493
494
UInitOnce gKnownCanonicalizedInitOnce = U_INITONCE_INITIALIZER;
495
UHashtable *gKnownCanonicalized = nullptr;
496
497
static const char* const KNOWN_CANONICALIZED[] = {
498
    "c",
499
    // Commonly used locales known are already canonicalized
500
    "af", "af_ZA", "am", "am_ET", "ar", "ar_001", "as", "as_IN", "az", "az_AZ",
501
    "be", "be_BY", "bg", "bg_BG", "bn", "bn_IN", "bs", "bs_BA", "ca", "ca_ES",
502
    "cs", "cs_CZ", "cy", "cy_GB", "da", "da_DK", "de", "de_DE", "el", "el_GR",
503
    "en", "en_GB", "en_US", "es", "es_419", "es_ES", "et", "et_EE", "eu",
504
    "eu_ES", "fa", "fa_IR", "fi", "fi_FI", "fil", "fil_PH", "fr", "fr_FR",
505
    "ga", "ga_IE", "gl", "gl_ES", "gu", "gu_IN", "he", "he_IL", "hi", "hi_IN",
506
    "hr", "hr_HR", "hu", "hu_HU", "hy", "hy_AM", "id", "id_ID", "is", "is_IS",
507
    "it", "it_IT", "ja", "ja_JP", "jv", "jv_ID", "ka", "ka_GE", "kk", "kk_KZ",
508
    "km", "km_KH", "kn", "kn_IN", "ko", "ko_KR", "ky", "ky_KG", "lo", "lo_LA",
509
    "lt", "lt_LT", "lv", "lv_LV", "mk", "mk_MK", "ml", "ml_IN", "mn", "mn_MN",
510
    "mr", "mr_IN", "ms", "ms_MY", "my", "my_MM", "nb", "nb_NO", "ne", "ne_NP",
511
    "nl", "nl_NL", "no", "or", "or_IN", "pa", "pa_IN", "pl", "pl_PL", "ps", "ps_AF",
512
    "pt", "pt_BR", "pt_PT", "ro", "ro_RO", "ru", "ru_RU", "sd", "sd_IN", "si",
513
    "si_LK", "sk", "sk_SK", "sl", "sl_SI", "so", "so_SO", "sq", "sq_AL", "sr",
514
    "sr_Cyrl_RS", "sr_Latn", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "ta",
515
    "ta_IN", "te", "te_IN", "th", "th_TH", "tk", "tk_TM", "tr", "tr_TR", "uk",
516
    "uk_UA", "ur", "ur_PK", "uz", "uz_UZ", "vi", "vi_VN", "yue", "yue_Hant",
517
    "yue_Hant_HK", "yue_HK", "zh", "zh_CN", "zh_Hans", "zh_Hans_CN", "zh_Hant",
518
    "zh_Hant_TW", "zh_TW", "zu", "zu_ZA"
519
};
520
521
0
static UBool U_CALLCONV cleanupKnownCanonicalized() {
522
0
    gKnownCanonicalizedInitOnce.reset();
523
0
    if (gKnownCanonicalized) { uhash_close(gKnownCanonicalized); }
524
0
    return TRUE;
525
0
}
526
527
0
static void U_CALLCONV loadKnownCanonicalized(UErrorCode &status) {
528
0
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KNOWN_CANONICALIZED,
529
0
                                cleanupKnownCanonicalized);
530
0
    LocalUHashtablePointer newKnownCanonicalizedMap(
531
0
        uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status));
532
0
    for (int32_t i = 0;
533
0
            U_SUCCESS(status) && i < UPRV_LENGTHOF(KNOWN_CANONICALIZED);
534
0
            i++) {
535
0
        uhash_puti(newKnownCanonicalizedMap.getAlias(),
536
0
                   (void*)KNOWN_CANONICALIZED[i],
537
0
                   1, &status);
538
0
    }
539
0
    if (U_FAILURE(status)) {
540
0
        return;
541
0
    }
542
543
0
    gKnownCanonicalized = newKnownCanonicalizedMap.orphan();
544
0
}
545
546
class AliasData;
547
548
/**
549
 * A Builder class to build the alias data.
550
 */
551
class AliasDataBuilder {
552
public:
553
0
    AliasDataBuilder() {
554
0
    }
555
556
    // Build the AliasData from resource.
557
    AliasData* build(UErrorCode &status);
558
559
private:
560
    void readAlias(UResourceBundle* alias,
561
                   UniqueCharStrings* strings,
562
                   LocalMemory<const char*>& types,
563
                   LocalMemory<int32_t>& replacementIndexes,
564
                   int32_t &length,
565
                   void (*checkType)(const char* type),
566
                   void (*checkReplacement)(const UnicodeString& replacement),
567
                   UErrorCode &status);
568
569
    // Read the languageAlias data from alias to
570
    // strings+types+replacementIndexes
571
    // The number of record will be stored into length.
572
    // Allocate length items for types, to store the type field.
573
    // Allocate length items for replacementIndexes,
574
    // to store the index in the strings for the replacement script.
575
    void readLanguageAlias(UResourceBundle* alias,
576
                           UniqueCharStrings* strings,
577
                           LocalMemory<const char*>& types,
578
                           LocalMemory<int32_t>& replacementIndexes,
579
                           int32_t &length,
580
                           UErrorCode &status);
581
582
    // Read the scriptAlias data from alias to
583
    // strings+types+replacementIndexes
584
    // Allocate length items for types, to store the type field.
585
    // Allocate length items for replacementIndexes,
586
    // to store the index in the strings for the replacement script.
587
    void readScriptAlias(UResourceBundle* alias,
588
                         UniqueCharStrings* strings,
589
                         LocalMemory<const char*>& types,
590
                         LocalMemory<int32_t>& replacementIndexes,
591
                         int32_t &length, UErrorCode &status);
592
593
    // Read the territoryAlias data from alias to
594
    // strings+types+replacementIndexes
595
    // Allocate length items for types, to store the type field.
596
    // Allocate length items for replacementIndexes,
597
    // to store the index in the strings for the replacement script.
598
    void readTerritoryAlias(UResourceBundle* alias,
599
                            UniqueCharStrings* strings,
600
                            LocalMemory<const char*>& types,
601
                            LocalMemory<int32_t>& replacementIndexes,
602
                            int32_t &length, UErrorCode &status);
603
604
    // Read the variantAlias data from alias to
605
    // strings+types+replacementIndexes
606
    // Allocate length items for types, to store the type field.
607
    // Allocate length items for replacementIndexes,
608
    // to store the index in the strings for the replacement variant.
609
    void readVariantAlias(UResourceBundle* alias,
610
                          UniqueCharStrings* strings,
611
                          LocalMemory<const char*>& types,
612
                          LocalMemory<int32_t>& replacementIndexes,
613
                          int32_t &length, UErrorCode &status);
614
615
    // Read the subdivisionAlias data from alias to
616
    // strings+types+replacementIndexes
617
    // Allocate length items for types, to store the type field.
618
    // Allocate length items for replacementIndexes,
619
    // to store the index in the strings for the replacement variant.
620
    void readSubdivisionAlias(UResourceBundle* alias,
621
                          UniqueCharStrings* strings,
622
                          LocalMemory<const char*>& types,
623
                          LocalMemory<int32_t>& replacementIndexes,
624
                          int32_t &length, UErrorCode &status);
625
};
626
627
/**
628
 * A class to hold the Alias Data.
629
 */
630
class AliasData : public UMemory {
631
public:
632
0
    static const AliasData* singleton(UErrorCode& status) {
633
0
        if (U_FAILURE(status)) {
634
            // Do not get into loadData if the status already has error.
635
0
            return nullptr;
636
0
        }
637
0
        umtx_initOnce(AliasData::gInitOnce, &AliasData::loadData, status);
638
0
        return gSingleton;
639
0
    }
640
641
0
    const CharStringMap& languageMap() const { return language; }
642
0
    const CharStringMap& scriptMap() const { return script; }
643
0
    const CharStringMap& territoryMap() const { return territory; }
644
0
    const CharStringMap& variantMap() const { return variant; }
645
0
    const CharStringMap& subdivisionMap() const { return subdivision; }
646
647
    static void U_CALLCONV loadData(UErrorCode &status);
648
    static UBool U_CALLCONV cleanup();
649
650
    static UInitOnce gInitOnce;
651
652
private:
653
    AliasData(CharStringMap languageMap,
654
              CharStringMap scriptMap,
655
              CharStringMap territoryMap,
656
              CharStringMap variantMap,
657
              CharStringMap subdivisionMap,
658
              CharString* strings)
659
0
        : language(std::move(languageMap)),
660
0
          script(std::move(scriptMap)),
661
0
          territory(std::move(territoryMap)),
662
0
          variant(std::move(variantMap)),
663
0
          subdivision(std::move(subdivisionMap)),
664
0
          strings(strings) {
665
0
    }
666
667
0
    ~AliasData() {
668
0
        delete strings;
669
0
    }
670
671
    static const AliasData* gSingleton;
672
673
    CharStringMap language;
674
    CharStringMap script;
675
    CharStringMap territory;
676
    CharStringMap variant;
677
    CharStringMap subdivision;
678
    CharString* strings;
679
680
    friend class AliasDataBuilder;
681
};
682
683
684
const AliasData* AliasData::gSingleton = nullptr;
685
UInitOnce AliasData::gInitOnce = U_INITONCE_INITIALIZER;
686
687
UBool U_CALLCONV
688
AliasData::cleanup()
689
0
{
690
0
    gInitOnce.reset();
691
0
    delete gSingleton;
692
0
    return TRUE;
693
0
}
694
695
void
696
AliasDataBuilder::readAlias(
697
        UResourceBundle* alias,
698
        UniqueCharStrings* strings,
699
        LocalMemory<const char*>& types,
700
        LocalMemory<int32_t>& replacementIndexes,
701
        int32_t &length,
702
        void (*checkType)(const char* type),
703
        void (*checkReplacement)(const UnicodeString& replacement),
704
0
        UErrorCode &status) {
705
0
    if (U_FAILURE(status)) {
706
0
        return;
707
0
    }
708
0
    length = ures_getSize(alias);
709
0
    const char** rawTypes = types.allocateInsteadAndCopy(length);
710
0
    if (rawTypes == nullptr) {
711
0
        status = U_MEMORY_ALLOCATION_ERROR;
712
0
        return;
713
0
    }
714
0
    int32_t* rawIndexes = replacementIndexes.allocateInsteadAndCopy(length);
715
0
    if (rawIndexes == nullptr) {
716
0
        status = U_MEMORY_ALLOCATION_ERROR;
717
0
        return;
718
0
    }
719
0
    int i = 0;
720
0
    while (ures_hasNext(alias)) {
721
0
        LocalUResourceBundlePointer res(
722
0
            ures_getNextResource(alias, nullptr, &status));
723
0
        const char* aliasFrom = ures_getKey(res.getAlias());
724
0
        UnicodeString aliasTo =
725
0
            ures_getUnicodeStringByKey(res.getAlias(), "replacement", &status);
726
727
0
        checkType(aliasFrom);
728
0
        checkReplacement(aliasTo);
729
730
0
        rawTypes[i] = aliasFrom;
731
0
        rawIndexes[i] = strings->add(aliasTo, status);
732
0
        i++;
733
0
    }
734
0
}
735
736
/**
737
 * Read the languageAlias data from alias to strings+types+replacementIndexes.
738
 * Allocate length items for types, to store the type field. Allocate length
739
 * items for replacementIndexes, to store the index in the strings for the
740
 * replacement language.
741
 */
742
void
743
AliasDataBuilder::readLanguageAlias(
744
        UResourceBundle* alias,
745
        UniqueCharStrings* strings,
746
        LocalMemory<const char*>& types,
747
        LocalMemory<int32_t>& replacementIndexes,
748
        int32_t &length,
749
        UErrorCode &status)
750
0
{
751
0
    return readAlias(
752
0
        alias, strings, types, replacementIndexes, length,
753
#if U_DEBUG
754
        [](const char* type) {
755
            // Assert the aliasFrom only contains the following possibilities
756
            // language_REGION_variant
757
            // language_REGION
758
            // language_variant
759
            // language
760
            // und_variant
761
            Locale test(type);
762
            // Assert no script in aliasFrom
763
            U_ASSERT(test.getScript()[0] == '\0');
764
            // Assert when language is und, no REGION in aliasFrom.
765
            U_ASSERT(test.getLanguage()[0] != '\0' || test.getCountry()[0] == '\0');
766
        },
767
#else
768
0
        [](const char*) {},
769
0
#endif
770
0
        [](const UnicodeString&) {}, status);
771
0
}
772
773
/**
774
 * Read the scriptAlias data from alias to strings+types+replacementIndexes.
775
 * Allocate length items for types, to store the type field. Allocate length
776
 * items for replacementIndexes, to store the index in the strings for the
777
 * replacement script.
778
 */
779
void
780
AliasDataBuilder::readScriptAlias(
781
        UResourceBundle* alias,
782
        UniqueCharStrings* strings,
783
        LocalMemory<const char*>& types,
784
        LocalMemory<int32_t>& replacementIndexes,
785
        int32_t &length,
786
        UErrorCode &status)
787
0
{
788
0
    return readAlias(
789
0
        alias, strings, types, replacementIndexes, length,
790
#if U_DEBUG
791
        [](const char* type) {
792
            U_ASSERT(uprv_strlen(type) == 4);
793
        },
794
        [](const UnicodeString& replacement) {
795
            U_ASSERT(replacement.length() == 4);
796
        },
797
#else
798
0
        [](const char*) {},
799
0
        [](const UnicodeString&) { },
800
0
#endif
801
0
        status);
802
0
}
803
804
/**
805
 * Read the territoryAlias data from alias to strings+types+replacementIndexes.
806
 * Allocate length items for types, to store the type field. Allocate length
807
 * items for replacementIndexes, to store the index in the strings for the
808
 * replacement regions.
809
 */
810
void
811
AliasDataBuilder::readTerritoryAlias(
812
        UResourceBundle* alias,
813
        UniqueCharStrings* strings,
814
        LocalMemory<const char*>& types,
815
        LocalMemory<int32_t>& replacementIndexes,
816
        int32_t &length,
817
        UErrorCode &status)
818
0
{
819
0
    return readAlias(
820
0
        alias, strings, types, replacementIndexes, length,
821
#if U_DEBUG
822
        [](const char* type) {
823
            U_ASSERT(uprv_strlen(type) == 2 || uprv_strlen(type) == 3);
824
        },
825
#else
826
0
        [](const char*) {},
827
0
#endif
828
0
        [](const UnicodeString&) { },
829
0
        status);
830
0
}
831
832
/**
833
 * Read the variantAlias data from alias to strings+types+replacementIndexes.
834
 * Allocate length items for types, to store the type field. Allocate length
835
 * items for replacementIndexes, to store the index in the strings for the
836
 * replacement variant.
837
 */
838
void
839
AliasDataBuilder::readVariantAlias(
840
        UResourceBundle* alias,
841
        UniqueCharStrings* strings,
842
        LocalMemory<const char*>& types,
843
        LocalMemory<int32_t>& replacementIndexes,
844
        int32_t &length,
845
        UErrorCode &status)
846
0
{
847
0
    return readAlias(
848
0
        alias, strings, types, replacementIndexes, length,
849
#if U_DEBUG
850
        [](const char* type) {
851
            U_ASSERT(uprv_strlen(type) >= 4 && uprv_strlen(type) <= 8);
852
            U_ASSERT(uprv_strlen(type) != 4 ||
853
                     (type[0] >= '0' && type[0] <= '9'));
854
        },
855
        [](const UnicodeString& replacement) {
856
            U_ASSERT(replacement.length() >= 4 && replacement.length() <= 8);
857
            U_ASSERT(replacement.length() != 4 ||
858
                     (replacement.charAt(0) >= u'0' &&
859
                      replacement.charAt(0) <= u'9'));
860
        },
861
#else
862
0
        [](const char*) {},
863
0
        [](const UnicodeString&) { },
864
0
#endif
865
0
        status);
866
0
}
867
868
/**
869
 * Read the subdivisionAlias data from alias to strings+types+replacementIndexes.
870
 * Allocate length items for types, to store the type field. Allocate length
871
 * items for replacementIndexes, to store the index in the strings for the
872
 * replacement regions.
873
 */
874
void
875
AliasDataBuilder::readSubdivisionAlias(
876
        UResourceBundle* alias,
877
        UniqueCharStrings* strings,
878
        LocalMemory<const char*>& types,
879
        LocalMemory<int32_t>& replacementIndexes,
880
        int32_t &length,
881
        UErrorCode &status)
882
0
{
883
0
    return readAlias(
884
0
        alias, strings, types, replacementIndexes, length,
885
#if U_DEBUG
886
        [](const char* type) {
887
            U_ASSERT(uprv_strlen(type) >= 3 && uprv_strlen(type) <= 8);
888
        },
889
#else
890
0
        [](const char*) {},
891
0
#endif
892
0
        [](const UnicodeString&) { },
893
0
        status);
894
0
}
895
896
/**
897
 * Initializes the alias data from the ICU resource bundles. The alias data
898
 * contains alias of language, country, script and variants.
899
 *
900
 * If the alias data has already loaded, then this method simply returns without
901
 * doing anything meaningful.
902
 */
903
void U_CALLCONV
904
AliasData::loadData(UErrorCode &status)
905
0
{
906
#ifdef LOCALE_CANONICALIZATION_DEBUG
907
    UDate start = uprv_getRawUTCtime();
908
#endif  // LOCALE_CANONICALIZATION_DEBUG
909
0
    ucln_common_registerCleanup(UCLN_COMMON_LOCALE_ALIAS, cleanup);
910
0
    AliasDataBuilder builder;
911
0
    gSingleton = builder.build(status);
912
#ifdef LOCALE_CANONICALIZATION_DEBUG
913
    UDate end = uprv_getRawUTCtime();
914
    printf("AliasData::loadData took total %f ms\n", end - start);
915
#endif  // LOCALE_CANONICALIZATION_DEBUG
916
0
}
917
918
/**
919
 * Build the alias data from resources.
920
 */
921
AliasData*
922
0
AliasDataBuilder::build(UErrorCode &status) {
923
0
    LocalUResourceBundlePointer metadata(
924
0
        ures_openDirect(nullptr, "metadata", &status));
925
0
    LocalUResourceBundlePointer metadataAlias(
926
0
        ures_getByKey(metadata.getAlias(), "alias", nullptr, &status));
927
0
    LocalUResourceBundlePointer languageAlias(
928
0
        ures_getByKey(metadataAlias.getAlias(), "language", nullptr, &status));
929
0
    LocalUResourceBundlePointer scriptAlias(
930
0
        ures_getByKey(metadataAlias.getAlias(), "script", nullptr, &status));
931
0
    LocalUResourceBundlePointer territoryAlias(
932
0
        ures_getByKey(metadataAlias.getAlias(), "territory", nullptr, &status));
933
0
    LocalUResourceBundlePointer variantAlias(
934
0
        ures_getByKey(metadataAlias.getAlias(), "variant", nullptr, &status));
935
0
    LocalUResourceBundlePointer subdivisionAlias(
936
0
        ures_getByKey(metadataAlias.getAlias(), "subdivision", nullptr, &status));
937
938
0
    if (U_FAILURE(status)) {
939
0
        return nullptr;
940
0
    }
941
0
    int32_t languagesLength = 0, scriptLength = 0, territoryLength = 0,
942
0
            variantLength = 0, subdivisionLength = 0;
943
944
    // Read the languageAlias into languageTypes, languageReplacementIndexes
945
    // and strings
946
0
    UniqueCharStrings strings(status);
947
0
    LocalMemory<const char*> languageTypes;
948
0
    LocalMemory<int32_t> languageReplacementIndexes;
949
0
    readLanguageAlias(languageAlias.getAlias(),
950
0
                      &strings,
951
0
                      languageTypes,
952
0
                      languageReplacementIndexes,
953
0
                      languagesLength,
954
0
                      status);
955
956
    // Read the scriptAlias into scriptTypes, scriptReplacementIndexes
957
    // and strings
958
0
    LocalMemory<const char*> scriptTypes;
959
0
    LocalMemory<int32_t> scriptReplacementIndexes;
960
0
    readScriptAlias(scriptAlias.getAlias(),
961
0
                    &strings,
962
0
                    scriptTypes,
963
0
                    scriptReplacementIndexes,
964
0
                    scriptLength,
965
0
                    status);
966
967
    // Read the territoryAlias into territoryTypes, territoryReplacementIndexes
968
    // and strings
969
0
    LocalMemory<const char*> territoryTypes;
970
0
    LocalMemory<int32_t> territoryReplacementIndexes;
971
0
    readTerritoryAlias(territoryAlias.getAlias(),
972
0
                       &strings,
973
0
                       territoryTypes,
974
0
                       territoryReplacementIndexes,
975
0
                       territoryLength, status);
976
977
    // Read the variantAlias into variantTypes, variantReplacementIndexes
978
    // and strings
979
0
    LocalMemory<const char*> variantTypes;
980
0
    LocalMemory<int32_t> variantReplacementIndexes;
981
0
    readVariantAlias(variantAlias.getAlias(),
982
0
                     &strings,
983
0
                     variantTypes,
984
0
                     variantReplacementIndexes,
985
0
                     variantLength, status);
986
987
    // Read the subdivisionAlias into subdivisionTypes, subdivisionReplacementIndexes
988
    // and strings
989
0
    LocalMemory<const char*> subdivisionTypes;
990
0
    LocalMemory<int32_t> subdivisionReplacementIndexes;
991
0
    readSubdivisionAlias(subdivisionAlias.getAlias(),
992
0
                         &strings,
993
0
                         subdivisionTypes,
994
0
                         subdivisionReplacementIndexes,
995
0
                         subdivisionLength, status);
996
997
0
    if (U_FAILURE(status)) {
998
0
        return nullptr;
999
0
    }
1000
1001
    // We can only use strings after freeze it.
1002
0
    strings.freeze();
1003
1004
    // Build the languageMap from languageTypes & languageReplacementIndexes
1005
0
    CharStringMap languageMap(490, status);
1006
0
    for (int32_t i = 0; U_SUCCESS(status) && i < languagesLength; i++) {
1007
0
        languageMap.put(languageTypes[i],
1008
0
                        strings.get(languageReplacementIndexes[i]),
1009
0
                        status);
1010
0
    }
1011
1012
    // Build the scriptMap from scriptTypes & scriptReplacementIndexes
1013
0
    CharStringMap scriptMap(1, status);
1014
0
    for (int32_t i = 0; U_SUCCESS(status) && i < scriptLength; i++) {
1015
0
        scriptMap.put(scriptTypes[i],
1016
0
                      strings.get(scriptReplacementIndexes[i]),
1017
0
                      status);
1018
0
    }
1019
1020
    // Build the territoryMap from territoryTypes & territoryReplacementIndexes
1021
0
    CharStringMap territoryMap(650, status);
1022
0
    for (int32_t i = 0; U_SUCCESS(status) && i < territoryLength; i++) {
1023
0
        territoryMap.put(territoryTypes[i],
1024
0
                         strings.get(territoryReplacementIndexes[i]),
1025
0
                         status);
1026
0
    }
1027
1028
    // Build the variantMap from variantTypes & variantReplacementIndexes.
1029
0
    CharStringMap variantMap(2, status);
1030
0
    for (int32_t i = 0; U_SUCCESS(status) && i < variantLength; i++) {
1031
0
        variantMap.put(variantTypes[i],
1032
0
                       strings.get(variantReplacementIndexes[i]),
1033
0
                       status);
1034
0
    }
1035
1036
    // Build the subdivisionMap from subdivisionTypes & subdivisionReplacementIndexes.
1037
0
    CharStringMap subdivisionMap(2, status);
1038
0
    for (int32_t i = 0; U_SUCCESS(status) && i < subdivisionLength; i++) {
1039
0
        subdivisionMap.put(subdivisionTypes[i],
1040
0
                       strings.get(subdivisionReplacementIndexes[i]),
1041
0
                       status);
1042
0
    }
1043
1044
0
    if (U_FAILURE(status)) {
1045
0
        return nullptr;
1046
0
    }
1047
1048
    // copy hashtables
1049
0
    auto *data = new AliasData(
1050
0
        std::move(languageMap),
1051
0
        std::move(scriptMap),
1052
0
        std::move(territoryMap),
1053
0
        std::move(variantMap),
1054
0
        std::move(subdivisionMap),
1055
0
        strings.orphanCharStrings());
1056
1057
0
    if (data == nullptr) {
1058
0
        status = U_MEMORY_ALLOCATION_ERROR;
1059
0
    }
1060
0
    return data;
1061
0
}
1062
1063
/**
1064
 * A class that find the replacement values of locale fields by using AliasData.
1065
 */
1066
class AliasReplacer {
1067
public:
1068
    AliasReplacer(UErrorCode status) :
1069
0
            language(nullptr), script(nullptr), region(nullptr),
1070
0
            extensions(nullptr), variants(status),
1071
0
            data(nullptr) {
1072
0
    }
1073
0
    ~AliasReplacer() {
1074
0
    }
1075
1076
    // Check the fields inside locale, if need to replace fields,
1077
    // place the the replaced locale ID in out and return true.
1078
    // Otherwise return false for no replacement or error.
1079
    bool replace(
1080
        const Locale& locale, CharString& out, UErrorCode& status);
1081
1082
private:
1083
    const char* language;
1084
    const char* script;
1085
    const char* region;
1086
    const char* extensions;
1087
    UVector variants;
1088
1089
    const AliasData* data;
1090
1091
0
    inline bool notEmpty(const char* str) {
1092
0
        return str && str[0] != NULL_CHAR;
1093
0
    }
1094
1095
    /**
1096
     * If replacement is neither null nor empty and input is either null or empty,
1097
     * return replacement.
1098
     * If replacement is neither null nor empty but input is not empty, return input.
1099
     * If replacement is either null or empty and type is either null or empty,
1100
     * return input.
1101
     * Otherwise return null.
1102
     *   replacement     input      type        return
1103
     *    AAA             nullptr    *           AAA
1104
     *    AAA             BBB        *           BBB
1105
     *    nullptr || ""   CCC        nullptr     CCC
1106
     *    nullptr || ""   *          DDD         nullptr
1107
     */
1108
    inline const char* deleteOrReplace(
1109
0
            const char* input, const char* type, const char* replacement) {
1110
0
        return notEmpty(replacement) ?
1111
0
            ((input == nullptr) ?  replacement : input) :
1112
0
            ((type == nullptr) ? input  : nullptr);
1113
0
    }
1114
1115
0
    inline bool same(const char* a, const char* b) {
1116
0
        if (a == nullptr && b == nullptr) {
1117
0
            return true;
1118
0
        }
1119
0
        if ((a == nullptr && b != nullptr) ||
1120
0
            (a != nullptr && b == nullptr)) {
1121
0
          return false;
1122
0
        }
1123
0
        return uprv_strcmp(a, b) == 0;
1124
0
    }
1125
1126
    // Gather fields and generate locale ID into out.
1127
    CharString& outputToString(CharString& out, UErrorCode status);
1128
1129
    // Generate the lookup key.
1130
    CharString& generateKey(const char* language, const char* region,
1131
                            const char* variant, CharString& out,
1132
                            UErrorCode status);
1133
1134
    void parseLanguageReplacement(const char* replacement,
1135
                                  const char*& replaceLanguage,
1136
                                  const char*& replaceScript,
1137
                                  const char*& replaceRegion,
1138
                                  const char*& replaceVariant,
1139
                                  const char*& replaceExtensions,
1140
                                  UVector& toBeFreed,
1141
                                  UErrorCode& status);
1142
1143
    // Replace by using languageAlias.
1144
    bool replaceLanguage(bool checkLanguage, bool checkRegion,
1145
                         bool checkVariants, UVector& toBeFreed,
1146
                         UErrorCode& status);
1147
1148
    // Replace by using territoryAlias.
1149
    bool replaceTerritory(UVector& toBeFreed, UErrorCode& status);
1150
1151
    // Replace by using scriptAlias.
1152
    bool replaceScript(UErrorCode& status);
1153
1154
    // Replace by using variantAlias.
1155
    bool replaceVariant(UErrorCode& status);
1156
1157
    // Replace by using subdivisionAlias.
1158
    bool replaceSubdivision(StringPiece subdivision,
1159
                            CharString& output, UErrorCode& status);
1160
1161
    // Replace transformed extensions.
1162
    bool replaceTransformedExtensions(
1163
        CharString& transformedExtensions, CharString& output, UErrorCode& status);
1164
};
1165
1166
CharString&
1167
AliasReplacer::generateKey(
1168
        const char* language, const char* region, const char* variant,
1169
        CharString& out, UErrorCode status)
1170
0
{
1171
0
    out.append(language, status);
1172
0
    if (notEmpty(region)) {
1173
0
        out.append(SEP_CHAR, status)
1174
0
            .append(region, status);
1175
0
    }
1176
0
    if (notEmpty(variant)) {
1177
0
       out.append(SEP_CHAR, status)
1178
0
           .append(variant, status);
1179
0
    }
1180
0
    return out;
1181
0
}
1182
1183
void
1184
AliasReplacer::parseLanguageReplacement(
1185
    const char* replacement,
1186
    const char*& replacedLanguage,
1187
    const char*& replacedScript,
1188
    const char*& replacedRegion,
1189
    const char*& replacedVariant,
1190
    const char*& replacedExtensions,
1191
    UVector& toBeFreed,
1192
    UErrorCode& status)
1193
0
{
1194
0
    if (U_FAILURE(status)) {
1195
0
        return;
1196
0
    }
1197
0
    replacedScript = replacedRegion = replacedVariant
1198
0
        = replacedExtensions = nullptr;
1199
0
    if (uprv_strchr(replacement, '_') == nullptr) {
1200
0
        replacedLanguage = replacement;
1201
        // reach the end, just return it.
1202
0
        return;
1203
0
    }
1204
    // We have multiple field so we have to allocate and parse
1205
0
    CharString* str = new CharString(
1206
0
        replacement, (int32_t)uprv_strlen(replacement), status);
1207
0
    if (U_FAILURE(status)) {
1208
0
        return;
1209
0
    }
1210
0
    if (str == nullptr) {
1211
0
        status = U_MEMORY_ALLOCATION_ERROR;
1212
0
        return;
1213
0
    }
1214
0
    toBeFreed.addElementX(str, status);
1215
0
    char* data = str->data();
1216
0
    replacedLanguage = (const char*) data;
1217
0
    char* endOfField = uprv_strchr(data, '_');
1218
0
    *endOfField = '\0'; // null terminiate it.
1219
0
    endOfField++;
1220
0
    const char* start = endOfField;
1221
0
    endOfField = (char*) uprv_strchr(start, '_');
1222
0
    size_t len = 0;
1223
0
    if (endOfField == nullptr) {
1224
0
        len = uprv_strlen(start);
1225
0
    } else {
1226
0
        len = endOfField - start;
1227
0
        *endOfField = '\0'; // null terminiate it.
1228
0
    }
1229
0
    if (len == 4 && uprv_isASCIILetter(*start)) {
1230
        // Got a script
1231
0
        replacedScript = start;
1232
0
        if (endOfField == nullptr) {
1233
0
            return;
1234
0
        }
1235
0
        start = endOfField++;
1236
0
        endOfField = (char*)uprv_strchr(start, '_');
1237
0
        if (endOfField == nullptr) {
1238
0
            len = uprv_strlen(start);
1239
0
        } else {
1240
0
            len = endOfField - start;
1241
0
            *endOfField = '\0'; // null terminiate it.
1242
0
        }
1243
0
    }
1244
0
    if (len >= 2 && len <= 3) {
1245
        // Got a region
1246
0
        replacedRegion = start;
1247
0
        if (endOfField == nullptr) {
1248
0
            return;
1249
0
        }
1250
0
        start = endOfField++;
1251
0
        endOfField = (char*)uprv_strchr(start, '_');
1252
0
        if (endOfField == nullptr) {
1253
0
            len = uprv_strlen(start);
1254
0
        } else {
1255
0
            len = endOfField - start;
1256
0
            *endOfField = '\0'; // null terminiate it.
1257
0
        }
1258
0
    }
1259
0
    if (len >= 4) {
1260
        // Got a variant
1261
0
        replacedVariant = start;
1262
0
        if (endOfField == nullptr) {
1263
0
            return;
1264
0
        }
1265
0
        start = endOfField++;
1266
0
    }
1267
0
    replacedExtensions = start;
1268
0
}
1269
1270
bool
1271
AliasReplacer::replaceLanguage(
1272
        bool checkLanguage, bool checkRegion,
1273
        bool checkVariants, UVector& toBeFreed, UErrorCode& status)
1274
0
{
1275
0
    if (U_FAILURE(status)) {
1276
0
        return false;
1277
0
    }
1278
0
    if (    (checkRegion && region == nullptr) ||
1279
0
            (checkVariants && variants.size() == 0)) {
1280
        // Nothing to search.
1281
0
        return false;
1282
0
    }
1283
0
    int32_t variant_size = checkVariants ? variants.size() : 1;
1284
    // Since we may have more than one variant, we need to loop through them.
1285
0
    const char* searchLanguage = checkLanguage ? language : "und";
1286
0
    const char* searchRegion = checkRegion ? region : nullptr;
1287
0
    const char* searchVariant = nullptr;
1288
0
    for (int32_t variant_index = 0;
1289
0
            variant_index < variant_size;
1290
0
            variant_index++) {
1291
0
        if (checkVariants) {
1292
0
            U_ASSERT(variant_index < variant_size);
1293
0
            searchVariant = (const char*)(variants.elementAt(variant_index));
1294
0
        }
1295
1296
0
        if (searchVariant != nullptr && uprv_strlen(searchVariant) < 4) {
1297
            // Do not consider  ill-formed variant subtag.
1298
0
            searchVariant = nullptr;
1299
0
        }
1300
0
        CharString typeKey;
1301
0
        generateKey(searchLanguage, searchRegion, searchVariant, typeKey,
1302
0
                    status);
1303
0
        if (U_FAILURE(status)) {
1304
0
            return false;
1305
0
        }
1306
0
        const char *replacement = data->languageMap().get(typeKey.data());
1307
0
        if (replacement == nullptr) {
1308
            // Found no replacement data.
1309
0
            continue;
1310
0
        }
1311
1312
0
        const char* replacedLanguage = nullptr;
1313
0
        const char* replacedScript = nullptr;
1314
0
        const char* replacedRegion = nullptr;
1315
0
        const char* replacedVariant = nullptr;
1316
0
        const char* replacedExtensions = nullptr;
1317
0
        parseLanguageReplacement(replacement,
1318
0
                                 replacedLanguage,
1319
0
                                 replacedScript,
1320
0
                                 replacedRegion,
1321
0
                                 replacedVariant,
1322
0
                                 replacedExtensions,
1323
0
                                 toBeFreed,
1324
0
                                 status);
1325
0
        replacedLanguage =
1326
0
            (replacedLanguage != nullptr && uprv_strcmp(replacedLanguage, "und") == 0) ?
1327
0
            language : replacedLanguage;
1328
0
        replacedScript = deleteOrReplace(script, nullptr, replacedScript);
1329
0
        replacedRegion = deleteOrReplace(region, searchRegion, replacedRegion);
1330
0
        replacedVariant = deleteOrReplace(
1331
0
            searchVariant, searchVariant, replacedVariant);
1332
1333
0
        if (    same(language, replacedLanguage) &&
1334
0
                same(script, replacedScript) &&
1335
0
                same(region, replacedRegion) &&
1336
0
                same(searchVariant, replacedVariant) &&
1337
0
                replacedExtensions == nullptr) {
1338
            // Replacement produce no changes.
1339
0
            continue;
1340
0
        }
1341
1342
0
        language = replacedLanguage;
1343
0
        region = replacedRegion;
1344
0
        script = replacedScript;
1345
0
        if (searchVariant != nullptr) {
1346
0
            if (notEmpty(replacedVariant)) {
1347
0
                variants.setElementAt((void*)replacedVariant, variant_index);
1348
0
            } else {
1349
0
                variants.removeElementAt(variant_index);
1350
0
            }
1351
0
        }
1352
0
        if (replacedExtensions != nullptr) {
1353
            // DO NOTHING
1354
            // UTS35 does not specify what should we do if we have extensions in the
1355
            // replacement. Currently we know only the following 4 "BCP47 LegacyRules" have
1356
            // extensions in them languageAlias:
1357
            //  i_default => en_x_i_default
1358
            //  i_enochian => und_x_i_enochian
1359
            //  i_mingo => see_x_i_mingo
1360
            //  zh_min => nan_x_zh_min
1361
            // But all of them are already changed by code inside ultag_parse() before
1362
            // hitting this code.
1363
0
        }
1364
1365
        // Something changed by language alias data.
1366
0
        return true;
1367
0
    }
1368
    // Nothing changed by language alias data.
1369
0
    return false;
1370
0
}
1371
1372
bool
1373
AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status)
1374
0
{
1375
0
    if (U_FAILURE(status)) {
1376
0
        return false;
1377
0
    }
1378
0
    if (region == nullptr) {
1379
        // No region to search.
1380
0
        return false;
1381
0
    }
1382
0
    const char *replacement = data->territoryMap().get(region);
1383
0
    if (replacement == nullptr) {
1384
        // Found no replacement data for this region.
1385
0
        return false;
1386
0
    }
1387
0
    const char* replacedRegion = replacement;
1388
0
    const char* firstSpace = uprv_strchr(replacement, ' ');
1389
0
    if (firstSpace != nullptr) {
1390
        // If there are are more than one region in the replacement.
1391
        // We need to check which one match based on the language.
1392
        // Cannot use nullptr for language because that will construct
1393
        // the default locale, in that case, use "und" to get the correct
1394
        // locale.
1395
0
        Locale l = LocaleBuilder()
1396
0
            .setLanguage(language == nullptr ? "und" : language)
1397
0
            .setScript(script)
1398
0
            .build(status);
1399
0
        l.addLikelySubtags(status);
1400
0
        const char* likelyRegion = l.getCountry();
1401
0
        LocalPointer<CharString> item;
1402
0
        if (likelyRegion != nullptr && uprv_strlen(likelyRegion) > 0) {
1403
0
            size_t len = uprv_strlen(likelyRegion);
1404
0
            const char* foundInReplacement = uprv_strstr(replacement,
1405
0
                                                         likelyRegion);
1406
0
            if (foundInReplacement != nullptr) {
1407
                // Assuming the case there are no three letter region code in
1408
                // the replacement of territoryAlias
1409
0
                U_ASSERT(foundInReplacement == replacement ||
1410
0
                         *(foundInReplacement-1) == ' ');
1411
0
                U_ASSERT(foundInReplacement[len] == ' ' ||
1412
0
                         foundInReplacement[len] == '\0');
1413
0
                item.adoptInsteadAndCheckErrorCode(
1414
0
                    new CharString(foundInReplacement, (int32_t)len, status), status);
1415
0
            }
1416
0
        }
1417
0
        if (item.isNull() && U_SUCCESS(status)) {
1418
0
            item.adoptInsteadAndCheckErrorCode(
1419
0
                new CharString(replacement,
1420
0
                               (int32_t)(firstSpace - replacement), status), status);
1421
0
        }
1422
0
        if (U_FAILURE(status)) { return false; }
1423
0
        if (item.isNull()) {
1424
0
            status = U_MEMORY_ALLOCATION_ERROR;
1425
0
            return false;
1426
0
        }
1427
0
        replacedRegion = item->data();
1428
0
        toBeFreed.addElementX(item.orphan(), status);
1429
0
    }
1430
0
    U_ASSERT(!same(region, replacedRegion));
1431
0
    region = replacedRegion;
1432
    // The region is changed by data in territory alias.
1433
0
    return true;
1434
0
}
1435
1436
bool
1437
AliasReplacer::replaceScript(UErrorCode& status)
1438
0
{
1439
0
    if (U_FAILURE(status)) {
1440
0
        return false;
1441
0
    }
1442
0
    if (script == nullptr) {
1443
        // No script to search.
1444
0
        return false;
1445
0
    }
1446
0
    const char *replacement = data->scriptMap().get(script);
1447
0
    if (replacement == nullptr) {
1448
        // Found no replacement data for this script.
1449
0
        return false;
1450
0
    }
1451
0
    U_ASSERT(!same(script, replacement));
1452
0
    script = replacement;
1453
    // The script is changed by data in script alias.
1454
0
    return true;
1455
0
}
1456
1457
bool
1458
AliasReplacer::replaceVariant(UErrorCode& status)
1459
0
{
1460
0
    if (U_FAILURE(status)) {
1461
0
        return false;
1462
0
    }
1463
    // Since we may have more than one variant, we need to loop through them.
1464
0
    for (int32_t i = 0; i < variants.size(); i++) {
1465
0
        const char *variant = (const char*)(variants.elementAt(i));
1466
0
        const char *replacement = data->variantMap().get(variant);
1467
0
        if (replacement == nullptr) {
1468
            // Found no replacement data for this variant.
1469
0
            continue;
1470
0
        }
1471
0
        U_ASSERT((uprv_strlen(replacement) >= 5  &&
1472
0
                  uprv_strlen(replacement) <= 8) ||
1473
0
                 (uprv_strlen(replacement) == 4 &&
1474
0
                  replacement[0] >= '0' &&
1475
0
                  replacement[0] <= '9'));
1476
0
        if (!same(variant, replacement)) {
1477
0
            variants.setElementAt((void*)replacement, i);
1478
            // Special hack to handle hepburn-heploc => alalc97
1479
0
            if (uprv_strcmp(variant, "heploc") == 0) {
1480
0
                for (int32_t j = 0; j < variants.size(); j++) {
1481
0
                     if (uprv_strcmp((const char*)(variants.elementAt(j)),
1482
0
                                     "hepburn") == 0) {
1483
0
                         variants.removeElementAt(j);
1484
0
                     }
1485
0
                }
1486
0
            }
1487
0
            return true;
1488
0
        }
1489
0
    }
1490
0
    return false;
1491
0
}
1492
1493
bool
1494
AliasReplacer::replaceSubdivision(
1495
    StringPiece subdivision, CharString& output, UErrorCode& status)
1496
0
{
1497
0
    if (U_FAILURE(status)) {
1498
0
        return false;
1499
0
    }
1500
0
    const char *replacement = data->subdivisionMap().get(subdivision.data());
1501
0
    if (replacement != nullptr) {
1502
0
        const char* firstSpace = uprv_strchr(replacement, ' ');
1503
        // Found replacement data for this subdivision.
1504
0
        size_t len = (firstSpace != nullptr) ?
1505
0
            (firstSpace - replacement) : uprv_strlen(replacement);
1506
0
        if (2 <= len && len <= 8) {
1507
0
            output.append(replacement, (int32_t)len, status);
1508
0
            if (2 == len) {
1509
                // Add 'zzzz' based on changes to UTS #35 for CLDR-14312.
1510
0
                output.append("zzzz", 4, status);
1511
0
            }
1512
0
        }
1513
0
        return true;
1514
0
    }
1515
0
    return false;
1516
0
}
1517
1518
bool
1519
AliasReplacer::replaceTransformedExtensions(
1520
    CharString& transformedExtensions, CharString& output, UErrorCode& status)
1521
0
{
1522
    // The content of the transformedExtensions will be modified in this
1523
    // function to NULL-terminating (tkey-tvalue) pairs.
1524
0
    if (U_FAILURE(status)) {
1525
0
        return false;
1526
0
    }
1527
0
    int32_t len = transformedExtensions.length();
1528
0
    const char* str = transformedExtensions.data();
1529
0
    const char* tkey = ultag_getTKeyStart(str);
1530
0
    int32_t tlangLen = (tkey == str) ? 0 :
1531
0
        ((tkey == nullptr) ? len : static_cast<int32_t>((tkey - str - 1)));
1532
0
    CharStringByteSink sink(&output);
1533
0
    if (tlangLen > 0) {
1534
0
        Locale tlang = LocaleBuilder()
1535
0
            .setLanguageTag(StringPiece(str, tlangLen))
1536
0
            .build(status);
1537
0
        tlang.canonicalize(status);
1538
0
        tlang.toLanguageTag(sink, status);
1539
0
        if (U_FAILURE(status)) {
1540
0
            return false;
1541
0
        }
1542
0
        T_CString_toLowerCase(output.data());
1543
0
    }
1544
0
    if (tkey != nullptr) {
1545
        // We need to sort the tfields by tkey
1546
0
        UVector tfields(status);
1547
0
        if (U_FAILURE(status)) {
1548
0
            return false;
1549
0
        }
1550
0
        do {
1551
0
            const char* tvalue = uprv_strchr(tkey, '-');
1552
0
            if (tvalue == nullptr) {
1553
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
1554
0
                return false;
1555
0
            }
1556
0
            const char* nextTKey = ultag_getTKeyStart(tvalue);
1557
0
            if (nextTKey != nullptr) {
1558
0
                *((char*)(nextTKey-1)) = '\0';  // NULL terminate tvalue
1559
0
            }
1560
0
            tfields.insertElementAt((void*)tkey, tfields.size(), status);
1561
0
            if (U_FAILURE(status)) {
1562
0
                return false;
1563
0
            }
1564
0
            tkey = nextTKey;
1565
0
        } while (tkey != nullptr);
1566
0
        tfields.sort([](UElement e1, UElement e2) -> int32_t {
1567
0
            return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1568
0
        }, status);
1569
0
        for (int32_t i = 0; i < tfields.size(); i++) {
1570
0
             if (output.length() > 0) {
1571
0
                 output.append('-', status);
1572
0
             }
1573
0
             const char* tfield = (const char*) tfields.elementAt(i);
1574
0
             const char* tvalue = uprv_strchr(tfield, '-');
1575
0
             if (tvalue == nullptr) {
1576
0
                 status = U_ILLEGAL_ARGUMENT_ERROR;
1577
0
                 return false;
1578
0
             }
1579
             // Split the "tkey-tvalue" pair string so that we can canonicalize the tvalue.
1580
0
             *((char*)tvalue++) = '\0'; // NULL terminate tkey
1581
0
             output.append(tfield, status).append('-', status);
1582
0
             const char* bcpTValue = ulocimp_toBcpType(tfield, tvalue, nullptr, nullptr);
1583
0
             output.append((bcpTValue == nullptr) ? tvalue : bcpTValue, status);
1584
0
        }
1585
0
    }
1586
0
    if (U_FAILURE(status)) {
1587
0
        return false;
1588
0
    }
1589
0
    return true;
1590
0
}
1591
1592
CharString&
1593
AliasReplacer::outputToString(
1594
    CharString& out, UErrorCode status)
1595
0
{
1596
0
    out.append(language, status);
1597
0
    if (notEmpty(script)) {
1598
0
        out.append(SEP_CHAR, status)
1599
0
            .append(script, status);
1600
0
    }
1601
0
    if (notEmpty(region)) {
1602
0
        out.append(SEP_CHAR, status)
1603
0
            .append(region, status);
1604
0
    }
1605
0
    if (variants.size() > 0) {
1606
0
        if (!notEmpty(script) && !notEmpty(region)) {
1607
0
          out.append(SEP_CHAR, status);
1608
0
        }
1609
0
        variants.sort([](UElement e1, UElement e2) -> int32_t {
1610
0
            return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1611
0
        }, status);
1612
0
        int32_t variantsStart = out.length();
1613
0
        for (int32_t i = 0; i < variants.size(); i++) {
1614
0
             out.append(SEP_CHAR, status)
1615
0
                 .append((const char*)(variants.elementAt(i)),
1616
0
                         status);
1617
0
        }
1618
0
        T_CString_toUpperCase(out.data() + variantsStart);
1619
0
    }
1620
0
    if (notEmpty(extensions)) {
1621
0
        CharString tmp("und_", status);
1622
0
        tmp.append(extensions, status);
1623
0
        Locale tmpLocale(tmp.data());
1624
        // only support x extension inside CLDR for now.
1625
0
        U_ASSERT(extensions[0] == 'x');
1626
0
        out.append(tmpLocale.getName() + 1, status);
1627
0
    }
1628
0
    return out;
1629
0
}
1630
1631
bool
1632
AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status)
1633
0
{
1634
0
    data = AliasData::singleton(status);
1635
0
    if (U_FAILURE(status)) {
1636
0
        return false;
1637
0
    }
1638
0
    U_ASSERT(data != nullptr);
1639
0
    out.clear();
1640
0
    language = locale.getLanguage();
1641
0
    if (!notEmpty(language)) {
1642
0
        language = nullptr;
1643
0
    }
1644
0
    script = locale.getScript();
1645
0
    if (!notEmpty(script)) {
1646
0
        script = nullptr;
1647
0
    }
1648
0
    region = locale.getCountry();
1649
0
    if (!notEmpty(region)) {
1650
0
        region = nullptr;
1651
0
    }
1652
0
    const char* variantsStr = locale.getVariant();
1653
0
    CharString variantsBuff(variantsStr, -1, status);
1654
0
    if (!variantsBuff.isEmpty()) {
1655
0
        if (U_FAILURE(status)) { return false; }
1656
0
        char* start = variantsBuff.data();
1657
0
        T_CString_toLowerCase(start);
1658
0
        char* end;
1659
0
        while ((end = uprv_strchr(start, SEP_CHAR)) != nullptr &&
1660
0
               U_SUCCESS(status)) {
1661
0
            *end = NULL_CHAR;  // null terminate inside variantsBuff
1662
0
            variants.addElementX(start, status);
1663
0
            start = end + 1;
1664
0
        }
1665
0
        variants.addElementX(start, status);
1666
0
    }
1667
0
    if (U_FAILURE(status)) { return false; }
1668
1669
    // Sort the variants
1670
0
    variants.sort([](UElement e1, UElement e2) -> int32_t {
1671
0
        return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1672
0
    }, status);
1673
1674
    // A changed count to assert when loop too many times.
1675
0
    int changed = 0;
1676
    // A UVector to to hold CharString allocated by the replace* method
1677
    // and freed when out of scope from his function.
1678
0
    UVector stringsToBeFreed([](void *obj){ delete ((CharString*) obj); },
1679
0
                             nullptr, 10, status);
1680
0
    while (U_SUCCESS(status)) {
1681
        // Something wrong with the data cause looping here more than 10 times
1682
        // already.
1683
0
        U_ASSERT(changed < 5);
1684
        // From observation of key in data/misc/metadata.txt
1685
        // we know currently we only need to search in the following combination
1686
        // of fields for type in languageAlias:
1687
        // * lang_region_variant
1688
        // * lang_region
1689
        // * lang_variant
1690
        // * lang
1691
        // * und_variant
1692
        // This assumption is ensured by the U_ASSERT in readLanguageAlias
1693
        //
1694
        //                      lang  REGION variant
1695
0
        if (    replaceLanguage(true, true,  true,  stringsToBeFreed, status) ||
1696
0
                replaceLanguage(true, true,  false, stringsToBeFreed, status) ||
1697
0
                replaceLanguage(true, false, true,  stringsToBeFreed, status) ||
1698
0
                replaceLanguage(true, false, false, stringsToBeFreed, status) ||
1699
0
                replaceLanguage(false,false, true,  stringsToBeFreed, status) ||
1700
0
                replaceTerritory(stringsToBeFreed, status) ||
1701
0
                replaceScript(status) ||
1702
0
                replaceVariant(status)) {
1703
            // Some values in data is changed, try to match from the beginning
1704
            // again.
1705
0
            changed++;
1706
0
            continue;
1707
0
        }
1708
        // Nothing changed. Break out.
1709
0
        break;
1710
0
    }  // while(1)
1711
1712
0
    if (U_FAILURE(status)) { return false; }
1713
    // Nothing changed and we know the order of the variants are not change
1714
    // because we have no variant or only one.
1715
0
    const char* extensionsStr = locale_getKeywordsStart(locale.getName());
1716
0
    if (changed == 0 && variants.size() <= 1 && extensionsStr == nullptr) {
1717
0
        return false;
1718
0
    }
1719
0
    outputToString(out, status);
1720
0
    if (U_FAILURE(status)) {
1721
0
        return false;
1722
0
    }
1723
0
    if (extensionsStr != nullptr) {
1724
0
        changed = 0;
1725
0
        Locale temp(locale);
1726
0
        LocalPointer<icu::StringEnumeration> iter(locale.createKeywords(status));
1727
0
        if (U_SUCCESS(status) && !iter.isNull()) {
1728
0
            const char* key;
1729
0
            while ((key = iter->next(nullptr, status)) != nullptr) {
1730
0
                if (uprv_strcmp("sd", key) == 0 || uprv_strcmp("rg", key) == 0 ||
1731
0
                        uprv_strcmp("t", key) == 0) {
1732
0
                    CharString value;
1733
0
                    CharStringByteSink valueSink(&value);
1734
0
                    locale.getKeywordValue(key, valueSink, status);
1735
0
                    if (U_FAILURE(status)) {
1736
0
                        status = U_ZERO_ERROR;
1737
0
                        continue;
1738
0
                    }
1739
0
                    CharString replacement;
1740
0
                    if (uprv_strlen(key) == 2) {
1741
0
                        if (replaceSubdivision(value.toStringPiece(), replacement, status)) {
1742
0
                            changed++;
1743
0
                            temp.setKeywordValue(key, replacement.data(), status);
1744
0
                        }
1745
0
                    } else {
1746
0
                        U_ASSERT(uprv_strcmp(key, "t") == 0);
1747
0
                        if (replaceTransformedExtensions(value, replacement, status)) {
1748
0
                            changed++;
1749
0
                            temp.setKeywordValue(key, replacement.data(), status);
1750
0
                        }
1751
0
                    }
1752
0
                    if (U_FAILURE(status)) {
1753
0
                        return false;
1754
0
                    }
1755
0
                }
1756
0
            }
1757
0
        }
1758
0
        if (changed != 0) {
1759
0
            extensionsStr = locale_getKeywordsStart(temp.getName());
1760
0
        }
1761
0
        out.append(extensionsStr, status);
1762
0
    }
1763
0
    if (U_FAILURE(status)) {
1764
0
        return false;
1765
0
    }
1766
    // If the tag is not changed, return.
1767
0
    if (uprv_strcmp(out.data(), locale.getName()) == 0) {
1768
0
        out.clear();
1769
0
        return false;
1770
0
    }
1771
0
    return true;
1772
0
}
1773
1774
// Return true if the locale is changed during canonicalization.
1775
// The replaced value then will be put into out.
1776
bool
1777
canonicalizeLocale(const Locale& locale, CharString& out, UErrorCode& status)
1778
0
{
1779
0
    AliasReplacer replacer(status);
1780
0
    return replacer.replace(locale, out, status);
1781
0
}
1782
1783
// Function to optimize for known cases without so we can skip the loading
1784
// of resources in the startup time until we really need it.
1785
bool
1786
isKnownCanonicalizedLocale(const char* locale, UErrorCode& status)
1787
0
{
1788
0
    if (    uprv_strcmp(locale, "c") == 0 ||
1789
0
            uprv_strcmp(locale, "en") == 0 ||
1790
0
            uprv_strcmp(locale, "en_US") == 0) {
1791
0
        return true;
1792
0
    }
1793
1794
    // common well-known Canonicalized.
1795
0
    umtx_initOnce(gKnownCanonicalizedInitOnce,
1796
0
                  &loadKnownCanonicalized, status);
1797
0
    if (U_FAILURE(status)) {
1798
0
        return false;
1799
0
    }
1800
0
    U_ASSERT(gKnownCanonicalized != nullptr);
1801
0
    return uhash_geti(gKnownCanonicalized, locale) != 0;
1802
0
}
1803
1804
}  // namespace
1805
1806
// Function for testing.
1807
U_CAPI const char* const*
1808
ulocimp_getKnownCanonicalizedLocaleForTest(int32_t* length)
1809
0
{
1810
0
    *length = UPRV_LENGTHOF(KNOWN_CANONICALIZED);
1811
0
    return KNOWN_CANONICALIZED;
1812
0
}
1813
1814
// Function for testing.
1815
U_CAPI bool
1816
ulocimp_isCanonicalizedLocaleForTest(const char* localeName)
1817
0
{
1818
0
    Locale l(localeName);
1819
0
    UErrorCode status = U_ZERO_ERROR;
1820
0
    CharString temp;
1821
0
    return !canonicalizeLocale(l, temp, status) && U_SUCCESS(status);
1822
0
}
1823
1824
/*This function initializes a Locale from a C locale ID*/
1825
Locale& Locale::init(const char* localeID, UBool canonicalize)
1826
0
{
1827
0
    fIsBogus = FALSE;
1828
    /* Free our current storage */
1829
0
    if ((baseName != fullName) && (baseName != fullNameBuffer)) {
1830
0
        uprv_free(baseName);
1831
0
    }
1832
0
    baseName = NULL;
1833
0
    if(fullName != fullNameBuffer) {
1834
0
        uprv_free(fullName);
1835
0
        fullName = fullNameBuffer;
1836
0
    }
1837
1838
    // not a loop:
1839
    // just an easy way to have a common error-exit
1840
    // without goto and without another function
1841
0
    do {
1842
0
        char *separator;
1843
0
        char *field[5] = {0};
1844
0
        int32_t fieldLen[5] = {0};
1845
0
        int32_t fieldIdx;
1846
0
        int32_t variantField;
1847
0
        int32_t length;
1848
0
        UErrorCode err;
1849
1850
0
        if(localeID == NULL) {
1851
            // not an error, just set the default locale
1852
0
            return *this = getDefault();
1853
0
        }
1854
1855
        /* preset all fields to empty */
1856
0
        language[0] = script[0] = country[0] = 0;
1857
1858
        // "canonicalize" the locale ID to ICU/Java format
1859
0
        err = U_ZERO_ERROR;
1860
0
        length = canonicalize ?
1861
0
            uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) :
1862
0
            uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err);
1863
1864
0
        if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) {
1865
0
            U_ASSERT(baseName == nullptr);
1866
            /*Go to heap for the fullName if necessary*/
1867
0
            fullName = (char *)uprv_malloc(sizeof(char)*(length + 1));
1868
0
            if(fullName == 0) {
1869
0
                fullName = fullNameBuffer;
1870
0
                break; // error: out of memory
1871
0
            }
1872
0
            err = U_ZERO_ERROR;
1873
0
            length = canonicalize ?
1874
0
                uloc_canonicalize(localeID, fullName, length+1, &err) :
1875
0
                uloc_getName(localeID, fullName, length+1, &err);
1876
0
        }
1877
0
        if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
1878
            /* should never occur */
1879
0
            break;
1880
0
        }
1881
1882
0
        variantBegin = length;
1883
1884
        /* after uloc_getName/canonicalize() we know that only '_' are separators */
1885
        /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */
1886
0
        separator = field[0] = fullName;
1887
0
        fieldIdx = 1;
1888
0
        char* at = uprv_strchr(fullName, '@');
1889
0
        while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 &&
1890
0
               fieldIdx < UPRV_LENGTHOF(field)-1 &&
1891
0
               (at == nullptr || separator < at)) {
1892
0
            field[fieldIdx] = separator + 1;
1893
0
            fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);
1894
0
            fieldIdx++;
1895
0
        }
1896
        // variant may contain @foo or .foo POSIX cruft; remove it
1897
0
        separator = uprv_strchr(field[fieldIdx-1], '@');
1898
0
        char* sep2 = uprv_strchr(field[fieldIdx-1], '.');
1899
0
        if (separator!=NULL || sep2!=NULL) {
1900
0
            if (separator==NULL || (sep2!=NULL && separator > sep2)) {
1901
0
                separator = sep2;
1902
0
            }
1903
0
            fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);
1904
0
        } else {
1905
0
            fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName);
1906
0
        }
1907
1908
0
        if (fieldLen[0] >= (int32_t)(sizeof(language)))
1909
0
        {
1910
0
            break; // error: the language field is too long
1911
0
        }
1912
1913
0
        variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */
1914
0
        if (fieldLen[0] > 0) {
1915
            /* We have a language */
1916
0
            uprv_memcpy(language, fullName, fieldLen[0]);
1917
0
            language[fieldLen[0]] = 0;
1918
0
        }
1919
0
        if (fieldLen[1] == 4 && uprv_isASCIILetter(field[1][0]) &&
1920
0
                uprv_isASCIILetter(field[1][1]) && uprv_isASCIILetter(field[1][2]) &&
1921
0
                uprv_isASCIILetter(field[1][3])) {
1922
            /* We have at least a script */
1923
0
            uprv_memcpy(script, field[1], fieldLen[1]);
1924
0
            script[fieldLen[1]] = 0;
1925
0
            variantField++;
1926
0
        }
1927
1928
0
        if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) {
1929
            /* We have a country */
1930
0
            uprv_memcpy(country, field[variantField], fieldLen[variantField]);
1931
0
            country[fieldLen[variantField]] = 0;
1932
0
            variantField++;
1933
0
        } else if (fieldLen[variantField] == 0) {
1934
0
            variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */
1935
0
        }
1936
1937
0
        if (fieldLen[variantField] > 0) {
1938
            /* We have a variant */
1939
0
            variantBegin = (int32_t)(field[variantField] - fullName);
1940
0
        }
1941
1942
0
        err = U_ZERO_ERROR;
1943
0
        initBaseName(err);
1944
0
        if (U_FAILURE(err)) {
1945
0
            break;
1946
0
        }
1947
1948
0
        if (canonicalize) {
1949
0
            if (!isKnownCanonicalizedLocale(fullName, err)) {
1950
0
                CharString replaced;
1951
                // Not sure it is already canonicalized
1952
0
                if (canonicalizeLocale(*this, replaced, err)) {
1953
0
                    U_ASSERT(U_SUCCESS(err));
1954
                    // If need replacement, call init again.
1955
0
                    init(replaced.data(), false);
1956
0
                }
1957
0
                if (U_FAILURE(err)) {
1958
0
                    break;
1959
0
                }
1960
0
            }
1961
0
        }   // if (canonicalize) {
1962
1963
        // successful end of init()
1964
0
        return *this;
1965
0
    } while(0); /*loop doesn't iterate*/
1966
1967
    // when an error occurs, then set this object to "bogus" (there is no UErrorCode here)
1968
0
    setToBogus();
1969
1970
0
    return *this;
1971
0
}
1972
1973
/*
1974
 * Set up the base name.
1975
 * If there are no key words, it's exactly the full name.
1976
 * If key words exist, it's the full name truncated at the '@' character.
1977
 * Need to set up both at init() and after setting a keyword.
1978
 */
1979
void
1980
0
Locale::initBaseName(UErrorCode &status) {
1981
0
    if (U_FAILURE(status)) {
1982
0
        return;
1983
0
    }
1984
0
    U_ASSERT(baseName==NULL || baseName==fullName);
1985
0
    const char *atPtr = uprv_strchr(fullName, '@');
1986
0
    const char *eqPtr = uprv_strchr(fullName, '=');
1987
0
    if (atPtr && eqPtr && atPtr < eqPtr) {
1988
        // Key words exist.
1989
0
        int32_t baseNameLength = (int32_t)(atPtr - fullName);
1990
0
        baseName = (char *)uprv_malloc(baseNameLength + 1);
1991
0
        if (baseName == NULL) {
1992
0
            status = U_MEMORY_ALLOCATION_ERROR;
1993
0
            return;
1994
0
        }
1995
0
        uprv_strncpy(baseName, fullName, baseNameLength);
1996
0
        baseName[baseNameLength] = 0;
1997
1998
        // The original computation of variantBegin leaves it equal to the length
1999
        // of fullName if there is no variant.  It should instead be
2000
        // the length of the baseName.
2001
0
        if (variantBegin > baseNameLength) {
2002
0
            variantBegin = baseNameLength;
2003
0
        }
2004
0
    } else {
2005
0
        baseName = fullName;
2006
0
    }
2007
0
}
2008
2009
2010
int32_t
2011
Locale::hashCode() const
2012
0
{
2013
0
    return ustr_hashCharsN(fullName, static_cast<int32_t>(uprv_strlen(fullName)));
2014
0
}
2015
2016
void
2017
0
Locale::setToBogus() {
2018
    /* Free our current storage */
2019
0
    if((baseName != fullName) && (baseName != fullNameBuffer)) {
2020
0
        uprv_free(baseName);
2021
0
    }
2022
0
    baseName = NULL;
2023
0
    if(fullName != fullNameBuffer) {
2024
0
        uprv_free(fullName);
2025
0
        fullName = fullNameBuffer;
2026
0
    }
2027
0
    *fullNameBuffer = 0;
2028
0
    *language = 0;
2029
0
    *script = 0;
2030
0
    *country = 0;
2031
0
    fIsBogus = TRUE;
2032
0
    variantBegin = 0;
2033
0
}
2034
2035
const Locale& U_EXPORT2
2036
Locale::getDefault()
2037
0
{
2038
0
    {
2039
0
        Mutex lock(&gDefaultLocaleMutex);
2040
0
        if (gDefaultLocale != NULL) {
2041
0
            return *gDefaultLocale;
2042
0
        }
2043
0
    }
2044
0
    UErrorCode status = U_ZERO_ERROR;
2045
0
    return *locale_set_default_internal(NULL, status);
2046
0
}
2047
2048
2049
2050
void U_EXPORT2
2051
Locale::setDefault( const   Locale&     newLocale,
2052
                            UErrorCode&  status)
2053
0
{
2054
0
    if (U_FAILURE(status)) {
2055
0
        return;
2056
0
    }
2057
2058
    /* Set the default from the full name string of the supplied locale.
2059
     * This is a convenient way to access the default locale caching mechanisms.
2060
     */
2061
0
    const char *localeID = newLocale.getName();
2062
0
    locale_set_default_internal(localeID, status);
2063
0
}
2064
2065
void
2066
0
Locale::addLikelySubtags(UErrorCode& status) {
2067
0
    if (U_FAILURE(status)) {
2068
0
        return;
2069
0
    }
2070
2071
0
    CharString maximizedLocaleID;
2072
0
    {
2073
0
        CharStringByteSink sink(&maximizedLocaleID);
2074
0
        ulocimp_addLikelySubtags(fullName, sink, &status);
2075
0
    }
2076
2077
0
    if (U_FAILURE(status)) {
2078
0
        return;
2079
0
    }
2080
2081
0
    init(maximizedLocaleID.data(), /*canonicalize=*/FALSE);
2082
0
    if (isBogus()) {
2083
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2084
0
    }
2085
0
}
2086
2087
void
2088
0
Locale::minimizeSubtags(UErrorCode& status) {
2089
0
    if (U_FAILURE(status)) {
2090
0
        return;
2091
0
    }
2092
2093
0
    CharString minimizedLocaleID;
2094
0
    {
2095
0
        CharStringByteSink sink(&minimizedLocaleID);
2096
0
        ulocimp_minimizeSubtags(fullName, sink, &status);
2097
0
    }
2098
2099
0
    if (U_FAILURE(status)) {
2100
0
        return;
2101
0
    }
2102
2103
0
    init(minimizedLocaleID.data(), /*canonicalize=*/FALSE);
2104
0
    if (isBogus()) {
2105
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2106
0
    }
2107
0
}
2108
2109
void
2110
0
Locale::canonicalize(UErrorCode& status) {
2111
0
    if (U_FAILURE(status)) {
2112
0
        return;
2113
0
    }
2114
0
    if (isBogus()) {
2115
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2116
0
        return;
2117
0
    }
2118
0
    CharString uncanonicalized(fullName, status);
2119
0
    if (U_FAILURE(status)) {
2120
0
        return;
2121
0
    }
2122
0
    init(uncanonicalized.data(), /*canonicalize=*/TRUE);
2123
0
    if (isBogus()) {
2124
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2125
0
    }
2126
0
}
2127
2128
Locale U_EXPORT2
2129
Locale::forLanguageTag(StringPiece tag, UErrorCode& status)
2130
0
{
2131
0
    Locale result(Locale::eBOGUS);
2132
2133
0
    if (U_FAILURE(status)) {
2134
0
        return result;
2135
0
    }
2136
2137
    // If a BCP 47 language tag is passed as the language parameter to the
2138
    // normal Locale constructor, it will actually fall back to invoking
2139
    // uloc_forLanguageTag() to parse it if it somehow is able to detect that
2140
    // the string actually is BCP 47. This works well for things like strings
2141
    // using BCP 47 extensions, but it does not at all work for things like
2142
    // legacy language tags (marked as “Type: grandfathered” in BCP 47,
2143
    // e.g., "en-GB-oed") which are possible to also
2144
    // interpret as ICU locale IDs and because of that won't trigger the BCP 47
2145
    // parsing. Therefore the code here explicitly calls uloc_forLanguageTag()
2146
    // and then Locale::init(), instead of just calling the normal constructor.
2147
2148
0
    CharString localeID;
2149
0
    int32_t parsedLength;
2150
0
    {
2151
0
        CharStringByteSink sink(&localeID);
2152
0
        ulocimp_forLanguageTag(
2153
0
                tag.data(),
2154
0
                tag.length(),
2155
0
                sink,
2156
0
                &parsedLength,
2157
0
                &status);
2158
0
    }
2159
2160
0
    if (U_FAILURE(status)) {
2161
0
        return result;
2162
0
    }
2163
2164
0
    if (parsedLength != tag.size()) {
2165
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2166
0
        return result;
2167
0
    }
2168
2169
0
    result.init(localeID.data(), /*canonicalize=*/FALSE);
2170
0
    if (result.isBogus()) {
2171
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2172
0
    }
2173
0
    return result;
2174
0
}
2175
2176
void
2177
Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const
2178
0
{
2179
0
    if (U_FAILURE(status)) {
2180
0
        return;
2181
0
    }
2182
2183
0
    if (fIsBogus) {
2184
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2185
0
        return;
2186
0
    }
2187
2188
0
    ulocimp_toLanguageTag(fullName, sink, /*strict=*/FALSE, &status);
2189
0
}
2190
2191
Locale U_EXPORT2
2192
Locale::createFromName (const char *name)
2193
0
{
2194
0
    if (name) {
2195
0
        Locale l("");
2196
0
        l.init(name, FALSE);
2197
0
        return l;
2198
0
    }
2199
0
    else {
2200
0
        return getDefault();
2201
0
    }
2202
0
}
2203
2204
Locale U_EXPORT2
2205
0
Locale::createCanonical(const char* name) {
2206
0
    Locale loc("");
2207
0
    loc.init(name, TRUE);
2208
0
    return loc;
2209
0
}
2210
2211
const char *
2212
Locale::getISO3Language() const
2213
0
{
2214
0
    return uloc_getISO3Language(fullName);
2215
0
}
2216
2217
2218
const char *
2219
Locale::getISO3Country() const
2220
0
{
2221
0
    return uloc_getISO3Country(fullName);
2222
0
}
2223
2224
/**
2225
 * Return the LCID value as specified in the "LocaleID" resource for this
2226
 * locale.  The LocaleID must be expressed as a hexadecimal number, from
2227
 * one to four digits.  If the LocaleID resource is not present, or is
2228
 * in an incorrect format, 0 is returned.  The LocaleID is for use in
2229
 * Windows (it is an LCID), but is available on all platforms.
2230
 */
2231
uint32_t
2232
Locale::getLCID() const
2233
0
{
2234
0
    return uloc_getLCID(fullName);
2235
0
}
2236
2237
const char* const* U_EXPORT2 Locale::getISOCountries()
2238
0
{
2239
0
    return uloc_getISOCountries();
2240
0
}
2241
2242
const char* const* U_EXPORT2 Locale::getISOLanguages()
2243
0
{
2244
0
    return uloc_getISOLanguages();
2245
0
}
2246
2247
// Set the locale's data based on a posix id.
2248
void Locale::setFromPOSIXID(const char *posixID)
2249
0
{
2250
0
    init(posixID, TRUE);
2251
0
}
2252
2253
const Locale & U_EXPORT2
2254
Locale::getRoot(void)
2255
0
{
2256
0
    return getLocale(eROOT);
2257
0
}
2258
2259
const Locale & U_EXPORT2
2260
Locale::getEnglish(void)
2261
0
{
2262
0
    return getLocale(eENGLISH);
2263
0
}
2264
2265
const Locale & U_EXPORT2
2266
Locale::getFrench(void)
2267
0
{
2268
0
    return getLocale(eFRENCH);
2269
0
}
2270
2271
const Locale & U_EXPORT2
2272
Locale::getGerman(void)
2273
0
{
2274
0
    return getLocale(eGERMAN);
2275
0
}
2276
2277
const Locale & U_EXPORT2
2278
Locale::getItalian(void)
2279
0
{
2280
0
    return getLocale(eITALIAN);
2281
0
}
2282
2283
const Locale & U_EXPORT2
2284
Locale::getJapanese(void)
2285
0
{
2286
0
    return getLocale(eJAPANESE);
2287
0
}
2288
2289
const Locale & U_EXPORT2
2290
Locale::getKorean(void)
2291
0
{
2292
0
    return getLocale(eKOREAN);
2293
0
}
2294
2295
const Locale & U_EXPORT2
2296
Locale::getChinese(void)
2297
0
{
2298
0
    return getLocale(eCHINESE);
2299
0
}
2300
2301
const Locale & U_EXPORT2
2302
Locale::getSimplifiedChinese(void)
2303
0
{
2304
0
    return getLocale(eCHINA);
2305
0
}
2306
2307
const Locale & U_EXPORT2
2308
Locale::getTraditionalChinese(void)
2309
0
{
2310
0
    return getLocale(eTAIWAN);
2311
0
}
2312
2313
2314
const Locale & U_EXPORT2
2315
Locale::getFrance(void)
2316
0
{
2317
0
    return getLocale(eFRANCE);
2318
0
}
2319
2320
const Locale & U_EXPORT2
2321
Locale::getGermany(void)
2322
0
{
2323
0
    return getLocale(eGERMANY);
2324
0
}
2325
2326
const Locale & U_EXPORT2
2327
Locale::getItaly(void)
2328
0
{
2329
0
    return getLocale(eITALY);
2330
0
}
2331
2332
const Locale & U_EXPORT2
2333
Locale::getJapan(void)
2334
0
{
2335
0
    return getLocale(eJAPAN);
2336
0
}
2337
2338
const Locale & U_EXPORT2
2339
Locale::getKorea(void)
2340
0
{
2341
0
    return getLocale(eKOREA);
2342
0
}
2343
2344
const Locale & U_EXPORT2
2345
Locale::getChina(void)
2346
0
{
2347
0
    return getLocale(eCHINA);
2348
0
}
2349
2350
const Locale & U_EXPORT2
2351
Locale::getPRC(void)
2352
0
{
2353
0
    return getLocale(eCHINA);
2354
0
}
2355
2356
const Locale & U_EXPORT2
2357
Locale::getTaiwan(void)
2358
0
{
2359
0
    return getLocale(eTAIWAN);
2360
0
}
2361
2362
const Locale & U_EXPORT2
2363
Locale::getUK(void)
2364
0
{
2365
0
    return getLocale(eUK);
2366
0
}
2367
2368
const Locale & U_EXPORT2
2369
Locale::getUS(void)
2370
0
{
2371
0
    return getLocale(eUS);
2372
0
}
2373
2374
const Locale & U_EXPORT2
2375
Locale::getCanada(void)
2376
0
{
2377
0
    return getLocale(eCANADA);
2378
0
}
2379
2380
const Locale & U_EXPORT2
2381
Locale::getCanadaFrench(void)
2382
0
{
2383
0
    return getLocale(eCANADA_FRENCH);
2384
0
}
2385
2386
const Locale &
2387
Locale::getLocale(int locid)
2388
0
{
2389
0
    Locale *localeCache = getLocaleCache();
2390
0
    U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0));
2391
0
    if (localeCache == NULL) {
2392
        // Failure allocating the locale cache.
2393
        //   The best we can do is return a NULL reference.
2394
0
        locid = 0;
2395
0
    }
2396
0
    return localeCache[locid]; /*operating on NULL*/
2397
0
}
2398
2399
/*
2400
This function is defined this way in order to get around static
2401
initialization and static destruction.
2402
 */
2403
Locale *
2404
Locale::getLocaleCache(void)
2405
0
{
2406
0
    UErrorCode status = U_ZERO_ERROR;
2407
0
    umtx_initOnce(gLocaleCacheInitOnce, locale_init, status);
2408
0
    return gLocaleCache;
2409
0
}
2410
2411
class KeywordEnumeration : public StringEnumeration {
2412
private:
2413
    char *keywords;
2414
    char *current;
2415
    int32_t length;
2416
    UnicodeString currUSKey;
2417
    static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */
2418
2419
public:
2420
0
    static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; }
2421
0
    virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); }
2422
public:
2423
    KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status)
2424
0
        : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) {
2425
0
        if(U_SUCCESS(status) && keywordLen != 0) {
2426
0
            if(keys == NULL || keywordLen < 0) {
2427
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
2428
0
            } else {
2429
0
                keywords = (char *)uprv_malloc(keywordLen+1);
2430
0
                if (keywords == NULL) {
2431
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2432
0
                }
2433
0
                else {
2434
0
                    uprv_memcpy(keywords, keys, keywordLen);
2435
0
                    keywords[keywordLen] = 0;
2436
0
                    current = keywords + currentIndex;
2437
0
                    length = keywordLen;
2438
0
                }
2439
0
            }
2440
0
        }
2441
0
    }
2442
2443
    virtual ~KeywordEnumeration();
2444
2445
    virtual StringEnumeration * clone() const
2446
0
    {
2447
0
        UErrorCode status = U_ZERO_ERROR;
2448
0
        return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status);
2449
0
    }
2450
2451
0
    virtual int32_t count(UErrorCode &/*status*/) const {
2452
0
        char *kw = keywords;
2453
0
        int32_t result = 0;
2454
0
        while(*kw) {
2455
0
            result++;
2456
0
            kw += uprv_strlen(kw)+1;
2457
0
        }
2458
0
        return result;
2459
0
    }
2460
2461
0
    virtual const char* next(int32_t* resultLength, UErrorCode& status) {
2462
0
        const char* result;
2463
0
        int32_t len;
2464
0
        if(U_SUCCESS(status) && *current != 0) {
2465
0
            result = current;
2466
0
            len = (int32_t)uprv_strlen(current);
2467
0
            current += len+1;
2468
0
            if(resultLength != NULL) {
2469
0
                *resultLength = len;
2470
0
            }
2471
0
        } else {
2472
0
            if(resultLength != NULL) {
2473
0
                *resultLength = 0;
2474
0
            }
2475
0
            result = NULL;
2476
0
        }
2477
0
        return result;
2478
0
    }
2479
2480
0
    virtual const UnicodeString* snext(UErrorCode& status) {
2481
0
        int32_t resultLength = 0;
2482
0
        const char *s = next(&resultLength, status);
2483
0
        return setChars(s, resultLength, status);
2484
0
    }
2485
2486
0
    virtual void reset(UErrorCode& /*status*/) {
2487
0
        current = keywords;
2488
0
    }
2489
};
2490
2491
const char KeywordEnumeration::fgClassID = '\0';
2492
2493
0
KeywordEnumeration::~KeywordEnumeration() {
2494
0
    uprv_free(keywords);
2495
0
}
2496
2497
// A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in
2498
// the next() method for each keyword before returning it.
2499
class UnicodeKeywordEnumeration : public KeywordEnumeration {
2500
public:
2501
    using KeywordEnumeration::KeywordEnumeration;
2502
    virtual ~UnicodeKeywordEnumeration();
2503
2504
0
    virtual const char* next(int32_t* resultLength, UErrorCode& status) {
2505
0
        const char* legacy_key = KeywordEnumeration::next(nullptr, status);
2506
0
        while (U_SUCCESS(status) && legacy_key != nullptr) {
2507
0
            const char* key = uloc_toUnicodeLocaleKey(legacy_key);
2508
0
            if (key != nullptr) {
2509
0
                if (resultLength != nullptr) {
2510
0
                    *resultLength = static_cast<int32_t>(uprv_strlen(key));
2511
0
                }
2512
0
                return key;
2513
0
            }
2514
            // Not a Unicode keyword, could be a t, x or other, continue to look at the next one.
2515
0
            legacy_key = KeywordEnumeration::next(nullptr, status);
2516
0
        }
2517
0
        if (resultLength != nullptr) *resultLength = 0;
2518
0
        return nullptr;
2519
0
    }
2520
};
2521
2522
// Out-of-line virtual destructor to serve as the "key function".
2523
UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default;
2524
2525
StringEnumeration *
2526
Locale::createKeywords(UErrorCode &status) const
2527
0
{
2528
0
    StringEnumeration *result = NULL;
2529
2530
0
    if (U_FAILURE(status)) {
2531
0
        return result;
2532
0
    }
2533
2534
0
    const char* variantStart = uprv_strchr(fullName, '@');
2535
0
    const char* assignment = uprv_strchr(fullName, '=');
2536
0
    if(variantStart) {
2537
0
        if(assignment > variantStart) {
2538
0
            CharString keywords;
2539
0
            CharStringByteSink sink(&keywords);
2540
0
            ulocimp_getKeywords(variantStart+1, '@', sink, FALSE, &status);
2541
0
            if (U_SUCCESS(status) && !keywords.isEmpty()) {
2542
0
                result = new KeywordEnumeration(keywords.data(), keywords.length(), 0, status);
2543
0
                if (!result) {
2544
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2545
0
                }
2546
0
            }
2547
0
        } else {
2548
0
            status = U_INVALID_FORMAT_ERROR;
2549
0
        }
2550
0
    }
2551
0
    return result;
2552
0
}
2553
2554
StringEnumeration *
2555
Locale::createUnicodeKeywords(UErrorCode &status) const
2556
0
{
2557
0
    StringEnumeration *result = NULL;
2558
2559
0
    if (U_FAILURE(status)) {
2560
0
        return result;
2561
0
    }
2562
2563
0
    const char* variantStart = uprv_strchr(fullName, '@');
2564
0
    const char* assignment = uprv_strchr(fullName, '=');
2565
0
    if(variantStart) {
2566
0
        if(assignment > variantStart) {
2567
0
            CharString keywords;
2568
0
            CharStringByteSink sink(&keywords);
2569
0
            ulocimp_getKeywords(variantStart+1, '@', sink, FALSE, &status);
2570
0
            if (U_SUCCESS(status) && !keywords.isEmpty()) {
2571
0
                result = new UnicodeKeywordEnumeration(keywords.data(), keywords.length(), 0, status);
2572
0
                if (!result) {
2573
0
                    status = U_MEMORY_ALLOCATION_ERROR;
2574
0
                }
2575
0
            }
2576
0
        } else {
2577
0
            status = U_INVALID_FORMAT_ERROR;
2578
0
        }
2579
0
    }
2580
0
    return result;
2581
0
}
2582
2583
int32_t
2584
Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const
2585
0
{
2586
0
    return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status);
2587
0
}
2588
2589
void
2590
0
Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const {
2591
0
    if (U_FAILURE(status)) {
2592
0
        return;
2593
0
    }
2594
2595
0
    if (fIsBogus) {
2596
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2597
0
        return;
2598
0
    }
2599
2600
    // TODO: Remove the need for a const char* to a NUL terminated buffer.
2601
0
    const CharString keywordName_nul(keywordName, status);
2602
0
    if (U_FAILURE(status)) {
2603
0
        return;
2604
0
    }
2605
2606
0
    ulocimp_getKeywordValue(fullName, keywordName_nul.data(), sink, &status);
2607
0
}
2608
2609
void
2610
Locale::getUnicodeKeywordValue(StringPiece keywordName,
2611
                               ByteSink& sink,
2612
0
                               UErrorCode& status) const {
2613
    // TODO: Remove the need for a const char* to a NUL terminated buffer.
2614
0
    const CharString keywordName_nul(keywordName, status);
2615
0
    if (U_FAILURE(status)) {
2616
0
        return;
2617
0
    }
2618
2619
0
    const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
2620
2621
0
    if (legacy_key == nullptr) {
2622
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2623
0
        return;
2624
0
    }
2625
2626
0
    CharString legacy_value;
2627
0
    {
2628
0
        CharStringByteSink sink(&legacy_value);
2629
0
        getKeywordValue(legacy_key, sink, status);
2630
0
    }
2631
2632
0
    if (U_FAILURE(status)) {
2633
0
        return;
2634
0
    }
2635
2636
0
    const char* unicode_value = uloc_toUnicodeLocaleType(
2637
0
            keywordName_nul.data(), legacy_value.data());
2638
2639
0
    if (unicode_value == nullptr) {
2640
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2641
0
        return;
2642
0
    }
2643
2644
0
    sink.Append(unicode_value, static_cast<int32_t>(uprv_strlen(unicode_value)));
2645
0
}
2646
2647
void
2648
Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)
2649
0
{
2650
0
    if (U_FAILURE(status)) {
2651
0
        return;
2652
0
    }
2653
0
    if (status == U_STRING_NOT_TERMINATED_WARNING) {
2654
0
        status = U_ZERO_ERROR;
2655
0
    }
2656
0
    int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY);
2657
0
    int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName,
2658
0
                                             bufferLength, &status) + 1;
2659
0
    U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING);
2660
    /* Handle the case the current buffer is not enough to hold the new id */
2661
0
    if (status == U_BUFFER_OVERFLOW_ERROR) {
2662
0
        U_ASSERT(newLength > bufferLength);
2663
0
        char* newFullName = (char *)uprv_malloc(newLength);
2664
0
        if (newFullName == nullptr) {
2665
0
            status = U_MEMORY_ALLOCATION_ERROR;
2666
0
            return;
2667
0
        }
2668
0
        uprv_strcpy(newFullName, fullName);
2669
0
        if (fullName != fullNameBuffer) {
2670
            // if full Name is already on the heap, need to free it.
2671
0
            uprv_free(fullName);
2672
0
            if (baseName == fullName) {
2673
0
                baseName = newFullName; // baseName should not point to freed memory.
2674
0
            }
2675
0
        }
2676
0
        fullName = newFullName;
2677
0
        status = U_ZERO_ERROR;
2678
0
        uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status);
2679
0
        U_ASSERT(status != U_STRING_NOT_TERMINATED_WARNING);
2680
0
    } else {
2681
0
        U_ASSERT(newLength <= bufferLength);
2682
0
    }
2683
0
    if (U_SUCCESS(status) && baseName == fullName) {
2684
        // May have added the first keyword, meaning that the fullName is no longer also the baseName.
2685
0
        initBaseName(status);
2686
0
    }
2687
0
}
2688
2689
void
2690
Locale::setKeywordValue(StringPiece keywordName,
2691
                        StringPiece keywordValue,
2692
0
                        UErrorCode& status) {
2693
    // TODO: Remove the need for a const char* to a NUL terminated buffer.
2694
0
    const CharString keywordName_nul(keywordName, status);
2695
0
    const CharString keywordValue_nul(keywordValue, status);
2696
0
    setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status);
2697
0
}
2698
2699
void
2700
Locale::setUnicodeKeywordValue(StringPiece keywordName,
2701
                               StringPiece keywordValue,
2702
0
                               UErrorCode& status) {
2703
    // TODO: Remove the need for a const char* to a NUL terminated buffer.
2704
0
    const CharString keywordName_nul(keywordName, status);
2705
0
    const CharString keywordValue_nul(keywordValue, status);
2706
2707
0
    if (U_FAILURE(status)) {
2708
0
        return;
2709
0
    }
2710
2711
0
    const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
2712
2713
0
    if (legacy_key == nullptr) {
2714
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
2715
0
        return;
2716
0
    }
2717
2718
0
    const char* legacy_value = nullptr;
2719
2720
0
    if (!keywordValue_nul.isEmpty()) {
2721
0
        legacy_value =
2722
0
            uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data());
2723
2724
0
        if (legacy_value == nullptr) {
2725
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
2726
0
            return;
2727
0
        }
2728
0
    }
2729
2730
0
    setKeywordValue(legacy_key, legacy_value, status);
2731
0
}
2732
2733
const char *
2734
0
Locale::getBaseName() const {
2735
0
    return baseName;
2736
0
}
2737
2738
0
Locale::Iterator::~Iterator() = default;
2739
2740
//eof
2741
U_NAMESPACE_END