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
56 U_CDECL_BEGIN
57 static UBool U_CALLCONV locale_cleanup(void);
58 U_CDECL_END
59
60 U_NAMESPACE_BEGIN
61
62 static Locale *gLocaleCache = NULL;
63 static UInitOnce gLocaleCacheInitOnce = U_INITONCE_INITIALIZER;
64
65 // gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale.
66 static UMutex gDefaultLocaleMutex;
67 static UHashtable *gDefaultLocalesHashT = NULL;
68 static 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
76 U_NAMESPACE_END
77
78 typedef enum ELocalePos {
79 eENGLISH,
80 eFRENCH,
81 eGERMAN,
82 eITALIAN,
83 eJAPANESE,
84 eKOREAN,
85 eCHINESE,
86
87 eFRANCE,
88 eGERMANY,
89 eITALY,
90 eJAPAN,
91 eKOREA,
92 eCHINA, /* Alias for PRC */
93 eTAIWAN,
94 eUK,
95 eUS,
96 eCANADA,
97 eCANADA_FRENCH,
98 eROOT,
99
100
101 //eDEFAULT,
102 eMAX_LOCALES
103 } ELocalePos;
104
105 U_CFUNC int32_t locale_getKeywords(const char *localeID,
106 char prev,
107 char *keywords, int32_t keywordCapacity,
108 UBool valuesToo,
109 UErrorCode *status);
110
111 U_CDECL_BEGIN
112 //
113 // Deleter function for Locales owned by the default Locale hash table/
114 //
115 static void U_CALLCONV
deleteLocale(void * obj)116 deleteLocale(void *obj) {
117 delete (icu::Locale *) obj;
118 }
119
locale_cleanup(void)120 static 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
locale_init(UErrorCode & status)137 static 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
168 U_CDECL_END
169
170 U_NAMESPACE_BEGIN
171
locale_set_default_internal(const char * id,UErrorCode & status)172 Locale *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
227 U_NAMESPACE_END
228
229 /* sfb 07/21/99 */
230 U_CFUNC void
locale_set_default(const char * id)231 locale_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
239 U_CFUNC const char *
locale_get_default(void)240 locale_get_default(void)
241 {
242 U_NAMESPACE_USE
243 return Locale::getDefault().getName();
244 }
245
246
247 U_NAMESPACE_BEGIN
248
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)249 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)
250
251 /*Character separating the posix id fields*/
252 // '_'
253 // In the platform codepage.
254 #define SEP_CHAR '_'
255
256 Locale::~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
Locale()270 Locale::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 */
Locale(Locale::ELocaleType)281 Locale::Locale(Locale::ELocaleType)
282 : UObject(), fullName(fullNameBuffer), baseName(NULL)
283 {
284 setToBogus();
285 }
286
287
Locale(const char * newLanguage,const char * newCountry,const char * newVariant,const char * newKeywords)288 Locale::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
Locale(const Locale & other)425 Locale::Locale(const Locale &other)
426 : UObject(other), fullName(fullNameBuffer), baseName(NULL)
427 {
428 *this = other;
429 }
430
Locale(Locale && other)431 Locale::Locale(Locale&& other) U_NOEXCEPT
432 : UObject(other), fullName(fullNameBuffer), baseName(fullName) {
433 *this = std::move(other);
434 }
435
operator =(const Locale & other)436 Locale& 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
operator =(Locale && other)469 Locale& 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
498 Locale *
clone() const499 Locale::clone() const {
500 return new Locale(*this);
501 }
502
503 UBool
operator ==(const Locale & other) const504 Locale::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 namespace {
512
AppendLSCVE(CharString & out,const char * language,const char * script,const char * country,const char * variants,const char * extension,UErrorCode & status)513 CharString& AppendLSCVE(CharString& out, const char* language, const char* script,
514 const char* country, const char* variants, const char* extension,
515 UErrorCode& status) {
516 out.append(language, status);
517 if (script && script[0] != '\0') {
518 out.append('_', status);
519 out.append(script, status);
520 }
521 if (country && country[0] != '\0') {
522 out.append('_', status);
523 out.append(country, status);
524 }
525 if (variants && variants[0] != '\0') {
526 if ((script == nullptr || script[0] == '\0') &&
527 (country == nullptr || country[0] == '\0')) {
528 out.append('_', status);
529 }
530 out.append('_', status);
531 out.append(variants, status);
532 }
533 if (extension && extension[0] != '\0') {
534 out.append(extension, status);
535 }
536 return out;
537 }
538
539 } // namespace
540
541 /*This function initializes a Locale from a C locale ID*/
init(const char * localeID,UBool canonicalize)542 Locale& Locale::init(const char* localeID, UBool canonicalize)
543 {
544 fIsBogus = FALSE;
545 /* Free our current storage */
546 if (baseName != fullName) {
547 uprv_free(baseName);
548 }
549 baseName = NULL;
550 if(fullName != fullNameBuffer) {
551 uprv_free(fullName);
552 fullName = fullNameBuffer;
553 }
554
555 // not a loop:
556 // just an easy way to have a common error-exit
557 // without goto and without another function
558 do {
559 char *separator;
560 char *field[5] = {0};
561 int32_t fieldLen[5] = {0};
562 int32_t fieldIdx;
563 int32_t variantField;
564 int32_t length;
565 UErrorCode err;
566
567 if(localeID == NULL) {
568 // not an error, just set the default locale
569 return *this = getDefault();
570 }
571
572 /* preset all fields to empty */
573 language[0] = script[0] = country[0] = 0;
574
575 // "canonicalize" the locale ID to ICU/Java format
576 err = U_ZERO_ERROR;
577 length = canonicalize ?
578 uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) :
579 uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err);
580
581 if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) {
582 /*Go to heap for the fullName if necessary*/
583 fullName = (char *)uprv_malloc(sizeof(char)*(length + 1));
584 if(fullName == 0) {
585 fullName = fullNameBuffer;
586 break; // error: out of memory
587 }
588 err = U_ZERO_ERROR;
589 length = canonicalize ?
590 uloc_canonicalize(localeID, fullName, length+1, &err) :
591 uloc_getName(localeID, fullName, length+1, &err);
592 }
593 if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
594 /* should never occur */
595 break;
596 }
597
598 variantBegin = length;
599
600 /* after uloc_getName/canonicalize() we know that only '_' are separators */
601 /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */
602 separator = field[0] = fullName;
603 fieldIdx = 1;
604 char* at = uprv_strchr(fullName, '@');
605 while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) != 0 &&
606 fieldIdx < UPRV_LENGTHOF(field)-1 &&
607 (at == nullptr || separator < at)) {
608 field[fieldIdx] = separator + 1;
609 fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);
610 fieldIdx++;
611 }
612 // variant may contain @foo or .foo POSIX cruft; remove it
613 separator = uprv_strchr(field[fieldIdx-1], '@');
614 char* sep2 = uprv_strchr(field[fieldIdx-1], '.');
615 if (separator!=NULL || sep2!=NULL) {
616 if (separator==NULL || (sep2!=NULL && separator > sep2)) {
617 separator = sep2;
618 }
619 fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]);
620 } else {
621 fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName);
622 }
623
624 if (fieldLen[0] >= (int32_t)(sizeof(language)))
625 {
626 break; // error: the language field is too long
627 }
628
629 variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */
630 if (fieldLen[0] > 0) {
631 /* We have a language */
632 uprv_memcpy(language, fullName, fieldLen[0]);
633 language[fieldLen[0]] = 0;
634 }
635 if (fieldLen[1] == 4 && ISASCIIALPHA(field[1][0]) &&
636 ISASCIIALPHA(field[1][1]) && ISASCIIALPHA(field[1][2]) &&
637 ISASCIIALPHA(field[1][3])) {
638 /* We have at least a script */
639 uprv_memcpy(script, field[1], fieldLen[1]);
640 script[fieldLen[1]] = 0;
641 variantField++;
642 }
643
644 if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) {
645 /* We have a country */
646 uprv_memcpy(country, field[variantField], fieldLen[variantField]);
647 country[fieldLen[variantField]] = 0;
648 variantField++;
649 } else if (fieldLen[variantField] == 0) {
650 variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */
651 }
652
653 if (fieldLen[variantField] > 0) {
654 /* We have a variant */
655 variantBegin = (int32_t)(field[variantField] - fullName);
656 }
657
658 err = U_ZERO_ERROR;
659 initBaseName(err);
660 if (U_FAILURE(err)) {
661 break;
662 }
663
664 if (canonicalize) {
665 UErrorCode status = U_ZERO_ERROR;
666 // TODO: Try to use ResourceDataValue and ures_getValueWithFallback() etc.
667 LocalUResourceBundlePointer metadata(ures_openDirect(NULL, "metadata", &status));
668 LocalUResourceBundlePointer metadataAlias(ures_getByKey(metadata.getAlias(), "alias", NULL, &status));
669 // Look up the metadata:alias:language:$key:replacement entries
670 // key could be one of the following:
671 // language
672 // language_Script_REGION
673 // language_REGION
674 // language_variant
675 do {
676 // The resource structure looks like
677 // metadata {
678 // alias {
679 // language {
680 // art_lojban {
681 // replacement{"jbo"}
682 // }
683 // ...
684 // ks_Arab_IN {
685 // replacement{"ks_IN"}
686 // }
687 // ...
688 // no {
689 // replacement{"nb"}
690 // }
691 // ....
692 // zh_CN {
693 // replacement{"zh_Hans_CN"}
694 // }
695 // }
696 // ...
697 // }
698 // }
699 LocalUResourceBundlePointer languageAlias(ures_getByKey(metadataAlias.getAlias(), "language", NULL, &status));
700 if (U_FAILURE(status))
701 break;
702 CharString temp;
703 // Handle cases of key pattern "language _ variant"
704 // ex: Map "art_lojban" to "jbo"
705 const char* variants = getVariant();
706 if (variants != nullptr && variants[0] != '\0') {
707 const char* begin = variants;
708 const char* end = begin;
709 // We may have multiple variants, need to look at each of
710 // them.
711 do {
712 status = U_ZERO_ERROR;
713 end = uprv_strchr(begin, '_');
714 int32_t len = (end == nullptr) ? int32_t(uprv_strlen(begin)) : int32_t(end - begin);
715 temp.clear().append(getLanguage(), status).append("_", status).append(begin, len, status);
716 LocalUResourceBundlePointer languageVariantAlias(
717 ures_getByKey(languageAlias.getAlias(),
718 temp.data(),
719 NULL, &status));
720 temp.clear().appendInvariantChars(
721 UnicodeString(ures_getStringByKey(languageVariantAlias.getAlias(), "replacement", nullptr, &status)), status);
722 if (U_SUCCESS(status)) {
723 CharString newVar;
724 if (begin != variants) {
725 newVar.append(variants, static_cast<int32_t>(begin - variants - 1), status);
726 }
727 if (end != nullptr) {
728 if (begin != variants) {
729 newVar.append("_", status);
730 }
731 newVar.append(end + 1, status);
732 }
733 Locale l(temp.data());
734 init(AppendLSCVE(temp.clear(),
735 l.getLanguage(),
736 (getScript() != nullptr && getScript()[0] != '\0') ? getScript() : l.getScript(),
737 (getCountry() != nullptr && getCountry()[0] != '\0') ? getCountry() : l.getCountry(),
738 newVar.data(),
739 uprv_strchr(fullName, '@'), status).data(), false);
740 break;
741 }
742 begin = end + 1;
743 } while (end != nullptr);
744 } // End of handle language _ variant
745 // Handle cases of key pattern "language _ Script _ REGION"
746 // ex: Map "ks_Arab_IN" to "ks_IN"
747 if (getScript() != nullptr && getScript()[0] != '\0' &&
748 getCountry() != nullptr && getCountry()[0] != '\0') {
749 status = U_ZERO_ERROR;
750 LocalUResourceBundlePointer replacedAlias(
751 ures_getByKey(languageAlias.getAlias(),
752 AppendLSCVE(temp.clear(), getLanguage(), getScript(), getCountry(),
753 nullptr, nullptr, status).data(), NULL, &status));
754 temp.clear().appendInvariantChars(
755 UnicodeString(ures_getStringByKey(replacedAlias.getAlias(), "replacement", nullptr, &status)), status);
756 if (U_SUCCESS(status)) {
757 Locale l(temp.data());
758 init(AppendLSCVE(temp.clear(),
759 l.getLanguage(),
760 l.getScript(),
761 l.getCountry(),
762 getVariant(),
763 uprv_strchr(fullName, '@'), status).data(), false);
764 }
765 } // End of handle language _ Script _ REGION
766 // Handle cases of key pattern "language _ REGION"
767 // ex: Map "zh_CN" to "zh_Hans_CN"
768 if (getCountry() != nullptr && getCountry()[0] != '\0') {
769 status = U_ZERO_ERROR;
770 LocalUResourceBundlePointer replacedAlias(
771 ures_getByKey(languageAlias.getAlias(),
772 AppendLSCVE(temp.clear(), getLanguage(), nullptr, getCountry(),
773 nullptr, nullptr, status).data(), NULL, &status));
774 temp.clear().appendInvariantChars(
775 UnicodeString(ures_getStringByKey(replacedAlias.getAlias(), "replacement", nullptr, &status)), status);
776 if (U_SUCCESS(status)) {
777 Locale l(temp.data());
778 init(AppendLSCVE(temp.clear(),
779 l.getLanguage(),
780 (getScript() != nullptr && getScript()[0] != '\0') ? getScript() : l.getScript(),
781 l.getCountry(),
782 getVariant(),
783 uprv_strchr(fullName, '@'), status).data(), false);
784 }
785 } // End of handle "language _ REGION"
786 // Handle cases of key pattern "language"
787 // ex: Map "no" to "nb"
788 {
789 status = U_ZERO_ERROR;
790 LocalUResourceBundlePointer replaceLanguageAlias(ures_getByKey(languageAlias.getAlias(), getLanguage(), NULL, &status));
791 temp.clear().appendInvariantChars(
792 UnicodeString(ures_getStringByKey(replaceLanguageAlias.getAlias(), "replacement", nullptr, &status)), status);
793 if (U_SUCCESS(status)) {
794 Locale l(temp.data());
795 init(AppendLSCVE(temp.clear(),
796 l.getLanguage(),
797 (getScript() != nullptr && getScript()[0] != '\0') ? getScript() : l.getScript(),
798 (getCountry() != nullptr && getCountry()[0] != '\0') ? getCountry() : l.getCountry(),
799 getVariant(),
800 uprv_strchr(fullName, '@'), status).data(), false);
801 }
802 } // End of handle "language"
803
804 // Look up the metadata:alias:territory:$key:replacement entries
805 // key is region code.
806 if (getCountry() != nullptr) {
807 status = U_ZERO_ERROR;
808 // The resource structure looks like
809 // metadata {
810 // alias {
811 // ...
812 // territory: {
813 // 172 {
814 // replacement{"RU AM AZ BY GE KG KZ MD TJ TM UA UZ"}
815 // }
816 // ...
817 // 554 {
818 // replacement{"NZ"}
819 // }
820 // }
821 // }
822 // }
823 LocalUResourceBundlePointer territoryAlias(ures_getByKey(metadataAlias.getAlias(), "territory", NULL, &status));
824 LocalUResourceBundlePointer countryAlias(ures_getByKey(territoryAlias.getAlias(), getCountry(), NULL, &status));
825 UnicodeString replacements(
826 ures_getStringByKey(countryAlias.getAlias(), "replacement", nullptr, &status));
827 if (U_SUCCESS(status)) {
828 CharString replacedCountry;
829 int32_t delPos = replacements.indexOf(' ');
830 if (delPos == -1) {
831 replacedCountry.appendInvariantChars(replacements, status);
832 } else {
833 Locale l(AppendLSCVE(temp.clear(), getLanguage(), nullptr, getScript(),
834 nullptr, nullptr, status).data());
835 l.addLikelySubtags(status);
836 if (replacements.indexOf(UnicodeString(l.getCountry())) != -1) {
837 replacedCountry.append(l.getCountry(), status);
838 } else {
839 replacedCountry.appendInvariantChars(replacements.getBuffer(), delPos, status);
840 }
841 }
842 init(AppendLSCVE(temp.clear(),
843 getLanguage(),
844 getScript(),
845 replacedCountry.data(),
846 getVariant(),
847 uprv_strchr(fullName, '@'), status).data(), false);
848 }
849 } // End of handle REGION
850 } while (0);
851 } // if (canonicalize) {
852
853 // successful end of init()
854 return *this;
855 } while(0); /*loop doesn't iterate*/
856
857 // when an error occurs, then set this object to "bogus" (there is no UErrorCode here)
858 setToBogus();
859
860 return *this;
861 }
862
863 /*
864 * Set up the base name.
865 * If there are no key words, it's exactly the full name.
866 * If key words exist, it's the full name truncated at the '@' character.
867 * Need to set up both at init() and after setting a keyword.
868 */
869 void
initBaseName(UErrorCode & status)870 Locale::initBaseName(UErrorCode &status) {
871 if (U_FAILURE(status)) {
872 return;
873 }
874 U_ASSERT(baseName==NULL || baseName==fullName);
875 const char *atPtr = uprv_strchr(fullName, '@');
876 const char *eqPtr = uprv_strchr(fullName, '=');
877 if (atPtr && eqPtr && atPtr < eqPtr) {
878 // Key words exist.
879 int32_t baseNameLength = (int32_t)(atPtr - fullName);
880 baseName = (char *)uprv_malloc(baseNameLength + 1);
881 if (baseName == NULL) {
882 status = U_MEMORY_ALLOCATION_ERROR;
883 return;
884 }
885 uprv_strncpy(baseName, fullName, baseNameLength);
886 baseName[baseNameLength] = 0;
887
888 // The original computation of variantBegin leaves it equal to the length
889 // of fullName if there is no variant. It should instead be
890 // the length of the baseName.
891 if (variantBegin > baseNameLength) {
892 variantBegin = baseNameLength;
893 }
894 } else {
895 baseName = fullName;
896 }
897 }
898
899
900 int32_t
hashCode() const901 Locale::hashCode() const
902 {
903 return ustr_hashCharsN(fullName, static_cast<int32_t>(uprv_strlen(fullName)));
904 }
905
906 void
setToBogus()907 Locale::setToBogus() {
908 /* Free our current storage */
909 if(baseName != fullName) {
910 uprv_free(baseName);
911 }
912 baseName = NULL;
913 if(fullName != fullNameBuffer) {
914 uprv_free(fullName);
915 fullName = fullNameBuffer;
916 }
917 *fullNameBuffer = 0;
918 *language = 0;
919 *script = 0;
920 *country = 0;
921 fIsBogus = TRUE;
922 variantBegin = 0;
923 }
924
925 const Locale& U_EXPORT2
getDefault()926 Locale::getDefault()
927 {
928 {
929 Mutex lock(&gDefaultLocaleMutex);
930 if (gDefaultLocale != NULL) {
931 return *gDefaultLocale;
932 }
933 }
934 UErrorCode status = U_ZERO_ERROR;
935 return *locale_set_default_internal(NULL, status);
936 }
937
938
939
940 void U_EXPORT2
setDefault(const Locale & newLocale,UErrorCode & status)941 Locale::setDefault( const Locale& newLocale,
942 UErrorCode& status)
943 {
944 if (U_FAILURE(status)) {
945 return;
946 }
947
948 /* Set the default from the full name string of the supplied locale.
949 * This is a convenient way to access the default locale caching mechanisms.
950 */
951 const char *localeID = newLocale.getName();
952 locale_set_default_internal(localeID, status);
953 }
954
955 void
addLikelySubtags(UErrorCode & status)956 Locale::addLikelySubtags(UErrorCode& status) {
957 if (U_FAILURE(status)) {
958 return;
959 }
960
961 CharString maximizedLocaleID;
962 {
963 CharStringByteSink sink(&maximizedLocaleID);
964 ulocimp_addLikelySubtags(fullName, sink, &status);
965 }
966
967 if (U_FAILURE(status)) {
968 return;
969 }
970
971 init(maximizedLocaleID.data(), /*canonicalize=*/FALSE);
972 if (isBogus()) {
973 status = U_ILLEGAL_ARGUMENT_ERROR;
974 }
975 }
976
977 void
minimizeSubtags(UErrorCode & status)978 Locale::minimizeSubtags(UErrorCode& status) {
979 if (U_FAILURE(status)) {
980 return;
981 }
982
983 CharString minimizedLocaleID;
984 {
985 CharStringByteSink sink(&minimizedLocaleID);
986 ulocimp_minimizeSubtags(fullName, sink, &status);
987 }
988
989 if (U_FAILURE(status)) {
990 return;
991 }
992
993 init(minimizedLocaleID.data(), /*canonicalize=*/FALSE);
994 if (isBogus()) {
995 status = U_ILLEGAL_ARGUMENT_ERROR;
996 }
997 }
998
999 void
canonicalize(UErrorCode & status)1000 Locale::canonicalize(UErrorCode& status) {
1001 if (U_FAILURE(status)) {
1002 return;
1003 }
1004 if (isBogus()) {
1005 status = U_ILLEGAL_ARGUMENT_ERROR;
1006 return;
1007 }
1008 CharString uncanonicalized(fullName, status);
1009 if (U_FAILURE(status)) {
1010 return;
1011 }
1012 init(uncanonicalized.data(), /*canonicalize=*/TRUE);
1013 if (isBogus()) {
1014 status = U_ILLEGAL_ARGUMENT_ERROR;
1015 }
1016 }
1017
1018 Locale U_EXPORT2
forLanguageTag(StringPiece tag,UErrorCode & status)1019 Locale::forLanguageTag(StringPiece tag, UErrorCode& status)
1020 {
1021 Locale result(Locale::eBOGUS);
1022
1023 if (U_FAILURE(status)) {
1024 return result;
1025 }
1026
1027 // If a BCP-47 language tag is passed as the language parameter to the
1028 // normal Locale constructor, it will actually fall back to invoking
1029 // uloc_forLanguageTag() to parse it if it somehow is able to detect that
1030 // the string actually is BCP-47. This works well for things like strings
1031 // using BCP-47 extensions, but it does not at all work for things like
1032 // BCP-47 grandfathered tags (eg. "en-GB-oed") which are possible to also
1033 // interpret as ICU locale IDs and because of that won't trigger the BCP-47
1034 // parsing. Therefore the code here explicitly calls uloc_forLanguageTag()
1035 // and then Locale::init(), instead of just calling the normal constructor.
1036
1037 CharString localeID;
1038 int32_t parsedLength;
1039 {
1040 CharStringByteSink sink(&localeID);
1041 ulocimp_forLanguageTag(
1042 tag.data(),
1043 tag.length(),
1044 sink,
1045 &parsedLength,
1046 &status);
1047 }
1048
1049 if (U_FAILURE(status)) {
1050 return result;
1051 }
1052
1053 if (parsedLength != tag.size()) {
1054 status = U_ILLEGAL_ARGUMENT_ERROR;
1055 return result;
1056 }
1057
1058 result.init(localeID.data(), /*canonicalize=*/FALSE);
1059 if (result.isBogus()) {
1060 status = U_ILLEGAL_ARGUMENT_ERROR;
1061 }
1062 return result;
1063 }
1064
1065 void
toLanguageTag(ByteSink & sink,UErrorCode & status) const1066 Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const
1067 {
1068 if (U_FAILURE(status)) {
1069 return;
1070 }
1071
1072 if (fIsBogus) {
1073 status = U_ILLEGAL_ARGUMENT_ERROR;
1074 return;
1075 }
1076
1077 ulocimp_toLanguageTag(fullName, sink, /*strict=*/FALSE, &status);
1078 }
1079
1080 Locale U_EXPORT2
createFromName(const char * name)1081 Locale::createFromName (const char *name)
1082 {
1083 if (name) {
1084 Locale l("");
1085 l.init(name, FALSE);
1086 return l;
1087 }
1088 else {
1089 return getDefault();
1090 }
1091 }
1092
1093 Locale U_EXPORT2
createCanonical(const char * name)1094 Locale::createCanonical(const char* name) {
1095 Locale loc("");
1096 loc.init(name, TRUE);
1097 return loc;
1098 }
1099
1100 const char *
getISO3Language() const1101 Locale::getISO3Language() const
1102 {
1103 return uloc_getISO3Language(fullName);
1104 }
1105
1106
1107 const char *
getISO3Country() const1108 Locale::getISO3Country() const
1109 {
1110 return uloc_getISO3Country(fullName);
1111 }
1112
1113 /**
1114 * Return the LCID value as specified in the "LocaleID" resource for this
1115 * locale. The LocaleID must be expressed as a hexadecimal number, from
1116 * one to four digits. If the LocaleID resource is not present, or is
1117 * in an incorrect format, 0 is returned. The LocaleID is for use in
1118 * Windows (it is an LCID), but is available on all platforms.
1119 */
1120 uint32_t
getLCID() const1121 Locale::getLCID() const
1122 {
1123 return uloc_getLCID(fullName);
1124 }
1125
getISOCountries()1126 const char* const* U_EXPORT2 Locale::getISOCountries()
1127 {
1128 return uloc_getISOCountries();
1129 }
1130
getISOLanguages()1131 const char* const* U_EXPORT2 Locale::getISOLanguages()
1132 {
1133 return uloc_getISOLanguages();
1134 }
1135
1136 // Set the locale's data based on a posix id.
setFromPOSIXID(const char * posixID)1137 void Locale::setFromPOSIXID(const char *posixID)
1138 {
1139 init(posixID, TRUE);
1140 }
1141
1142 const Locale & U_EXPORT2
getRoot(void)1143 Locale::getRoot(void)
1144 {
1145 return getLocale(eROOT);
1146 }
1147
1148 const Locale & U_EXPORT2
getEnglish(void)1149 Locale::getEnglish(void)
1150 {
1151 return getLocale(eENGLISH);
1152 }
1153
1154 const Locale & U_EXPORT2
getFrench(void)1155 Locale::getFrench(void)
1156 {
1157 return getLocale(eFRENCH);
1158 }
1159
1160 const Locale & U_EXPORT2
getGerman(void)1161 Locale::getGerman(void)
1162 {
1163 return getLocale(eGERMAN);
1164 }
1165
1166 const Locale & U_EXPORT2
getItalian(void)1167 Locale::getItalian(void)
1168 {
1169 return getLocale(eITALIAN);
1170 }
1171
1172 const Locale & U_EXPORT2
getJapanese(void)1173 Locale::getJapanese(void)
1174 {
1175 return getLocale(eJAPANESE);
1176 }
1177
1178 const Locale & U_EXPORT2
getKorean(void)1179 Locale::getKorean(void)
1180 {
1181 return getLocale(eKOREAN);
1182 }
1183
1184 const Locale & U_EXPORT2
getChinese(void)1185 Locale::getChinese(void)
1186 {
1187 return getLocale(eCHINESE);
1188 }
1189
1190 const Locale & U_EXPORT2
getSimplifiedChinese(void)1191 Locale::getSimplifiedChinese(void)
1192 {
1193 return getLocale(eCHINA);
1194 }
1195
1196 const Locale & U_EXPORT2
getTraditionalChinese(void)1197 Locale::getTraditionalChinese(void)
1198 {
1199 return getLocale(eTAIWAN);
1200 }
1201
1202
1203 const Locale & U_EXPORT2
getFrance(void)1204 Locale::getFrance(void)
1205 {
1206 return getLocale(eFRANCE);
1207 }
1208
1209 const Locale & U_EXPORT2
getGermany(void)1210 Locale::getGermany(void)
1211 {
1212 return getLocale(eGERMANY);
1213 }
1214
1215 const Locale & U_EXPORT2
getItaly(void)1216 Locale::getItaly(void)
1217 {
1218 return getLocale(eITALY);
1219 }
1220
1221 const Locale & U_EXPORT2
getJapan(void)1222 Locale::getJapan(void)
1223 {
1224 return getLocale(eJAPAN);
1225 }
1226
1227 const Locale & U_EXPORT2
getKorea(void)1228 Locale::getKorea(void)
1229 {
1230 return getLocale(eKOREA);
1231 }
1232
1233 const Locale & U_EXPORT2
getChina(void)1234 Locale::getChina(void)
1235 {
1236 return getLocale(eCHINA);
1237 }
1238
1239 const Locale & U_EXPORT2
getPRC(void)1240 Locale::getPRC(void)
1241 {
1242 return getLocale(eCHINA);
1243 }
1244
1245 const Locale & U_EXPORT2
getTaiwan(void)1246 Locale::getTaiwan(void)
1247 {
1248 return getLocale(eTAIWAN);
1249 }
1250
1251 const Locale & U_EXPORT2
getUK(void)1252 Locale::getUK(void)
1253 {
1254 return getLocale(eUK);
1255 }
1256
1257 const Locale & U_EXPORT2
getUS(void)1258 Locale::getUS(void)
1259 {
1260 return getLocale(eUS);
1261 }
1262
1263 const Locale & U_EXPORT2
getCanada(void)1264 Locale::getCanada(void)
1265 {
1266 return getLocale(eCANADA);
1267 }
1268
1269 const Locale & U_EXPORT2
getCanadaFrench(void)1270 Locale::getCanadaFrench(void)
1271 {
1272 return getLocale(eCANADA_FRENCH);
1273 }
1274
1275 const Locale &
getLocale(int locid)1276 Locale::getLocale(int locid)
1277 {
1278 Locale *localeCache = getLocaleCache();
1279 U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0));
1280 if (localeCache == NULL) {
1281 // Failure allocating the locale cache.
1282 // The best we can do is return a NULL reference.
1283 locid = 0;
1284 }
1285 return localeCache[locid]; /*operating on NULL*/
1286 }
1287
1288 /*
1289 This function is defined this way in order to get around static
1290 initialization and static destruction.
1291 */
1292 Locale *
getLocaleCache(void)1293 Locale::getLocaleCache(void)
1294 {
1295 UErrorCode status = U_ZERO_ERROR;
1296 umtx_initOnce(gLocaleCacheInitOnce, locale_init, status);
1297 return gLocaleCache;
1298 }
1299
1300 class KeywordEnumeration : public StringEnumeration {
1301 private:
1302 char *keywords;
1303 char *current;
1304 int32_t length;
1305 UnicodeString currUSKey;
1306 static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */
1307
1308 public:
getStaticClassID(void)1309 static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; }
getDynamicClassID(void) const1310 virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); }
1311 public:
KeywordEnumeration(const char * keys,int32_t keywordLen,int32_t currentIndex,UErrorCode & status)1312 KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status)
1313 : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) {
1314 if(U_SUCCESS(status) && keywordLen != 0) {
1315 if(keys == NULL || keywordLen < 0) {
1316 status = U_ILLEGAL_ARGUMENT_ERROR;
1317 } else {
1318 keywords = (char *)uprv_malloc(keywordLen+1);
1319 if (keywords == NULL) {
1320 status = U_MEMORY_ALLOCATION_ERROR;
1321 }
1322 else {
1323 uprv_memcpy(keywords, keys, keywordLen);
1324 keywords[keywordLen] = 0;
1325 current = keywords + currentIndex;
1326 length = keywordLen;
1327 }
1328 }
1329 }
1330 }
1331
1332 virtual ~KeywordEnumeration();
1333
clone() const1334 virtual StringEnumeration * clone() const
1335 {
1336 UErrorCode status = U_ZERO_ERROR;
1337 return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status);
1338 }
1339
count(UErrorCode &) const1340 virtual int32_t count(UErrorCode &/*status*/) const {
1341 char *kw = keywords;
1342 int32_t result = 0;
1343 while(*kw) {
1344 result++;
1345 kw += uprv_strlen(kw)+1;
1346 }
1347 return result;
1348 }
1349
next(int32_t * resultLength,UErrorCode & status)1350 virtual const char* next(int32_t* resultLength, UErrorCode& status) {
1351 const char* result;
1352 int32_t len;
1353 if(U_SUCCESS(status) && *current != 0) {
1354 result = current;
1355 len = (int32_t)uprv_strlen(current);
1356 current += len+1;
1357 if(resultLength != NULL) {
1358 *resultLength = len;
1359 }
1360 } else {
1361 if(resultLength != NULL) {
1362 *resultLength = 0;
1363 }
1364 result = NULL;
1365 }
1366 return result;
1367 }
1368
snext(UErrorCode & status)1369 virtual const UnicodeString* snext(UErrorCode& status) {
1370 int32_t resultLength = 0;
1371 const char *s = next(&resultLength, status);
1372 return setChars(s, resultLength, status);
1373 }
1374
reset(UErrorCode &)1375 virtual void reset(UErrorCode& /*status*/) {
1376 current = keywords;
1377 }
1378 };
1379
1380 const char KeywordEnumeration::fgClassID = '\0';
1381
~KeywordEnumeration()1382 KeywordEnumeration::~KeywordEnumeration() {
1383 uprv_free(keywords);
1384 }
1385
1386 // A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in
1387 // the next() method for each keyword before returning it.
1388 class UnicodeKeywordEnumeration : public KeywordEnumeration {
1389 public:
1390 using KeywordEnumeration::KeywordEnumeration;
1391 virtual ~UnicodeKeywordEnumeration();
1392
next(int32_t * resultLength,UErrorCode & status)1393 virtual const char* next(int32_t* resultLength, UErrorCode& status) {
1394 const char* legacy_key = KeywordEnumeration::next(nullptr, status);
1395 if (U_SUCCESS(status) && legacy_key != nullptr) {
1396 const char* key = uloc_toUnicodeLocaleKey(legacy_key);
1397 if (key == nullptr) {
1398 status = U_ILLEGAL_ARGUMENT_ERROR;
1399 } else {
1400 if (resultLength != nullptr) {
1401 *resultLength = static_cast<int32_t>(uprv_strlen(key));
1402 }
1403 return key;
1404 }
1405 }
1406 if (resultLength != nullptr) *resultLength = 0;
1407 return nullptr;
1408 }
1409 };
1410
1411 // Out-of-line virtual destructor to serve as the "key function".
1412 UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default;
1413
1414 StringEnumeration *
createKeywords(UErrorCode & status) const1415 Locale::createKeywords(UErrorCode &status) const
1416 {
1417 char keywords[256];
1418 int32_t keywordCapacity = sizeof keywords;
1419 StringEnumeration *result = NULL;
1420
1421 if (U_FAILURE(status)) {
1422 return result;
1423 }
1424
1425 const char* variantStart = uprv_strchr(fullName, '@');
1426 const char* assignment = uprv_strchr(fullName, '=');
1427 if(variantStart) {
1428 if(assignment > variantStart) {
1429 int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, FALSE, &status);
1430 if(U_SUCCESS(status) && keyLen) {
1431 result = new KeywordEnumeration(keywords, keyLen, 0, status);
1432 if (!result) {
1433 status = U_MEMORY_ALLOCATION_ERROR;
1434 }
1435 }
1436 } else {
1437 status = U_INVALID_FORMAT_ERROR;
1438 }
1439 }
1440 return result;
1441 }
1442
1443 StringEnumeration *
createUnicodeKeywords(UErrorCode & status) const1444 Locale::createUnicodeKeywords(UErrorCode &status) const
1445 {
1446 char keywords[256];
1447 int32_t keywordCapacity = sizeof keywords;
1448 StringEnumeration *result = NULL;
1449
1450 if (U_FAILURE(status)) {
1451 return result;
1452 }
1453
1454 const char* variantStart = uprv_strchr(fullName, '@');
1455 const char* assignment = uprv_strchr(fullName, '=');
1456 if(variantStart) {
1457 if(assignment > variantStart) {
1458 int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, FALSE, &status);
1459 if(U_SUCCESS(status) && keyLen) {
1460 result = new UnicodeKeywordEnumeration(keywords, keyLen, 0, status);
1461 if (!result) {
1462 status = U_MEMORY_ALLOCATION_ERROR;
1463 }
1464 }
1465 } else {
1466 status = U_INVALID_FORMAT_ERROR;
1467 }
1468 }
1469 return result;
1470 }
1471
1472 int32_t
getKeywordValue(const char * keywordName,char * buffer,int32_t bufLen,UErrorCode & status) const1473 Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const
1474 {
1475 return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status);
1476 }
1477
1478 void
getKeywordValue(StringPiece keywordName,ByteSink & sink,UErrorCode & status) const1479 Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const {
1480 if (U_FAILURE(status)) {
1481 return;
1482 }
1483
1484 if (fIsBogus) {
1485 status = U_ILLEGAL_ARGUMENT_ERROR;
1486 return;
1487 }
1488
1489 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1490 const CharString keywordName_nul(keywordName, status);
1491 if (U_FAILURE(status)) {
1492 return;
1493 }
1494
1495 LocalMemory<char> scratch;
1496 int32_t scratch_capacity = 16; // Arbitrarily chosen default size.
1497
1498 char* buffer;
1499 int32_t result_capacity, reslen;
1500
1501 for (;;) {
1502 if (scratch.allocateInsteadAndReset(scratch_capacity) == nullptr) {
1503 status = U_MEMORY_ALLOCATION_ERROR;
1504 return;
1505 }
1506
1507 buffer = sink.GetAppendBuffer(
1508 /*min_capacity=*/scratch_capacity,
1509 /*desired_capacity_hint=*/scratch_capacity,
1510 scratch.getAlias(),
1511 scratch_capacity,
1512 &result_capacity);
1513
1514 reslen = uloc_getKeywordValue(
1515 fullName,
1516 keywordName_nul.data(),
1517 buffer,
1518 result_capacity,
1519 &status);
1520
1521 if (status != U_BUFFER_OVERFLOW_ERROR) {
1522 break;
1523 }
1524
1525 scratch_capacity = reslen;
1526 status = U_ZERO_ERROR;
1527 }
1528
1529 if (U_FAILURE(status)) {
1530 return;
1531 }
1532
1533 sink.Append(buffer, reslen);
1534 if (status == U_STRING_NOT_TERMINATED_WARNING) {
1535 status = U_ZERO_ERROR; // Terminators not used.
1536 }
1537 }
1538
1539 void
getUnicodeKeywordValue(StringPiece keywordName,ByteSink & sink,UErrorCode & status) const1540 Locale::getUnicodeKeywordValue(StringPiece keywordName,
1541 ByteSink& sink,
1542 UErrorCode& status) const {
1543 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1544 const CharString keywordName_nul(keywordName, status);
1545 if (U_FAILURE(status)) {
1546 return;
1547 }
1548
1549 const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
1550
1551 if (legacy_key == nullptr) {
1552 status = U_ILLEGAL_ARGUMENT_ERROR;
1553 return;
1554 }
1555
1556 CharString legacy_value;
1557 {
1558 CharStringByteSink sink(&legacy_value);
1559 getKeywordValue(legacy_key, sink, status);
1560 }
1561
1562 if (U_FAILURE(status)) {
1563 return;
1564 }
1565
1566 const char* unicode_value = uloc_toUnicodeLocaleType(
1567 keywordName_nul.data(), legacy_value.data());
1568
1569 if (unicode_value == nullptr) {
1570 status = U_ILLEGAL_ARGUMENT_ERROR;
1571 return;
1572 }
1573
1574 sink.Append(unicode_value, static_cast<int32_t>(uprv_strlen(unicode_value)));
1575 }
1576
1577 void
setKeywordValue(const char * keywordName,const char * keywordValue,UErrorCode & status)1578 Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)
1579 {
1580 if (U_FAILURE(status)) {
1581 return;
1582 }
1583 int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY);
1584 int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName,
1585 bufferLength, &status) + 1;
1586 /* Handle the case the current buffer is not enough to hold the new id */
1587 if (status == U_BUFFER_OVERFLOW_ERROR) {
1588 U_ASSERT(newLength > bufferLength);
1589 char* newFullName = (char *)uprv_malloc(newLength);
1590 if (newFullName == nullptr) {
1591 status = U_MEMORY_ALLOCATION_ERROR;
1592 return;
1593 }
1594 uprv_strcpy(newFullName, fullName);
1595 if (fullName != fullNameBuffer) {
1596 // if full Name is already on the heap, need to free it.
1597 uprv_free(fullName);
1598 }
1599 fullName = newFullName;
1600 status = U_ZERO_ERROR;
1601 uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status);
1602 } else {
1603 U_ASSERT(newLength <= bufferLength);
1604 }
1605 if (U_SUCCESS(status) && baseName == fullName) {
1606 // May have added the first keyword, meaning that the fullName is no longer also the baseName.
1607 initBaseName(status);
1608 }
1609 }
1610
1611 void
setKeywordValue(StringPiece keywordName,StringPiece keywordValue,UErrorCode & status)1612 Locale::setKeywordValue(StringPiece keywordName,
1613 StringPiece keywordValue,
1614 UErrorCode& status) {
1615 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1616 const CharString keywordName_nul(keywordName, status);
1617 const CharString keywordValue_nul(keywordValue, status);
1618 setKeywordValue(keywordName_nul.data(), keywordValue_nul.data(), status);
1619 }
1620
1621 void
setUnicodeKeywordValue(StringPiece keywordName,StringPiece keywordValue,UErrorCode & status)1622 Locale::setUnicodeKeywordValue(StringPiece keywordName,
1623 StringPiece keywordValue,
1624 UErrorCode& status) {
1625 // TODO: Remove the need for a const char* to a NUL terminated buffer.
1626 const CharString keywordName_nul(keywordName, status);
1627 const CharString keywordValue_nul(keywordValue, status);
1628
1629 if (U_FAILURE(status)) {
1630 return;
1631 }
1632
1633 const char* legacy_key = uloc_toLegacyKey(keywordName_nul.data());
1634
1635 if (legacy_key == nullptr) {
1636 status = U_ILLEGAL_ARGUMENT_ERROR;
1637 return;
1638 }
1639
1640 const char* legacy_value = nullptr;
1641
1642 if (!keywordValue_nul.isEmpty()) {
1643 legacy_value =
1644 uloc_toLegacyType(keywordName_nul.data(), keywordValue_nul.data());
1645
1646 if (legacy_value == nullptr) {
1647 status = U_ILLEGAL_ARGUMENT_ERROR;
1648 return;
1649 }
1650 }
1651
1652 setKeywordValue(legacy_key, legacy_value, status);
1653 }
1654
1655 const char *
getBaseName() const1656 Locale::getBaseName() const {
1657 return baseName;
1658 }
1659
1660 Locale::Iterator::~Iterator() = default;
1661
1662 //eof
1663 U_NAMESPACE_END
1664