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