]> granicus.if.org Git - icu/commitdiff
ICU-20862 Fix setKeywordValue U_BUFFER_OVERFLOW_ERROR bug.
authorFrank Tang <ftang@chromium.org>
Thu, 17 Oct 2019 22:42:14 +0000 (22:42 +0000)
committerFrank Yung-Fong Tang <41213225+FrankYFTang@users.noreply.github.com>
Fri, 18 Oct 2019 17:54:56 +0000 (10:54 -0700)
See #885

icu4c/source/common/locid.cpp
icu4c/source/test/intltest/loctest.cpp
icu4c/source/test/intltest/loctest.h

index c6d3f88fc3be0729b9b2fff172a3d8c3b85a1667..c6c976394def4d7f22d7e292d07c80525f443b79 100644 (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);
index 98612a1a37cd0720e93b9693cff1ca1d0f642589..73f1b73794a28255d0f8adad380b37397de007cd 100644 (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());
+        }
+    }
+}
index 065213e92275fd1a4837daa8db4d03d425eda124..b62cd26016951d4f2274bd0ad5e1dcdbef0784f6 100644 (file)
@@ -145,6 +145,8 @@ public:
     void TestPointerConvertingIterator();
     void TestTagConvertingIterator();
     void TestCapturingTagConvertingIterator();
+    void TestSetUnicodeKeywordValueInLongLocale();
+    void TestSetUnicodeKeywordValueNullInLongLocale();
 
 private:
     void _checklocs(const char* label,