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