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