blob: c67d31a8624a3aef1c321e43baa6477cb1a732bf [file] [log] [blame]
diff --git a/source/test/intltest/loctest.cpp b/source/test/intltest/loctest.cpp
index 1bcf771..5922e1c 100644
--- a/source/test/intltest/loctest.cpp
+++ b/source/test/intltest/loctest.cpp
@@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2014, International Business Machines Corporation and
+ * Copyright (c) 1997-2015, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@@ -181,6 +181,7 @@ LocaleTest::~LocaleTest()
void LocaleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
{
TESTCASE_AUTO_BEGIN;
+ TESTCASE_AUTO(TestBug11421); // Must run early in list to trigger failure.
TESTCASE_AUTO(TestBasicGetters);
TESTCASE_AUTO(TestSimpleResourceInfo);
TESTCASE_AUTO(TestDisplayNames);
@@ -1756,12 +1757,13 @@ LocaleTest::TestGetBaseName(void) {
} testCases[] = {
{ "de_DE@ C o ll A t i o n = Phonebook ", "de_DE" },
{ "de@currency = euro; CoLLaTion = PHONEBOOk", "de" },
- { "ja@calendar = buddhist", "ja" }
+ { "ja@calendar = buddhist", "ja" },
+ { "de-u-co-phonebk", "de"}
};
int32_t i = 0;
- for(i = 0; i < (int32_t)(sizeof(testCases)/sizeof(testCases[0])); i++) {
+ for(i = 0; i < UPRV_LENGTHOF(testCases); i++) {
Locale loc(testCases[i].localeID);
if(strcmp(testCases[i].baseName, loc.getBaseName())) {
errln("For locale \"%s\" expected baseName \"%s\", but got \"%s\"",
@@ -1769,6 +1771,20 @@ LocaleTest::TestGetBaseName(void) {
return;
}
}
+
+ // Verify that adding a keyword to an existing Locale doesn't change the base name.
+ UErrorCode status = U_ZERO_ERROR;
+ Locale loc2("en-US");
+ if (strcmp("en_US", loc2.getBaseName())) {
+ errln("%s:%d Expected \"en_US\", got \"%s\"", __FILE__, __LINE__, loc2.getBaseName());
+ }
+ loc2.setKeywordValue("key", "value", status);
+ if (strcmp("en_US@key=value", loc2.getName())) {
+ errln("%s:%d Expected \"en_US@key=value\", got \"%s\"", __FILE__, __LINE__, loc2.getName());
+ }
+ if (strcmp("en_US", loc2.getBaseName())) {
+ errln("%s:%d Expected \"en_US\", got \"%s\"", __FILE__, __LINE__, loc2.getBaseName());
+ }
}
/**
@@ -2549,3 +2565,17 @@ void LocaleTest::TestIsRightToLeft() {
assertFalse("fil LTR", Locale("fil").isRightToLeft());
assertFalse("he-Zyxw LTR", Locale("he-Zyxw").isRightToLeft());
}
+
+void LocaleTest::TestBug11421() {
+ Locale::getDefault().getBaseName();
+ int32_t numLocales;
+ const Locale *localeList = Locale::getAvailableLocales(numLocales);
+ for (int localeIndex = 0; localeIndex < numLocales; localeIndex++) {
+ const Locale &loc = localeList[localeIndex];
+ if (strncmp(loc.getName(), loc.getBaseName(), strlen(loc.getBaseName()))) {
+ errln("%s:%d loc.getName=\"%s\"; loc.getBaseName=\"%s\"",
+ __FILE__, __LINE__, loc.getName(), loc.getBaseName());
+ break;
+ }
+ }
+}
diff --git a/source/test/intltest/loctest.h b/source/test/intltest/loctest.h
index 53f606d..d556571 100644
--- a/source/test/intltest/loctest.h
+++ b/source/test/intltest/loctest.h
@@ -1,6 +1,6 @@
/********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2014, International Business Machines Corporation and
+ * Copyright (c) 1997-2015, International Business Machines Corporation and
* others. All Rights Reserved.
********************************************************************/
@@ -102,6 +102,7 @@ public:
void TestGetVariantWithKeywords(void);
void TestIsRightToLeft();
+ void TestBug11421();
private:
void _checklocs(const char* label,
diff --git a/source/common/locid.cpp b/source/common/locid.cpp
index f073aad..c99c61c 100644
--- a/source/common/locid.cpp
+++ b/source/common/locid.cpp
@@ -38,6 +38,7 @@
#include "uassert.h"
#include "cmemory.h"
#include "cstring.h"
+#include "uassert.h"
#include "uhash.h"
#include "ucln_cmn.h"
#include "ustr_imp.h"
@@ -240,16 +241,16 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)
Locale::~Locale()
{
+ if (baseName != fullName) {
+ uprv_free(baseName);
+ }
+ baseName = NULL;
/*if fullName is on the heap, we free it*/
if (fullName != fullNameBuffer)
{
uprv_free(fullName);
fullName = NULL;
}
- if (baseName && baseName != baseNameBuffer) {
- uprv_free(baseName);
- baseName = NULL;
- }
}
Locale::Locale()
@@ -421,6 +422,10 @@ Locale &Locale::operator=(const Locale &other)
}
/* Free our current storage */
+ if (baseName != fullName) {
+ uprv_free(baseName);
+ }
+ baseName = NULL;
if(fullName != fullNameBuffer) {
uprv_free(fullName);
fullName = fullNameBuffer;
@@ -436,18 +441,13 @@ Locale &Locale::operator=(const Locale &other)
/* Copy the full name */
uprv_strcpy(fullName, other.fullName);
- /* baseName is the cached result of getBaseName. if 'other' has a
- baseName and it fits in baseNameBuffer, then copy it. otherwise set
- it to NULL, and let the user lazy-create it (in getBaseName) if they
- want it. */
- if(baseName && baseName != baseNameBuffer) {
- uprv_free(baseName);
- }
- baseName = NULL;
-
- if(other.baseName == other.baseNameBuffer) {
- uprv_strcpy(baseNameBuffer, other.baseNameBuffer);
- baseName = baseNameBuffer;
+ /* Copy the baseName if it differs from fullName. */
+ if (other.baseName == other.fullName) {
+ baseName = fullName;
+ } else {
+ if (other.baseName) {
+ baseName = uprv_strdup(other.baseName);
+ }
}
/* Copy the language and country fields */
@@ -479,16 +479,15 @@ Locale& Locale::init(const char* localeID, UBool canonicalize)
{
fIsBogus = FALSE;
/* Free our current storage */
+ if (baseName != fullName) {
+ uprv_free(baseName);
+ }
+ baseName = NULL;
if(fullName != fullNameBuffer) {
uprv_free(fullName);
fullName = fullNameBuffer;
}
- if(baseName && baseName != baseNameBuffer) {
- uprv_free(baseName);
- baseName = NULL;
- }
-
// not a loop:
// just an easy way to have a common error-exit
// without goto and without another function
@@ -588,6 +587,12 @@ Locale& Locale::init(const char* localeID, UBool canonicalize)
variantBegin = (int32_t)(field[variantField] - fullName);
}
+ err = U_ZERO_ERROR;
+ initBaseName(err);
+ if (U_FAILURE(err)) {
+ break;
+ }
+
// successful end of init()
return *this;
} while(0); /*loop doesn't iterate*/
@@ -598,6 +603,43 @@ Locale& Locale::init(const char* localeID, UBool canonicalize)
return *this;
}
+/*
+ * Set up the base name.
+ * If there are no key words, it's exactly the full name.
+ * If key words exist, it's the full name truncated at the '@' character.
+ * Need to set up both at init() and after setting a keyword.
+ */
+void
+Locale::initBaseName(UErrorCode &status) {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ U_ASSERT(baseName==NULL || baseName==fullName);
+ const char *atPtr = uprv_strchr(fullName, '@');
+ const char *eqPtr = uprv_strchr(fullName, '=');
+ if (atPtr && eqPtr && atPtr < eqPtr) {
+ // Key words exist.
+ int32_t baseNameLength = (int32_t)(atPtr - fullName);
+ baseName = (char *)uprv_malloc(baseNameLength + 1);
+ if (baseName == NULL) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ return;
+ }
+ uprv_strncpy(baseName, fullName, baseNameLength);
+ baseName[baseNameLength] = 0;
+
+ // The original computation of variantBegin leaves it equal to the length
+ // of fullName if there is no variant. It should instead be
+ // the length of the baseName.
+ if (variantBegin > baseNameLength) {
+ variantBegin = baseNameLength;
+ }
+ } else {
+ baseName = fullName;
+ }
+}
+
+
int32_t
Locale::hashCode() const
{
@@ -607,14 +649,14 @@ Locale::hashCode() const
void
Locale::setToBogus() {
/* Free our current storage */
+ if(baseName != fullName) {
+ uprv_free(baseName);
+ }
+ baseName = NULL;
if(fullName != fullNameBuffer) {
uprv_free(fullName);
fullName = fullNameBuffer;
}
- if(baseName && baseName != baseNameBuffer) {
- uprv_free(baseName);
- baseName = NULL;
- }
*fullNameBuffer = 0;
*language = 0;
*script = 0;
@@ -990,33 +1032,14 @@ void
Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)
{
uloc_setKeywordValue(keywordName, keywordValue, fullName, ULOC_FULLNAME_CAPACITY, &status);
+ if (U_SUCCESS(status) && baseName == fullName) {
+ // May have added the first keyword, meaning that the fullName is no longer also the baseName.
+ initBaseName(status);
+ }
}
const char *
-Locale::getBaseName() const
-{
- // lazy init
- UErrorCode status = U_ZERO_ERROR;
- // semantically const
- if(baseName == 0) {
- ((Locale *)this)->baseName = ((Locale *)this)->baseNameBuffer;
- int32_t baseNameSize = uloc_getBaseName(fullName, baseName, ULOC_FULLNAME_CAPACITY, &status);
- if(baseNameSize >= ULOC_FULLNAME_CAPACITY) {
- ((Locale *)this)->baseName = (char *)uprv_malloc(sizeof(char) * baseNameSize + 1);
- if (baseName == NULL) {
- return baseName;
- }
- uloc_getBaseName(fullName, baseName, baseNameSize+1, &status);
- }
- baseName[baseNameSize] = 0;
-
- // the computation of variantBegin leaves it equal to the length
- // of fullName if there is no variant. It should instead be
- // the length of the baseName. Patch around this for now.
- if (variantBegin == (int32_t)uprv_strlen(fullName)) {
- ((Locale*)this)->variantBegin = baseNameSize;
- }
- }
+Locale::getBaseName() const {
return baseName;
}
diff --git a/source/common/unicode/locid.h b/source/common/unicode/locid.h
index 3546192..1ad5cb5 100644
--- a/source/common/unicode/locid.h
+++ b/source/common/unicode/locid.h
@@ -1,7 +1,7 @@
/*
******************************************************************************
*
-* Copyright (C) 1996-2014, International Business Machines
+* Copyright (C) 1996-2015, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
@@ -750,7 +750,7 @@ private:
char fullNameBuffer[ULOC_FULLNAME_CAPACITY];
// name without keywords
char* baseName;
- char baseNameBuffer[ULOC_FULLNAME_CAPACITY];
+ void initBaseName(UErrorCode& status);
UBool fIsBogus;
@@ -795,7 +795,6 @@ Locale::getScript() const
inline const char *
Locale::getVariant() const
{
- getBaseName(); // lazy init
return &baseName[variantBegin];
}