michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "prmem.h" michael@0: #include "nsCollationUnix.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsILocaleService.h" michael@0: #include "nsIPlatformCharset.h" michael@0: #include "nsPosixLocale.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsCRT.h" michael@0: //#define DEBUG_UNIX_COLLATION michael@0: michael@0: inline void nsCollationUnix::DoSetLocale() michael@0: { michael@0: char *locale = setlocale(LC_COLLATE, nullptr); michael@0: mSavedLocale.Assign(locale ? locale : ""); michael@0: if (!mSavedLocale.EqualsIgnoreCase(mLocale.get())) { michael@0: (void) setlocale(LC_COLLATE, PromiseFlatCString(Substring(mLocale,0,MAX_LOCALE_LEN)).get()); michael@0: } michael@0: } michael@0: michael@0: inline void nsCollationUnix::DoRestoreLocale() michael@0: { michael@0: if (!mSavedLocale.EqualsIgnoreCase(mLocale.get())) { michael@0: (void) setlocale(LC_COLLATE, PromiseFlatCString(Substring(mSavedLocale,0,MAX_LOCALE_LEN)).get()); michael@0: } michael@0: } michael@0: michael@0: nsCollationUnix::nsCollationUnix() : mCollation(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsCollationUnix::~nsCollationUnix() michael@0: { michael@0: if (mCollation) michael@0: delete mCollation; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCollationUnix, nsICollation) michael@0: michael@0: nsresult nsCollationUnix::Initialize(nsILocale* locale) michael@0: { michael@0: #define kPlatformLocaleLength 64 michael@0: NS_ASSERTION(!mCollation, "Should only be initialized once"); michael@0: michael@0: nsresult res; michael@0: michael@0: mCollation = new nsCollation; michael@0: michael@0: // default platform locale michael@0: mLocale.Assign('C'); michael@0: michael@0: nsAutoString localeStr; michael@0: NS_NAMED_LITERAL_STRING(aCategory, "NSILOCALE_COLLATE##PLATFORM"); michael@0: michael@0: // get locale string, use app default if no locale specified michael@0: if (locale == nullptr) { michael@0: nsCOMPtr localeService = michael@0: do_GetService(NS_LOCALESERVICE_CONTRACTID, &res); michael@0: if (NS_SUCCEEDED(res)) { michael@0: nsCOMPtr appLocale; michael@0: res = localeService->GetApplicationLocale(getter_AddRefs(appLocale)); michael@0: if (NS_SUCCEEDED(res)) { michael@0: res = appLocale->GetCategory(aCategory, localeStr); michael@0: NS_ASSERTION(NS_SUCCEEDED(res), "failed to get app locale info"); michael@0: } michael@0: } michael@0: } michael@0: else { michael@0: res = locale->GetCategory(aCategory, localeStr); michael@0: NS_ASSERTION(NS_SUCCEEDED(res), "failed to get locale info"); michael@0: } michael@0: michael@0: // Get platform locale and charset name from locale, if available michael@0: if (NS_SUCCEEDED(res)) { michael@0: // keep the same behavior as 4.x as well as avoiding Linux collation key problem michael@0: if (localeStr.LowerCaseEqualsLiteral("en_us")) { // note: locale is in platform format michael@0: localeStr.AssignLiteral("C"); michael@0: } michael@0: michael@0: nsPosixLocale::GetPlatformLocale(localeStr, mLocale); michael@0: michael@0: nsCOMPtr platformCharset = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &res); michael@0: if (NS_SUCCEEDED(res)) { michael@0: nsAutoCString mappedCharset; michael@0: res = platformCharset->GetDefaultCharsetForLocale(localeStr, mappedCharset); michael@0: if (NS_SUCCEEDED(res)) { michael@0: mCollation->SetCharset(mappedCharset.get()); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult nsCollationUnix::CompareString(int32_t strength, michael@0: const nsAString& string1, michael@0: const nsAString& string2, michael@0: int32_t* result) michael@0: { michael@0: nsresult res = NS_OK; michael@0: michael@0: nsAutoString stringNormalized1, stringNormalized2; michael@0: if (strength != kCollationCaseSensitive) { michael@0: res = mCollation->NormalizeString(string1, stringNormalized1); michael@0: if (NS_FAILED(res)) { michael@0: return res; michael@0: } michael@0: res = mCollation->NormalizeString(string2, stringNormalized2); michael@0: if (NS_FAILED(res)) { michael@0: return res; michael@0: } michael@0: } else { michael@0: stringNormalized1 = string1; michael@0: stringNormalized2 = string2; michael@0: } michael@0: michael@0: // convert unicode to charset michael@0: char *str1, *str2; michael@0: michael@0: res = mCollation->UnicodeToChar(stringNormalized1, &str1); michael@0: if (NS_SUCCEEDED(res) && str1) { michael@0: res = mCollation->UnicodeToChar(stringNormalized2, &str2); michael@0: if (NS_SUCCEEDED(res) && str2) { michael@0: DoSetLocale(); michael@0: *result = strcoll(str1, str2); michael@0: DoRestoreLocale(); michael@0: PR_Free(str2); michael@0: } michael@0: PR_Free(str1); michael@0: } michael@0: michael@0: return res; michael@0: } michael@0: michael@0: michael@0: nsresult nsCollationUnix::AllocateRawSortKey(int32_t strength, michael@0: const nsAString& stringIn, michael@0: uint8_t** key, uint32_t* outLen) michael@0: { michael@0: nsresult res = NS_OK; michael@0: michael@0: nsAutoString stringNormalized; michael@0: if (strength != kCollationCaseSensitive) { michael@0: res = mCollation->NormalizeString(stringIn, stringNormalized); michael@0: if (NS_FAILED(res)) michael@0: return res; michael@0: } else { michael@0: stringNormalized = stringIn; michael@0: } michael@0: // convert unicode to charset michael@0: char *str; michael@0: michael@0: res = mCollation->UnicodeToChar(stringNormalized, &str); michael@0: if (NS_SUCCEEDED(res) && str) { michael@0: DoSetLocale(); michael@0: // call strxfrm to generate a key michael@0: size_t len = strxfrm(nullptr, str, 0) + 1; michael@0: void *buffer = PR_Malloc(len); michael@0: if (!buffer) { michael@0: res = NS_ERROR_OUT_OF_MEMORY; michael@0: } else if (strxfrm((char *)buffer, str, len) >= len) { michael@0: PR_Free(buffer); michael@0: res = NS_ERROR_FAILURE; michael@0: } else { michael@0: *key = (uint8_t *)buffer; michael@0: *outLen = len; michael@0: } michael@0: DoRestoreLocale(); michael@0: PR_Free(str); michael@0: } michael@0: michael@0: return res; michael@0: } michael@0: michael@0: nsresult nsCollationUnix::CompareRawSortKey(const uint8_t* key1, uint32_t len1, michael@0: const uint8_t* key2, uint32_t len2, michael@0: int32_t* result) michael@0: { michael@0: *result = PL_strcmp((const char *)key1, (const char *)key2); michael@0: return NS_OK; michael@0: }