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