ICU-20862 Fix setKeywordValue U_BUFFER_OVERFLOW_ERROR bug.

See #885
This commit is contained in:
Frank Tang 2019-10-17 22:42:14 +00:00 committed by Frank Yung-Fong Tang
parent 727505bdda
commit 596647c0c3
3 changed files with 95 additions and 1 deletions

View File

@ -1340,7 +1340,31 @@ Locale::getUnicodeKeywordValue(StringPiece keywordName,
void
Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status)
{
uloc_setKeywordValue(keywordName, keywordValue, fullName, ULOC_FULLNAME_CAPACITY, &status);
if (U_FAILURE(status)) {
return;
}
int32_t bufferLength = uprv_max((int32_t)(uprv_strlen(fullName) + 1), ULOC_FULLNAME_CAPACITY);
int32_t newLength = uloc_setKeywordValue(keywordName, keywordValue, fullName,
bufferLength, &status) + 1;
/* Handle the case the current buffer is not enough to hold the new id */
if (status == U_BUFFER_OVERFLOW_ERROR) {
U_ASSERT(newLength > bufferLength);
char* newFullName = (char *)uprv_malloc(newLength);
if (newFullName == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
uprv_strcpy(newFullName, fullName);
if (fullName != fullNameBuffer) {
// if full Name is already on the heap, need to free it.
uprv_free(fullName);
}
fullName = newFullName;
status = U_ZERO_ERROR;
uloc_setKeywordValue(keywordName, keywordValue, fullName, newLength, &status);
} else {
U_ASSERT(newLength <= bufferLength);
}
if (U_SUCCESS(status) && baseName == fullName) {
// May have added the first keyword, meaning that the fullName is no longer also the baseName.
initBaseName(status);

View File

@ -271,6 +271,8 @@ void LocaleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, c
TESTCASE_AUTO(TestPointerConvertingIterator);
TESTCASE_AUTO(TestTagConvertingIterator);
TESTCASE_AUTO(TestCapturingTagConvertingIterator);
TESTCASE_AUTO(TestSetUnicodeKeywordValueInLongLocale);
TESTCASE_AUTO(TestSetUnicodeKeywordValueNullInLongLocale);
TESTCASE_AUTO_END;
}
@ -3952,3 +3954,69 @@ void LocaleTest::TestCapturingTagConvertingIterator() {
assertFalse("3.hasNext()", iter.hasNext());
}
void LocaleTest::TestSetUnicodeKeywordValueInLongLocale() {
IcuTestErrorCode status(*this, "TestSetUnicodeKeywordValueInLongLocale");
const char* value = "efghijkl";
icu::Locale l("de");
char keyword[3];
CharString expected("de-u", status);
keyword[2] = '\0';
for (char i = 'a'; i < 's'; i++) {
keyword[0] = keyword[1] = i;
expected.append("-", status);
expected.append(keyword, status);
expected.append("-", status);
expected.append(value, status);
l.setUnicodeKeywordValue(keyword, value, status);
if (status.errIfFailureAndReset(
"setUnicodeKeywordValue(\"%s\", \"%s\") fail while locale is \"%s\"",
keyword, value, l.getName())) {
return;
}
std::string tag = l.toLanguageTag<std::string>(status);
if (status.errIfFailureAndReset(
"toLanguageTag fail on \"%s\"", l.getName())) {
return;
}
if (tag != expected.data()) {
errln("Expected to get \"%s\" bug got \"%s\"", tag.c_str(),
expected.data());
return;
}
}
}
void LocaleTest::TestSetUnicodeKeywordValueNullInLongLocale() {
IcuTestErrorCode status(*this, "TestSetUnicodeKeywordValueNullInLongLocale");
const char *exts[] = {"cf", "cu", "em", "kk", "kr", "ks", "kv", "lb", "lw",
"ms", "nu", "rg", "sd", "ss", "tz"};
for (int32_t i = 0; i < UPRV_LENGTHOF(exts); i++) {
CharString tag("de-u", status);
for (int32_t j = 0; j <= i; j++) {
tag.append("-", status).append(exts[j], status);
}
if (status.errIfFailureAndReset(
"Cannot create tag \"%s\"", tag.data())) {
continue;
}
Locale l = Locale::forLanguageTag(tag.data(), status);
if (status.errIfFailureAndReset(
"Locale::forLanguageTag(\"%s\") failed", tag.data())) {
continue;
}
for (int32_t j = 0; j <= i; j++) {
l.setUnicodeKeywordValue(exts[j], nullptr, status);
if (status.errIfFailureAndReset(
"Locale(\"%s\").setUnicodeKeywordValue(\"%s\", nullptr) failed",
tag.data(), exts[j])) {
continue;
}
}
if (strcmp("de", l.getName()) != 0) {
errln("setUnicodeKeywordValue should remove all extensions from "
"\"%s\" and only have \"de\", but is \"%s\" instead.",
tag.data(), l.getName());
}
}
}

View File

@ -145,6 +145,8 @@ public:
void TestPointerConvertingIterator();
void TestTagConvertingIterator();
void TestCapturingTagConvertingIterator();
void TestSetUnicodeKeywordValueInLongLocale();
void TestSetUnicodeKeywordValueNullInLongLocale();
private:
void _checklocs(const char* label,