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