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