/*
* Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
* Distributed under the terms of the MIT License.
*/
#include "ICUCollateData.h"
#include <string.h>
#include <strings.h>
#include <wchar.h>
#include <unicode/unistr.h>
#include <AutoDeleter.h>
namespace BPrivate {
namespace Libroot {
ICUCollateData::ICUCollateData(pthread_key_t tlsKey)
:
inherited(tlsKey),
fCollator(NULL)
{
}
ICUCollateData::~ICUCollateData()
{
delete fCollator;
}
status_t
ICUCollateData::SetTo(const Locale& locale, const char* posixLocaleName)
{
status_t result = inherited::SetTo(locale, posixLocaleName);
if (result == B_OK) {
UErrorCode icuStatus = U_ZERO_ERROR;
delete fCollator;
fCollator = Collator::createInstance(fLocale, icuStatus);
if (!U_SUCCESS(icuStatus))
return B_NO_MEMORY;
}
return result;
}
status_t
ICUCollateData::SetToPosix()
{
status_t result = inherited::SetToPosix();
if (result == B_OK) {
delete fCollator;
fCollator = NULL;
}
return result;
}
status_t
ICUCollateData::Strcoll(const char* a, const char* b, int& result)
{
if (fCollator == NULL || strcmp(fPosixLocaleName, "POSIX") == 0) {
// handle POSIX here as the collator ICU uses for that (english) is
// incompatible in too many ways
result = strcmp(a, b);
for (const char* aIter = a; *aIter != 0; ++aIter) {
if (*aIter < 0)
return B_BAD_VALUE;
}
for (const char* bIter = b; *bIter != 0; ++bIter) {
if (*bIter < 0)
return B_BAD_VALUE;
}
return B_OK;
}
status_t status = B_OK;
UErrorCode icuStatus = U_ZERO_ERROR;
if (strcasecmp(fGivenCharset, "utf-8") == 0) {
UCharIterator aIter, bIter;
uiter_setUTF8(&aIter, a, -1);
uiter_setUTF8(&bIter, b, -1);
result = fCollator->compare(aIter, bIter, icuStatus);
} else {
UnicodeString unicodeA;
UnicodeString unicodeB;
if (_ToUnicodeString(a, unicodeA) != B_OK
|| _ToUnicodeString(b, unicodeB) != B_OK) {
status = B_BAD_VALUE;
}
result = fCollator->compare(unicodeA, unicodeB, icuStatus);
}
if (!U_SUCCESS(icuStatus))
status = B_BAD_VALUE;
return status;
}
status_t
ICUCollateData::Strxfrm(char* out, const char* in, size_t size, size_t& outSize)
{
if (fCollator == NULL || strcmp(fPosixLocaleName, "POSIX") == 0) {
// handle POSIX here as the collator ICU uses for that (english) is
// incompatible in too many ways
outSize = strlcpy(out, in, size);
for (const char* inIter = in; *inIter != 0; ++inIter) {
if (*inIter < 0)
return B_BAD_VALUE;
}
return B_OK;
}
if (in == NULL) {
outSize = 0;
return B_OK;
}
UnicodeString unicodeIn;
if (_ToUnicodeString(in, unicodeIn) != B_OK)
return B_BAD_VALUE;
outSize = fCollator->getSortKey(unicodeIn, (uint8_t*)out, size);
return B_OK;
}
status_t
ICUCollateData::Wcscoll(const wchar_t* a, const wchar_t* b, int& result)
{
if (fCollator == NULL || strcmp(fPosixLocaleName, "POSIX") == 0) {
// handle POSIX here as the collator ICU uses for that (english) is
// incompatible in too many ways
result = wcscmp(a, b);
for (const wchar_t* aIter = a; *aIter != 0; ++aIter) {
if (*aIter > 127)
return B_BAD_VALUE;
}
for (const wchar_t* bIter = b; *bIter != 0; ++bIter) {
if (*bIter > 127)
return B_BAD_VALUE;
}
return B_OK;
}
UnicodeString unicodeA = UnicodeString::fromUTF32((UChar32*)a, -1);
UnicodeString unicodeB = UnicodeString::fromUTF32((UChar32*)b, -1);
UErrorCode icuStatus = U_ZERO_ERROR;
result = fCollator->compare(unicodeA, unicodeB, icuStatus);
if (!U_SUCCESS(icuStatus))
return B_BAD_VALUE;
return B_OK;
}
status_t
ICUCollateData::Wcsxfrm(wchar_t* out, const wchar_t* in, size_t size,
size_t& outSize)
{
if (in == NULL) {
outSize = 0;
return B_OK;
}
if (fCollator == NULL || strcmp(fPosixLocaleName, "POSIX") == 0) {
// handle POSIX here as the collator ICU uses for that (english) is
// incompatible in too many ways
outSize = wcslcpy(out, in, size);
for (const wchar_t* inIter = in; *inIter != 0; ++inIter) {
if (*inIter > 127)
return B_BAD_VALUE;
}
return B_OK;
}
UnicodeString unicodeIn = UnicodeString::fromUTF32((UChar32*)in, -1);
size_t requiredSize = fCollator->getSortKey(unicodeIn, NULL, 0);
uint8_t* buffer = (uint8_t*)out;
outSize = fCollator->getSortKey(unicodeIn, buffer, requiredSize);
// convert 1-byte characters to 4-byte wide characters:
for (size_t i = 0; i < outSize; ++i)
out[outSize - 1 - i] = buffer[outSize - 1 - i];
return B_OK;
}
status_t
ICUCollateData::_ToUnicodeString(const char* in, UnicodeString& out)
{
out.remove();
if (in == NULL)
return B_OK;
size_t inLen = strlen(in);
if (inLen == 0)
return B_OK;
UConverter* converter;
status_t result = _GetConverter(converter);
if (result != B_OK)
return result;
UErrorCode icuStatus = U_ZERO_ERROR;
int32_t outLen = ucnv_toUChars(converter, NULL, 0, in, inLen, &icuStatus);
if (icuStatus != U_BUFFER_OVERFLOW_ERROR)
return B_BAD_VALUE;
if (outLen < 0)
return B_ERROR;
if (outLen == 0)
return B_OK;
UChar* outBuf = out.getBuffer(outLen + 1);
icuStatus = U_ZERO_ERROR;
outLen
= ucnv_toUChars(converter, outBuf, outLen + 1, in, inLen, &icuStatus);
if (!U_SUCCESS(icuStatus)) {
out.releaseBuffer(0);
return B_BAD_VALUE;
}
out.releaseBuffer(outLen);
return B_OK;
}
} // namespace Libroot
} // namespace BPrivate
↑ V595 The 'in' pointer was utilized before it was verified against nullptr. Check lines: 119, 127.