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/strenum.h"
39#include "unicode/stringpiece.h"
40#include "unicode/uloc.h"
41#include "unicode/ures.h"
42
43#include "bytesinkutil.h"
44#include "charstr.h"
45#include "cmemory.h"
46#include "cstring.h"
47#include "mutex.h"
48#include "putilimp.h"
49#include "uassert.h"
50#include "ucln_cmn.h"
51#include "uhash.h"
52#include "ulocimp.h"
53#include "umutex.h"
54#include "ustr_imp.h"
55
56U_CDECL_BEGIN
57static UBool U_CALLCONV locale_cleanup(void);
58U_CDECL_END
59
60U_NAMESPACE_BEGIN
61
62static Locale *gLocaleCache = NULL;
63static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER;
64
65// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale.
66static UMutex gDefaultLocaleMutex;
67static UHashtable *gDefaultLocalesHashT = NULL;
68static Locale *gDefaultLocale = NULL;
69
70/**
71 * \def ULOC_STRING_LIMIT
72 * strings beyond this value crash in CharString
73 */
74#define ULOC_STRING_LIMIT 357913941
75
76U_NAMESPACE_END
77
78typedef 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
105U_CFUNC int32_t locale_getKeywords(const char *localeID,
106 char prev,
107 char *keywords, int32_t keywordCapacity,
108 UBool valuesToo,
109 UErrorCode *status);
110
111U_CDECL_BEGIN
112//
113// Deleter function for Locales owned by the default Locale hash table/
114//
115static void U_CALLCONV
116deleteLocale(void *obj) {
117 delete (icu::Locale *) obj;
118}
119
120static UBool U_CALLCONV locale_cleanup(void)
121{
122 U_NAMESPACE_USE
123
124 delete [] gLocaleCache;
125 gLocaleCache = NULL;
126 gLocaleCacheInitOnce.reset();
127
128 if (gDefaultLocalesHashT) {
129 uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func.
130 gDefaultLocalesHashT = NULL;
131 }
132 gDefaultLocale = NULL;
133 return TRUE;
134}
135
136
137static void U_CALLCONV locale_init(UErrorCode &status) {
138 U_NAMESPACE_USE
139
140 U_ASSERT(gLocaleCache == NULL);
141 gLocaleCache = new Locale[(int)eMAX_LOCALES];
142 if (gLocaleCache == NULL) {
143 status = U_MEMORY_ALLOCATION_ERROR;
144 return;
145 }
146 ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
147 gLocaleCache[eROOT] = Locale("");
148 gLocaleCache[eENGLISH] = Locale("en");
149 gLocaleCache[eFRENCH] = Locale("fr");
150 gLocaleCache[eGERMAN] = Locale("de");
151 gLocaleCache[eITALIAN] = Locale("it");
152 gLocaleCache[eJAPANESE] = Locale("ja");
153 gLocaleCache[eKOREAN] = Locale("ko");
154 gLocaleCache[eCHINESE] = Locale("zh");
155 gLocaleCache[eFRANCE] = Locale("fr", "FR");
156 gLocaleCache[eGERMANY] = Locale("de", "DE");
157 gLocaleCache[eITALY] = Locale("it", "IT");
158 gLocaleCache[eJAPAN] = Locale("ja", "JP");
159 gLocaleCache[eKOREA] = Locale("ko", "KR");
160 gLocaleCache[eCHINA] = Locale("zh", "CN");
161 gLocaleCache[eTAIWAN] = Locale("zh", "TW");
162 gLocaleCache[eUK] = Locale("en", "GB");
163 gLocaleCache[eUS] = Locale("en", "US");
164 gLocaleCache[eCANADA] = Locale("en", "CA");
165 gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA");
166}
167
168U_CDECL_END
169
170U_NAMESPACE_BEGIN
171
172Locale *locale_set_default_internal(const char *id, UErrorCode& status) {
173 // Synchronize this entire function.
174 Mutex lock(&gDefaultLocaleMutex);
175
176 UBool canonicalize = FALSE;
177
178 // If given a NULL string for the locale id, grab the default
179 // name from the system.
180 // (Different from most other locale APIs, where a null name means use
181 // the current ICU default locale.)
182 if (id == NULL) {
183 id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify.
184 canonicalize = TRUE; // always canonicalize host ID
185 }
186
187 CharString localeNameBuf;
188 {
189 CharStringByteSink sink(&localeNameBuf);
190 if (canonicalize) {
191 ulocimp_canonicalize(id, sink, &status);
192 } else {
193 ulocimp_getName(id, sink, &status);
194 }
195 }
196
197 if (U_FAILURE(status)) {
198 return gDefaultLocale;
199 }
200
201 if (gDefaultLocalesHashT == NULL) {
202 gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status);
203 if (U_FAILURE(status)) {
204 return gDefaultLocale;
205 }
206 uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale);
207 ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
208 }
209
210 Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf.data());
211 if (newDefault == NULL) {
212 newDefault = new Locale(Locale::eBOGUS);
213 if (newDefault == NULL) {
214 status = U_MEMORY_ALLOCATION_ERROR;
215 return gDefaultLocale;
216 }
217 newDefault->init(localeNameBuf.data(), FALSE);
218 uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status);
219 if (U_FAILURE(status)) {
220 return gDefaultLocale;
221 }
222 }
223 gDefaultLocale = newDefault;
224 return gDefaultLocale;
225}
226
227U_NAMESPACE_END
228
229/* sfb 07/21/99 */
230U_CFUNC void
231locale_set_default(const char *id)
232{
233 U_NAMESPACE_USE
234 UErrorCode status = U_ZERO_ERROR;
235 locale_set_default_internal(id, status);
236}
237/* end */
238
239U_CFUNC const char *
240locale_get_default(void)
241{
242 U_NAMESPACE_USE
243 return Locale::getDefault().getName();
244}
245
246
247U_NAMESPACE_BEGIN
248
249UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)
250
251/*Character separating the posix id fields*/
252// '_'
253// In the platform codepage.
254#define SEP_CHAR '_'
255
256Locale::~Locale()
257{
258 if (baseName != fullName) {
259 uprv_free(baseName);
260 }
261 baseName = NULL;
262 /*if fullName is on the heap, we free it*/
263 if (fullName != fullNameBuffer)
264 {
265 uprv_free(fullName);
266 fullName = NULL;
267 }
268}
269
270Locale::Locale()
271 : UObject(), fullName(fullNameBuffer), baseName(NULL)
272{
273 init(NULL, FALSE);
274}
275
276/*
277 * Internal constructor to allow construction of a locale object with
278 * NO side effects. (Default constructor tries to get
279 * the default locale.)
280 */
281Locale::Locale(Locale::ELocaleType)
282 : UObject(), fullName(fullNameBuffer), baseName(NULL)
283{
284 setToBogus();
285}
286
287
288Locale::Locale( const char * newLanguage,
289 const char * newCountry,
290 const char * newVariant,
291 const char * newKeywords)
292 : UObject(), fullName(fullNameBuffer), baseName(NULL)
293{
294 if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) )
295 {
296 init(NULL, FALSE); /* shortcut */
297 }
298 else
299 {
300 UErrorCode status = U_ZERO_ERROR;
301 int32_t size = 0;
302 int32_t lsize = 0;
303 int32_t csize = 0;
304 int32_t vsize = 0;
305 int32_t ksize = 0;
306
307 // Calculate the size of the resulting string.
308
309 // Language
310 if ( newLanguage != NULL )
311 {
312 lsize = (int32_t)uprv_strlen(newLanguage);
313 if ( lsize < 0 || lsize > ULOC_STRING_LIMIT ) { // int32 wrap
314 setToBogus();
315 return;
316 }
317 size = lsize;
318 }
319
320 CharString togo(newLanguage, lsize, status); // start with newLanguage
321
322 // _Country
323 if ( newCountry != NULL )
324 {
325 csize = (int32_t)uprv_strlen(newCountry);
326 if ( csize < 0 || csize > ULOC_STRING_LIMIT ) { // int32 wrap
327 setToBogus();
328 return;
329 }
330 size += csize;
331 }
332
333 // _Variant
334 if ( newVariant != NULL )
335 {
336 // remove leading _'s
337 while(newVariant[0] == SEP_CHAR)
338 {
339 newVariant++;
340 }
341
342 // remove trailing _'s
343 vsize = (int32_t)uprv_strlen(newVariant);
344 if ( vsize < 0 || vsize > ULOC_STRING_LIMIT ) { // int32 wrap
345 setToBogus();
346 return;
347 }
348 while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) )
349 {
350 vsize--;
351 }
352 }
353
354 if( vsize > 0 )
355 {
356 size += vsize;
357 }
358
359 // Separator rules:
360 if ( vsize > 0 )
361 {
362 size += 2; // at least: __v
363 }
364 else if ( csize > 0 )
365 {
366 size += 1; // at least: _v
367 }
368
369 if ( newKeywords != NULL)
370 {
371 ksize = (int32_t)uprv_strlen(newKeywords);
372 if ( ksize < 0 || ksize > ULOC_STRING_LIMIT ) {
373 setToBogus();
374 return;
375 }
376 size += ksize + 1;
377 }
378
379 // NOW we have the full locale string..
380 // Now, copy it back.
381
382 // newLanguage is already copied
383
384 if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v
385 { // ^
386 togo.append(SEP_CHAR, status);
387 }
388
389 if ( csize != 0 )
390 {
391 togo.append(newCountry, status);
392 }
393
394 if ( vsize != 0)
395 {
396 togo.append(SEP_CHAR, status)
397 .append(newVariant, vsize, status);
398 }
399
400 if ( ksize != 0)
401 {
402 if (uprv_strchr(newKeywords, '=')) {
403 togo.append('@', status); /* keyword parsing */
404 }
405 else {
406 togo.append('_', status); /* Variant parsing with a script */
407 if ( vsize == 0) {
408 togo.append('_', status); /* No country found */
409 }
410 }
411 togo.append(newKeywords, status);
412 }
413
414 if (U_FAILURE(status)) {
415 // Something went wrong with appending, etc.
416 setToBogus();
417 return;
418 }
419 // Parse it, because for example 'language' might really be a complete
420 // string.
421 init(togo.data(), FALSE);
422 }
423}
424
425Locale::Locale(const Locale &other)
426 : UObject(other), fullName(fullNameBuffer), baseName(NULL)
427{
428 *this = other;
429}
430
431Locale::Locale(Locale&& other) U_NOEXCEPT
432 : UObject(other), fullName(fullNameBuffer), baseName(fullName) {
433 *this = std::move(other);
434}
435
436Locale& Locale::operator=(const Locale& other) {
437 if (this == &other) {
438 return *this;
439 }
440
441 setToBogus();
442
443 if (other.fullName == other.fullNameBuffer) {
444 uprv_strcpy(fullNameBuffer, other.fullNameBuffer);
445 } else if (other.fullName == nullptr) {
446 fullName = nullptr;
447 } else {
448 fullName = uprv_strdup(other.fullName);
449 if (fullName == nullptr) return *this;
450 }
451
452 if (other.baseName == other.fullName) {
453 baseName = fullName;
454 } else if (other.baseName != nullptr) {
455 baseName = uprv_strdup(other.baseName);
456 if (baseName == nullptr) return *this;
457 }
458
459 uprv_strcpy(language, other.language);
460 uprv_strcpy(script, other.script);
461 uprv_strcpy(country, other.country);
462
463 variantBegin = other.variantBegin;
464 fIsBogus = other.fIsBogus;
465
466 return *this;
467}
468
469Locale& Locale::operator=(Locale&& other) U_NOEXCEPT {
470 if (baseName != fullName) uprv_free(baseName);
471 if (fullName != fullNameBuffer) uprv_free(fullName);
472
473 if (other.fullName == other.fullNameBuffer) {
474 uprv_strcpy(fullNameBuffer, other.fullNameBuffer);
475 fullName = fullNameBuffer;
476 } else {
477 fullName = other.fullName;
478 }
479
480 if (other.baseName == other.fullName) {
481 baseName = fullName;
482 } else {
483 baseName = other.baseName;
484 }
485
486 uprv_strcpy(language, other.language);
487 uprv_strcpy(script, other.script);
488 uprv_strcpy(country, other.country);
489
490 variantBegin = other.variantBegin;
491 fIsBogus = other.fIsBogus;
492
493 other.baseName = other.fullName = other.fullNameBuffer;
494
495 return *this;
496}
497
498Locale *
499Locale::clone() const {
500 return new Locale(*this);
501}
502
503UBool
504Locale::operator==( const Locale& other) const
505{
506 return (uprv_strcmp(other.fullName, fullName) == 0);
507}
508
509#define ISASCIIALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
510
511#if 0 // TURN OFF the new part of UTS35 Locale Canonicalization due to start up performance regression
512namespace {
513
514CharString& AppendLSCVE(CharString& out, const char* language, const char* script,
515 const char* country, const char* variants, const char* extension,
516 UErrorCode& status) {
517 out.append(language, status);
518 if (script && script[0] != '\0') {
519 out.append('_', status);
520 out.append(script, status);
521 }
522 if (country && country[0] != '\0') {
523 out.append('_', status);
524 out.append(country, status);
525 }
526 if (variants && variants[0] != '\0') {
527 if ((script == nullptr || script[0] == '\0') &&
528 (country == nullptr || country[0] == '\0')) {
529 out.append('_', status);
530 }
531 out.append('_', status);
532 out.append(variants, status);
533 }
534 if (extension && extension[0] != '\0') {
535 out.append(extension, status);
536 }
537 return out;
538}
539
540} // namespace
541#endif
542
543/*This function initializes a Locale from a C locale ID*/
544Locale& Locale::init(const char* localeID, UBool canonicalize)
545{
546 fIsBogus = FALSE;
547 /* Free our current storage */
548 if (baseName != fullName) {
549 uprv_free(baseName);
550 }
551 baseName = NULL;
552 if(fullName != fullNameBuffer) {
553 uprv_free(fullName);
554 fullName = fullNameBuffer;
555 }
556
557 // not a loop:
558 // just an easy way to have a common error-exit
559 // without goto and without another function
560 do {
561 char *separator;
562 char *field[5] = {0};
563 int32_t fieldLen[5] = {0};
564 int32_t fieldIdx;
565 int32_t variantField;
566 int32_t length;
567 UErrorCode err;
568
569 if(localeID == NULL) {
570 // not an error, just set the default locale
571 return *this = getDefault();
572 }
573
574 /* preset all fields to empty */
575 language[0] = script[0] = country[0] = 0;
576
577 // "canonicalize" the locale ID to ICU/Java format
578 err = U_ZERO_ERROR;
579 length = canonicalize ?
580 uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) :
581 uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err);
582
583 if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) {
584 /*Go to heap for the fullName if necessary*/
585 fullName = (char *)uprv_malloc(sizeof(char)*(length + 1));
586 if(fullName == 0) {
587 fullName = fullNameBuffer;
588 break; // error: out of memory
589 }
590 err = U_ZERO_ERROR;
591 length = canonicalize ?
592 uloc_canonicalize(localeID, fullName, length+1, &err) :
593 uloc_getName(localeID, fullName, length+1, &err);
594 }
595 if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
596 /* should never occur */
597 break;
598 }
599
600 variantBegin = length;
601
602 /* after uloc_getName/canonicalize() we know that only '_' are separators */
603 /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */
604 separator = field[0] = fullName;
605 fieldIdx = 1;
606 char* at = uprv_strchr(fullName, '@');
607 while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 &&
608 fieldIdx < UPRV_LENGTHOF(field)-1 &&
609 (at == nullptr || separator < at)) {
610 field[fieldIdx] = separator + 1;
611 fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);
612 fieldIdx++;
613 }
614 // variant may contain @foo or .foo POSIX cruft; remove it
615 separator = uprv_strchr(field[fieldIdx-1], '@');
616 char* sep2 = uprv_strchr(field[fieldIdx-1], '.');
617 if (separator!=NULL || sep2!=NULL) {
618 if (separator==NULL || (sep2!=NULL && separator > sep2)) {
619 separator = sep2;
620 }
621 fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);
622 } else {
623 fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName);
624 }
625
626 if (fieldLen[0] >= (int32_t)(sizeof(language)))
627 {
628 break; // error: the language field is too long
629 }
630
631 variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */
632 if (fieldLen[0] > 0) {
633 /* We have a language */
634 uprv_memcpy(language, fullName, fieldLen[0]);
635 language[fieldLen[0]] = 0;
636 }
637 if (fieldLen[1] == 4 && ISASCIIALPHA(field[1][0]) &&
638 ISASCIIALPHA(field[1][1]) && ISASCIIALPHA(field[1][2]) &&
639 ISASCIIALPHA(field[1][3])) {
640 /* We have at least a script */
641 uprv_memcpy(script, field[1], fieldLen[1]);
642 script[fieldLen[1]] = 0;
643 variantField++;
644 }
645
646 if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) {
647 /* We have a country */
648 uprv_memcpy(country, field[variantField], fieldLen[variantField]);
649 country[fieldLen[variantField]] = 0;
650 variantField++;
651 } else if (fieldLen[variantField] == 0) {
652 variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */
653 }
654
655 if (fieldLen[variantField] > 0) {
656 /* We have a variant */
657 variantBegin = (int32_t)(field[variantField] - fullName);
658 }
659
660 err = U_ZERO_ERROR;
661 initBaseName(err);
662 if (U_FAILURE(err)) {
663 break;
664 }
665
666#if 0 // TURN OFF the new part of UTS35 Locale Canonicalization due to start up performance regression
667 if (canonicalize) {
668 UErrorCode status = U_ZERO_ERROR;
669 // TODO: Try to use ResourceDataValue and ures_getValueWithFallback() etc.
670 LocalUResourceBundlePointer metadata(ures_openDirect(NULL, "metadata", &status));
671 LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(), "alias", NULL, &status));
672 // Look up the metadata:alias:language:$key:replacement entries
673 // key could be one of the following:
674 // language
675 // language_Script_REGION
676 // language_REGION
677 // language_variant
678 do {
679 // The resource structure looks like
680 // metadata {
681 // alias {
682 // language {
683 // art_lojban {
684 // replacement{"jbo"}
685 // }
686 // ...
687 // ks_Arab_IN {
688 // replacement{"ks_IN"}
689 // }
690 // ...
691 // no {
692 // replacement{"nb"}
693 // }
694 // ....
695 // zh_CN {
696 // replacement{"zh_Hans_CN"}
697 // }
698 // }
699 // ...
700 // }
701 // }
702 LocalUResourceBundlePointer languageAlias(ures_getByKey(metadataAlias.getAlias(), "language", NULL, &status));
703 if (U_FAILURE(status))
704 break;
705 CharString temp;
706 // Handle cases of key pattern "language _ variant"
707 // ex: Map "art_lojban" to "jbo"
708 const char* variants = getVariant();
709 if (variants != nullptr && variants[0] != '\0') {
710 const char* begin = variants;
711 const char* end = begin;
712 // We may have multiple variants, need to look at each of
713 // them.
714 do {
715 status = U_ZERO_ERROR;
716 end = uprv_strchr(begin, '_');
717 int32_t len = (end == nullptr) ? int32_t(uprv_strlen(begin)) : int32_t(end - begin);
718 temp.clear().append(getLanguage(), status).append("_", status).append(begin, len, status);
719 LocalUResourceBundlePointer languageVariantAlias(
720 ures_getByKey(languageAlias.getAlias(),
721 temp.data(),
722 NULL, &status));
723 temp.clear().appendInvariantChars(
724 UnicodeString(ures_getStringByKey(languageVariantAlias.getAlias(), "replacement", nullptr, &status)), status);
725 if (U_SUCCESS(status)) {
726 CharString newVar;
727 if (begin != variants) {
728 newVar.append(variants, static_cast<int32_t>(begin - variants - 1), status);
729 }
730 if (end != nullptr) {
731 if (begin != variants) {
732 newVar.append("_", status);
733 }
734 newVar.append(end + 1, status);
735 }
736 Locale l(temp.data());
737 init(AppendLSCVE(temp.clear(),
738 l.getLanguage(),
739 (getScript() != nullptr && getScript()[0] != '\0') ? getScript() : l.getScript(),
740 (getCountry() != nullptr && getCountry()[0] != '\0') ? getCountry() : l.getCountry(),
741 newVar.data(),
742 uprv_strchr(fullName, '@'), status).data(), false);
743 break;
744 }
745 begin = end + 1;
746 } while (end != nullptr);
747 } // End of handle language _ variant
748 // Handle cases of key pattern "language _ Script _ REGION"
749 // ex: Map "ks_Arab_IN" to "ks_IN"
750 if (getScript() != nullptr && getScript()[0] != '\0' &&
751 getCountry() != nullptr && getCountry()[0] != '\0') {
752 status = U_ZERO_ERROR;
753 LocalUResourceBundlePointer replacedAlias(
754 ures_getByKey(languageAlias.getAlias(),
755 AppendLSCVE(temp.clear(), getLanguage(), getScript(), getCountry(),
756 nullptr, nullptr, status).data(), NULL, &status));
757 temp.clear().appendInvariantChars(
758 UnicodeString(ures_getStringByKey(replacedAlias.getAlias(), "replacement", nullptr, &status)), status);
759 if (U_SUCCESS(status)) {
760 Locale l(temp.data());
761 init(AppendLSCVE(temp.clear(),
762 l.getLanguage(),
763 l.getScript(),
764 l.getCountry(),
765 getVariant(),
766 uprv_strchr(fullName, '@'), status).data(), false);
767 }
768 } // End of handle language _ Script _ REGION
769 // Handle cases of key pattern "language _ REGION"
770 // ex: Map "zh_CN" to "zh_Hans_CN"
771 if (getCountry() != nullptr && getCountry()[0] != '\0') {
772 status = U_ZERO_ERROR;
773 LocalUResourceBundlePointer replacedAlias(
774 ures_getByKey(languageAlias.getAlias(),
775 AppendLSCVE(temp.clear(), getLanguage(), nullptr, getCountry(),
776 nullptr, nullptr, status).data(), NULL, &status));
777 temp.clear().appendInvariantChars(
778 UnicodeString(ures_getStringByKey(replacedAlias.getAlias(), "replacement", nullptr, &status)), status);
779 if (U_SUCCESS(status)) {
780 Locale l(temp.data());
781 init(AppendLSCVE(temp.clear(),
782 l.getLanguage(),
783 (getScript() != nullptr && getScript()[0] != '\0') ? getScript() : l.getScript(),
784 l.getCountry(),
785 getVariant(),
786 uprv_strchr(fullName, '@'), status).data(), false);
787 }
788 } // End of handle "language _ REGION"
789 // Handle cases of key pattern "language"
790 // ex: Map "no" to "nb"
791 {
792 status = U_ZERO_ERROR;
793 LocalUResourceBundlePointer replaceLanguageAlias(ures_getByKey(languageAlias.getAlias(), getLanguage(), NULL, &status));
794 temp.clear().appendInvariantChars(
795 UnicodeString(ures_getStringByKey(replaceLanguageAlias.getAlias(), "replacement", nullptr, &status)), status);
796 if (U_SUCCESS(status)) {
797 Locale l(temp.data());
798 init(AppendLSCVE(temp.clear(),
799 l.getLanguage(),
800 (getScript() != nullptr && getScript()[0] != '\0') ? getScript() : l.getScript(),
801 (getCountry() != nullptr && getCountry()[0] != '\0') ? getCountry() : l.getCountry(),
802 getVariant(),
803 uprv_strchr(fullName, '@'), status).data(), false);
804 }
805 } // End of handle "language"
806
807 // Look up the metadata:alias:territory:$key:replacement entries
808 // key is region code.
809 if (getCountry() != nullptr) {
810 status = U_ZERO_ERROR;
811 // The resource structure looks like
812 // metadata {
813 // alias {
814 // ...
815 // territory: {
816 // 172 {
817 // replacement{"RU AM AZ BY GE KG KZ MD TJ TM UA UZ"}
818 // }
819 // ...
820 // 554 {
821 // replacement{"NZ"}
822 // }
823 // }
824 // }
825 // }
826 LocalUResourceBundlePointer territoryAlias(ures_getByKey(metadataAlias.getAlias(), "territory", NULL, &status));
827 LocalUResourceBundlePointer countryAlias(ures_getByKey(territoryAlias.getAlias(), getCountry(), NULL, &status));
828 UnicodeString replacements(
829 ures_getStringByKey(countryAlias.getAlias(), "replacement", nullptr, &status));
830 if (U_SUCCESS(status)) {
831 CharString replacedCountry;
832 int32_t delPos = replacements.indexOf(' ');
833 if (delPos == -1) {
834 replacedCountry.appendInvariantChars(replacements, status);
835 } else {
836 Locale l(AppendLSCVE(temp.clear(), getLanguage(), nullptr, getScript(),
837 nullptr, nullptr, status).data());
838 l.addLikelySubtags(status);
839 if (replacements.indexOf(UnicodeString(l.getCountry())) != -1) {
840 replacedCountry.append(l.getCountry(), status);
841 } else {
842 replacedCountry.appendInvariantChars(replacements.getBuffer(), delPos, status);
843 }
844 }
845 init(AppendLSCVE(temp.clear(),
846 getLanguage(),
847 getScript(),
848 replacedCountry.data(),
849 getVariant(),
850 uprv_strchr(fullName, '@'), status).data(), false);
851 }
852 } // End of handle REGION
853 } while (0);
854 } // if (canonicalize) {
855#endif
856
857 // successful end of init()
858 return *this;
859 } while(0); /*loop doesn't iterate*/
860
861 // when an error occurs, then set this object to "bogus" (there is no UErrorCode here)
862 setToBogus();
863
864 return *this;
865}
866
867/*
868 * Set up the base name.
869 * If there are no key words, it's exactly the full name.
870 * If key words exist, it's the full name truncated at the '@' character.
871 * Need to set up both at init() and after setting a keyword.
872 */
873void
874Locale::initBaseName(UErrorCode &status) {
875 if (U_FAILURE(status)) {
876 return;
877 }
878 U_ASSERT(baseName==NULL || baseName==fullName);
879 const char *atPtr = uprv_strchr(fullName, '@');
880 const char *eqPtr = uprv_strchr(fullName, '=');
881 if (atPtr && eqPtr && atPtr < eqPtr) {
882 // Key words exist.
883 int32_t baseNameLength = (int32_t)(atPtr - fullName);
884 baseName = (char *)uprv_malloc(baseNameLength + 1);
885 if (baseName == NULL) {
886 status = U_MEMORY_ALLOCATION_ERROR;
887 return;
888 }
889 uprv_strncpy(baseName, fullName, baseNameLength);
890 baseName[baseNameLength] = 0;
891
892 // The original computation of variantBegin leaves it equal to the length
893 // of fullName if there is no variant. It should instead be
894 // the length of the baseName.
895 if (variantBegin > baseNameLength) {
896 variantBegin = baseNameLength;
897 }
898 } else {
899 baseName = fullName;
900 }
901}
902
903
904int32_t
905Locale::hashCode() const
906{
907 return ustr_hashCharsN(fullName, static_cast<int32_t>(uprv_strlen(fullName)));
908}
909
910void
911Locale::setToBogus() {
912 /* Free our current storage */
913 if(baseName != fullName) {
914 uprv_free(baseName);
915 }
916 baseName = NULL;
917 if(fullName != fullNameBuffer) {
918 uprv_free(fullName);
919 fullName = fullNameBuffer;
920 }
921 *fullNameBuffer = 0;
922 *language = 0;
923 *script = 0;
924 *country = 0;
925 fIsBogus = TRUE;
926 variantBegin = 0;
927}
928
929const Locale& U_EXPORT2
930Locale::getDefault()
931{
932 {
933 Mutex lock(&gDefaultLocaleMutex);
934 if (gDefaultLocale != NULL) {
935 return *gDefaultLocale;
936 }
937 }
938 UErrorCode status = U_ZERO_ERROR;
939 return *locale_set_default_internal(NULL, status);
940}
941
942
943
944void U_EXPORT2
945Locale::setDefault( const Locale& newLocale,
946 UErrorCode& status)
947{
948 if (U_FAILURE(status)) {
949 return;
950 }
951
952 /* Set the default from the full name string of the supplied locale.
953 * This is a convenient way to access the default locale caching mechanisms.
954 */
955 const char *localeID = newLocale.getName();
956 locale_set_default_internal(localeID, status);
957}
958
959void
960Locale::addLikelySubtags(UErrorCode& status) {
961 if (U_FAILURE(status)) {
962 return;
963 }
964
965 CharString maximizedLocaleID;
966 {
967 CharStringByteSink sink(&maximizedLocaleID);
968 ulocimp_addLikelySubtags(fullName, sink, &status);
969 }
970
971 if (U_FAILURE(status)) {
972 return;
973 }
974
975 init(maximizedLocaleID.data(), /*canonicalize=*/FALSE);
976 if (isBogus()) {
977 status = U_ILLEGAL_ARGUMENT_ERROR;
978 }
979}
980
981void
982Locale::minimizeSubtags(UErrorCode& status) {
983 if (U_FAILURE(status)) {
984 return;
985 }
986
987 CharString minimizedLocaleID;
988 {
989 CharStringByteSink sink(&minimizedLocaleID);
990 ulocimp_minimizeSubtags(fullName, sink, &status);
991 }
992
993 if (U_FAILURE(status)) {
994 return;
995 }
996
997 init(minimizedLocaleID.data(), /*canonicalize=*/FALSE);
998 if (isBogus()) {
999 status = U_ILLEGAL_ARGUMENT_ERROR;
1000 }
1001}
1002
1003void
1004Locale::canonicalize(UErrorCode& status) {
1005 if (U_FAILURE(status)) {
1006 return;
1007 }
1008 if (isBogus()) {
1009 status = U_ILLEGAL_ARGUMENT_ERROR;
1010 return;
1011 }
1012 CharString uncanonicalized(fullName, status);
1013 if (U_FAILURE(status)) {
1014 return;
1015 }
1016 init(uncanonicalized.data(), /*canonicalize=*/TRUE);
1017 if (isBogus()) {
1018 status = U_ILLEGAL_ARGUMENT_ERROR;
1019 }
1020}
1021
1022Locale U_EXPORT2
1023Locale::forLanguageTag(StringPiece tag, UErrorCode& status)
1024{
1025 Locale result(Locale::eBOGUS);
1026
1027 if (U_FAILURE(status)) {
1028 return result;
1029 }
1030
1031 // If a BCP-47 language tag is passed as the language parameter to the
1032 // normal Locale constructor, it will actually fall back to invoking
1033 // uloc_forLanguageTag() to parse it if it somehow is able to detect that
1034 // the string actually is BCP-47. This works well for things like strings
1035 // using BCP-47 extensions, but it does not at all work for things like
1036 // BCP-47 grandfathered tags (eg. "en-GB-oed") which are possible to also
1037 // interpret as ICU locale IDs and because of that won't trigger the BCP-47
1038 // parsing. Therefore the code here explicitly calls uloc_forLanguageTag()
1039 // and then Locale::init(), instead of just calling the normal constructor.
1040
1041 CharString localeID;
1042 int32_t parsedLength;
1043 {
1044 CharStringByteSink sink(&localeID);
1045 ulocimp_forLanguageTag(
1046 tag.data(),
1047 tag.length(),
1048 sink,
1049 &parsedLength,
1050 &status);
1051 }
1052
1053 if (U_FAILURE(status)) {
1054 return result;
1055 }
1056
1057 if (parsedLength != tag.size()) {
1058 status = U_ILLEGAL_ARGUMENT_ERROR;
1059 return result;
1060 }
1061
1062 result.init(localeID.data(), /*canonicalize=*/FALSE);
1063 if (result.isBogus()) {
1064 status = U_ILLEGAL_ARGUMENT_ERROR;
1065 }
1066 return result;
1067}
1068
1069void
1070Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const
1071{
1072 if (U_FAILURE(status)) {
1073 return;
1074 }
1075
1076 if (fIsBogus) {
1077 status = U_ILLEGAL_ARGUMENT_ERROR;
1078 return;
1079 }
1080
1081 ulocimp_toLanguageTag(fullName, sink, /*strict=*/FALSE, &status);
1082}
1083
1084Locale U_EXPORT2
1085Locale::createFromName (const char *name)
1086{
1087 if (name) {
1088 Locale l("");
1089 l.init(name, FALSE);
1090 return l;
1091 }
1092 else {
1093 return getDefault();
1094 }
1095}
1096
1097Locale U_EXPORT2
1098Locale::createCanonical(const char* name) {
1099 Locale loc("");
1100 loc.init(name, TRUE);
1101 return loc;
1102}
1103
1104const char *
1105Locale::getISO3Language() const
1106{
1107 return uloc_getISO3Language(fullName);
1108}
1109
1110
1111const char *
1112Locale::getISO3Country() const
1113{
1114 return uloc_getISO3Country(fullName);
1115}
1116
1117/**
1118 * Return the LCID value as specified in the "LocaleID" resource for this
1119 * locale. The LocaleID must be expressed as a hexadecimal number, from
1120 * one to four digits. If the LocaleID resource is not present, or is
1121 * in an incorrect format, 0 is returned. The LocaleID is for use in
1122 * Windows (it is an LCID), but is available on all platforms.
1123 */
1124uint32_t
1125Locale::getLCID() const
1126{
1127 return uloc_getLCID(fullName);
1128}
1129
1130const char* const* U_EXPORT2 Locale::getISOCountries()
1131{
1132 return uloc_getISOCountries();
1133}
1134
1135const char* const* U_EXPORT2 Locale::getISOLanguages()
1136{
1137 return uloc_getISOLanguages();
1138}
1139
1140// Set the locale's data based on a posix id.
1141void Locale::setFromPOSIXID(const char *posixID)
1142{
1143 init(posixID, TRUE);
1144}
1145
1146const Locale & U_EXPORT2
1147Locale::getRoot(void)
1148{
1149 return getLocale(eROOT);
1150}
1151
1152const Locale & U_EXPORT2
1153Locale::getEnglish(void)
1154{
1155 return getLocale(eENGLISH);
1156}
1157
1158const Locale & U_EXPORT2
1159Locale::getFrench(void)
1160{
1161 return getLocale(eFRENCH);
1162}
1163
1164const Locale & U_EXPORT2
1165Locale::getGerman(void)
1166{
1167 return getLocale(eGERMAN);
1168}
1169
1170const Locale & U_EXPORT2
1171Locale::getItalian(void)
1172{
1173 return getLocale(eITALIAN);
1174}
1175
1176const Locale & U_EXPORT2
1177Locale::getJapanese(void)
1178{
1179 return getLocale(eJAPANESE);
1180}
1181
1182const Locale & U_EXPORT2
1183Locale::getKorean(void)
1184{
1185 return getLocale(eKOREAN);
1186}
1187
1188const Locale & U_EXPORT2
1189Locale::getChinese(void)
1190{
1191 return getLocale(eCHINESE);
1192}
1193
1194const Locale & U_EXPORT2
1195Locale::getSimplifiedChinese(void)
1196{
1197 return getLocale(eCHINA);
1198}
1199
1200const Locale & U_EXPORT2
1201Locale::getTraditionalChinese(void)
1202{
1203 return getLocale(eTAIWAN);
1204}
1205
1206
1207const Locale & U_EXPORT2
1208Locale::getFrance(void)
1209{
1210 return getLocale(eFRANCE);
1211}
1212
1213const Locale & U_EXPORT2
1214Locale::getGermany(void)
1215{
1216 return getLocale(eGERMANY);
1217}
1218
1219const Locale & U_EXPORT2
1220Locale::getItaly(void)
1221{
1222 return getLocale(eITALY);
1223}
1224
1225const Locale & U_EXPORT2
1226Locale::getJapan(void)
1227{
1228 return getLocale(eJAPAN);
1229}
1230
1231const Locale & U_EXPORT2
1232Locale::getKorea(void)
1233{
1234 return getLocale(eKOREA);
1235}
1236
1237const Locale & U_EXPORT2
1238Locale::getChina(void)
1239{
1240 return getLocale(eCHINA);
1241}
1242
1243const Locale & U_EXPORT2
1244Locale::getPRC(void)
1245{
1246 return getLocale(eCHINA);
1247}
1248
1249const Locale & U_EXPORT2
1250Locale::getTaiwan(void)
1251{
1252 return getLocale(eTAIWAN);
1253}
1254
1255const Locale & U_EXPORT2
1256Locale::getUK(void)
1257{
1258 return getLocale(eUK);
1259}
1260
1261const Locale & U_EXPORT2
1262Locale::getUS(void)
1263{
1264 return getLocale(eUS);
1265}
1266
1267const Locale & U_EXPORT2
1268Locale::getCanada(void)
1269{
1270 return getLocale(eCANADA);
1271}
1272
1273const Locale & U_EXPORT2
1274Locale::getCanadaFrench(void)
1275{
1276 return getLocale(eCANADA_FRENCH);
1277}
1278
1279const Locale &
1280Locale::getLocale(int locid)
1281{
1282 Locale *localeCache = getLocaleCache();
1283 U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0));
1284 if (localeCache == NULL) {
1285 // Failure allocating the locale cache.
1286 // The best we can do is return a NULL reference.
1287 locid = 0;
1288 }
1289 return localeCache[locid]; /*operating on NULL*/
1290}
1291
1292/*
1293This function is defined this way in order to get around static
1294initialization and static destruction.
1295 */
1296Locale *
1297Locale::getLocaleCache(void)
1298{
1299 UErrorCode status = U_ZERO_ERROR;
1300 umtx_initOnce(gLocaleCacheInitOnce, locale_init, status);
1301 return gLocaleCache;
1302}
1303
1304class KeywordEnumeration : public StringEnumeration {
1305private:
1306 char *keywords;
1307 char *current;
1308 int32_t length;
1309 UnicodeString currUSKey;
1310 static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */
1311
1312public:
1313 static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; }
1314 virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); }
1315public:
1316 KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status)
1317 : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) {
1318 if(U_SUCCESS(status) && keywordLen != 0) {
1319 if(keys == NULL || keywordLen < 0) {
1320 status = U_ILLEGAL_ARGUMENT_ERROR;
1321 } else {
1322 keywords = (char *)uprv_malloc(keywordLen+1);
1323 if (keywords == NULL) {
1324 status = U_MEMORY_ALLOCATION_ERROR;
1325 }
1326 else {
1327 uprv_memcpy(keywords, keys, keywordLen);
1328 keywords[keywordLen] = 0;
1329 current = keywords + currentIndex;
1330 length = keywordLen;
1331 }
1332 }
1333 }
1334 }
1335
1336 virtual ~KeywordEnumeration();
1337
1338 virtual StringEnumeration * clone() const
1339 {
1340 UErrorCode status = U_ZERO_ERROR;
1341 return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status);
1342 }
1343
1344 virtual int32_t count(UErrorCode &/*status*/) const {
1345 char *kw = keywords;
1346 int32_t result = 0;
1347 while(*kw) {
1348 result++;
1349 kw += uprv_strlen(kw)+1;
1350 }
1351 return result;
1352 }
1353
1354 virtual const char* next(int32_t* resultLength, UErrorCode& status) {
1355 const char* result;
1356 int32_t len;
1357 if(U_SUCCESS(status) && *current != 0) {
1358 result = current;
1359 len = (int32_t)uprv_strlen(current);
1360 current += len+1;
1361 if(resultLength != NULL) {
1362 *resultLength = len;
1363 }
1364 } else {
1365 if(resultLength != NULL) {
1366 *resultLength = 0;
1367 }
1368 result = NULL;
1369 }
1370 return result;
1371 }
1372
1373 virtual const UnicodeString* snext(UErrorCode& status) {
1374 int32_t resultLength = 0;
1375 const char *s = next(&resultLength, status);
1376 return setChars(s, resultLength, status);
1377 }
1378
1379 virtual void reset(UErrorCode& /*status*/) {
1380 current = keywords;
1381 }
1382};
1383
1384const char KeywordEnumeration::fgClassID = '\0';
1385
1386KeywordEnumeration::~KeywordEnumeration() {
1387 uprv_free(keywords);
1388}
1389
1390// A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in
1391// the next() method for each keyword before returning it.
1392class UnicodeKeywordEnumeration : public KeywordEnumeration {
1393public:
1394 using KeywordEnumeration::KeywordEnumeration;
1395 virtual ~UnicodeKeywordEnumeration();
1396
1397 virtual const char* next(int32_t* resultLength, UErrorCode& status) {
1398 const char* legacy_key = KeywordEnumeration::next(nullptr, status);
1399 if (U_SUCCESS(status) && legacy_key != nullptr) {
1400 const char* key = uloc_toUnicodeLocaleKey(legacy_key);
1401 if (key == nullptr) {
1402 status = U_ILLEGAL_ARGUMENT_ERROR;
1403 } else {
1404 if (resultLength != nullptr) {
1405 *resultLength = static_cast<int32_t>(uprv_strlen(key));
1406 }
1407 return key;
1408 }
1409 }
1410 if (resultLength != nullptr) *resultLength = 0;
1411 return nullptr;
1412 }
1413};
1414
1415// Out-of-line virtual destructor to serve as the "key function".
1416UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default;
1417
1418StringEnumeration *
1419Locale::createKeywords(UErrorCode &status) const
1420{
1421 char keywords[256];
1422 int32_t keywordCapacity = sizeof keywords;
1423 StringEnumeration *result = NULL;
1424
1425 if (U_FAILURE(status)) {
1426 return result;
1427 }
1428
1429 const char* variantStart = uprv_strchr(fullName, '@');
1430 const char* assignment = uprv_strchr(fullName, '=');
1431 if(variantStart) {
1432 if(assignment > variantStart) {
1433 int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, FALSE, &status);
1434 if(U_SUCCESS(status) && keyLen) {
1435 result = new KeywordEnumeration(keywords, keyLen, 0, status);
1436 if (!result) {
1437 status = U_MEMORY_ALLOCATION_ERROR;
1438 }
1439 }
1440 } else {
1441 status = U_INVALID_FORMAT_ERROR;
1442 }
1443 }
1444 return result;
1445}
1446
1447StringEnumeration *
1448Locale::createUnicodeKeywords(UErrorCode &status) const
1449{
1450 char keywords[256];
1451 int32_t keywordCapacity = sizeof keywords;
1452 StringEnumeration *result = NULL;
1453
1454 if (U_FAILURE(status)) {
1455 return result;
1456 }
1457
1458 const char* variantStart = uprv_strchr(fullName, '@');
1459 const char* assignment = uprv_strchr(fullName, '=');
1460 if(variantStart) {
1461 if(assignment > variantStart) {
1462 int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, FALSE, &status);
1463 if(U_SUCCESS(status) && keyLen) {
1464 result = new UnicodeKeywordEnumeration(keywords, keyLen, 0, status);
1465 if (!result) {
1466 status = U_MEMORY_ALLOCATION_ERROR;
1467 }
1468 }
1469 } else {
1470 status = U_INVALID_FORMAT_ERROR;
1471 }
1472 }
1473 return result;
1474}
1475
1476int32_t
1477Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const
1478{
1479 return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status);
1480}
1481
1482void
1483Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const {
1484 if (U_FAILURE(status)) {
1485 return;
1486 }
1487
1488 if (fIsBogus) {
1489 status = U_ILLEGAL_ARGUMENT_ERROR;
1490 return;
1491 }
1492
1493 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1494 const CharString keywordName_nul(keywordName, status);
1495 if (U_FAILURE(status)) {
1496 return;
1497 }
1498
1499 LocalMemory<char> scratch;
1500 int32_t scratch_capacity = 16; // Arbitrarily chosen default size.
1501
1502 char* buffer;
1503 int32_t result_capacity, reslen;
1504
1505 for (;;) {
1506 if (scratch.allocateInsteadAndReset(scratch_capacity) == nullptr) {
1507 status = U_MEMORY_ALLOCATION_ERROR;
1508 return;
1509 }
1510
1511 buffer = sink.GetAppendBuffer(
1512 /*min_capacity=*/scratch_capacity,
1513 /*desired_capacity_hint=*/scratch_capacity,
1514 scratch.getAlias(),
1515 scratch_capacity,
1516 &result_capacity);
1517
1518 reslen = uloc_getKeywordValue(
1519 fullName,
1520 keywordName_nul.data(),
1521 buffer,
1522 result_capacity,
1523 &status);
1524
1525 if (status != U_BUFFER_OVERFLOW_ERROR) {
1526 break;
1527 }
1528
1529 scratch_capacity = reslen;
1530 status = U_ZERO_ERROR;
1531 }
1532
1533 if (U_FAILURE(status)) {
1534 return;
1535 }
1536
1537 sink.Append(buffer, reslen);
1538 if (status == U_STRING_NOT_TERMINATED_WARNING) {
1539 status = U_ZERO_ERROR; // Terminators not used.
1540 }
1541}
1542
1543void
1544Locale::getUnicodeKeywordValue(StringPiece keywordName,
1545 ByteSink& sink,
1546 UErrorCode& status) const {
1547 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1548 const CharString keywordName_nul(keywordName, status);
1549 if (U_FAILURE(status)) {
1550 return;
1551 }
1552
1553 const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
1554
1555 if (legacy_key == nullptr) {
1556 status = U_ILLEGAL_ARGUMENT_ERROR;
1557 return;
1558 }
1559
1560 CharString legacy_value;
1561 {
1562 CharStringByteSink sink(&legacy_value);
1563 getKeywordValue(legacy_key, sink, status);
1564 }
1565
1566 if (U_FAILURE(status)) {
1567 return;
1568 }
1569
1570 const char* unicode_value = uloc_toUnicodeLocaleType(
1571 keywordName_nul.data(), legacy_value.data());
1572
1573 if (unicode_value == nullptr) {
1574 status = U_ILLEGAL_ARGUMENT_ERROR;
1575 return;
1576 }
1577
1578 sink.Append(unicode_value, static_cast<int32_t>(uprv_strlen(unicode_value)));
1579}
1580
1581void
1582Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)
1583{
1584 if (U_FAILURE(status)) {
1585 return;
1586 }
1587 int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY);
1588 int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName,
1589 bufferLength, &status) + 1;
1590 /* Handle the case the current buffer is not enough to hold the new id */
1591 if (status == U_BUFFER_OVERFLOW_ERROR) {
1592 U_ASSERT(newLength > bufferLength);
1593 char* newFullName = (char *)uprv_malloc(newLength);
1594 if (newFullName == nullptr) {
1595 status = U_MEMORY_ALLOCATION_ERROR;
1596 return;
1597 }
1598 uprv_strcpy(newFullName, fullName);
1599 if (fullName != fullNameBuffer) {
1600 // if full Name is already on the heap, need to free it.
1601 uprv_free(fullName);
1602 }
1603 fullName = newFullName;
1604 status = U_ZERO_ERROR;
1605 uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status);
1606 } else {
1607 U_ASSERT(newLength <= bufferLength);
1608 }
1609 if (U_SUCCESS(status) && baseName == fullName) {
1610 // May have added the first keyword, meaning that the fullName is no longer also the baseName.
1611 initBaseName(status);
1612 }
1613}
1614
1615void
1616Locale::setKeywordValue(StringPiece keywordName,
1617 StringPiece keywordValue,
1618 UErrorCode& status) {
1619 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1620 const CharString keywordName_nul(keywordName, status);
1621 const CharString keywordValue_nul(keywordValue, status);
1622 setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status);
1623}
1624
1625void
1626Locale::setUnicodeKeywordValue(StringPiece keywordName,
1627 StringPiece keywordValue,
1628 UErrorCode& status) {
1629 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1630 const CharString keywordName_nul(keywordName, status);
1631 const CharString keywordValue_nul(keywordValue, status);
1632
1633 if (U_FAILURE(status)) {
1634 return;
1635 }
1636
1637 const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
1638
1639 if (legacy_key == nullptr) {
1640 status = U_ILLEGAL_ARGUMENT_ERROR;
1641 return;
1642 }
1643
1644 const char* legacy_value = nullptr;
1645
1646 if (!keywordValue_nul.isEmpty()) {
1647 legacy_value =
1648 uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data());
1649
1650 if (legacy_value == nullptr) {
1651 status = U_ILLEGAL_ARGUMENT_ERROR;
1652 return;
1653 }
1654 }
1655
1656 setKeywordValue(legacy_key, legacy_value, status);
1657}
1658
1659const char *
1660Locale::getBaseName() const {
1661 return baseName;
1662}
1663
1664Locale::Iterator::~Iterator() = default;
1665
1666//eof
1667U_NAMESPACE_END
1668