AuroraRuntime/Source/Locale/LocaleGetLocale.cpp
Jamie Reece Wilson 26209ea066 [*] Prevent SysPanic/SysAsserts under Windows Vista and beta builds of Windows 7 during locale lookup. This shouldn't impact Windows XP. Perhaps Winserver 2003, beta 7, and other releases of older Windows could crash on start up.
Unrelated note, Windows XP does in fact have NLS support with these APIs; we just need to use "nlsdl.dll". Fortunately, we already have Windows XP paths, and that DLL is nothing more than a 24kb stub that calls GetLocaleInfoW. Something we already do ourselves.
2024-05-13 21:57:00 +01:00

356 lines
10 KiB
C++

/***
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: LocaleConvertWide.cpp
Date: 2022-9-15
File: Locale.cpp
Date: 2021-6-11
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Locale.hpp"
#if !defined(AU_NO_CPPLOCALE)
#include <locale>
#include <codecvt>
#endif
#include <wchar.h>
#include <tuple>
namespace Aurora::Locale
{
static bool gLockLocale = false;
static AuString gCountryCode;
static AuString gLanguageCode;
static AuString gCodeset;
static ECodePage gInternalCodePage = ECodePage::eEnumInvalid;
ECodePage GetInternalCodePage()
{
return gInternalCodePage;
}
AuString const &GetInternalCodePageString()
{
return gCodeset;
}
#if defined(AU_HAS_MSFT_NATIONALLANGSUPPORT)
static void SetCodesetCommonGuessWin32()
{
int acp = GetACP();
if (acp == CP_CHINESE)
{
gCodeset = "GB18030";
gInternalCodePage = ECodePage::e18030;
}
else if (acp == CP_UTF8)
{
gCodeset = "UTF-8";
gInternalCodePage = ECodePage::eUTF8;
}
else if (acp == CP_UTF_16)
{
gCodeset = "UTF-16";
gInternalCodePage = ECodePage::eUTF16;
}
else if (acp == CP_UTF_16 + 1)
{
gCodeset = "UTF-16";
gInternalCodePage = ECodePage::eUTF16BE;
}
else if (acp == CP_LATIN_1)
{
gCodeset = "Latin-1";
gInternalCodePage = ECodePage::eLatin1;
}
else if (acp == CP_2312_LIMITED_GBK)
{
gCodeset = "GBK";
gInternalCodePage = ECodePage::eGBK;
}
else if (acp == 437)
{
gCodeset = "IBM437";
gInternalCodePage = ECodePage::eSysUnk;
}
else if (acp == CP_SHIFTJIS)
{
gCodeset = "SJIS";
gInternalCodePage = ECodePage::eSJIS;
}
else
{
gCodeset = "MS-" + AuToString(acp);
gInternalCodePage = ECodePage::eSysUnk;
}
}
static void SetLanguageWin32()
{
int ret { ERROR_NOT_SUPPORTED };
wchar_t name[LOCALE_NAME_MAX_LENGTH] = { 0 };
wchar_t *pName { nullptr };
if (pLCIDToLocaleName)
{
if (AuSWInfo::IsWindows7OrGreater())
{
ret = pLCIDToLocaleName(LOCALE_USER_DEFAULT, name, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES);
}
else
{
ret = pLCIDToLocaleName(LOCALE_USER_DEFAULT, name, LOCALE_NAME_MAX_LENGTH, 0);
}
if (ret)
{
pName = name;
}
else
{
pName = nullptr; /* LOCALE_NAME_USER_DEFAULT. Windows XP < x <= Windows Vista/Beta 7 - detection failure? Windows server? */
}
#if 0
SysAssert(ret, "Couldn't acquire win32 locale information");
#endif
}
{
wchar_t language[LOCALE_NAME_MAX_LENGTH] = { 0 };
if (pGetLocaleInfoEx)
{
ret = pGetLocaleInfoEx(pName, LOCALE_SISO639LANGNAME, language, LOCALE_NAME_MAX_LENGTH);
SysAssert(ret, "Couldn't acquire win32 provided ISO 639 map of {}", ConvertFromWChar(name));
}
else if (pGetLocaleInfoW)
{
ret = pGetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, language, LOCALE_NAME_MAX_LENGTH);
SysAssert(ret, "Couldn't acquire win32 provided ISO 639 map of {}", ConvertFromWChar(name));
}
gLanguageCode = ConvertFromWChar(language);
}
{
wchar_t country[LOCALE_NAME_MAX_LENGTH] = { 0 };
if (pGetLocaleInfoEx)
{
ret = pGetLocaleInfoEx(pName, LOCALE_SISO3166CTRYNAME, country, LOCALE_NAME_MAX_LENGTH);
SysAssert(ret, "Couldn't acquire win32 provided ISO 3166 map of {}", ConvertFromWChar(name));
}
else if (pGetLocaleInfoW)
{
ret = pGetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, LOCALE_NAME_MAX_LENGTH);
SysAssert(ret, "Couldn't acquire win32 provided ISO 3166 map of {}", ConvertFromWChar(name));
}
gCountryCode = ConvertFromWChar(country);
}
SetCodesetCommonGuessWin32();
}
#elif defined(AURORA_IS_POSIX_DERIVED)
static AuHashMap<unsigned char, AuString> ParseLocaleString(const AuString &locale)
{
static auto isCharacterSplitter = [&](unsigned char ch) -> bool
{
static AuList<unsigned char> characterSplitters = { '.', '_', '@' };
for (auto const splitter:characterSplitters)
{
if (splitter == ch)
{
return true;
}
}
return false;
};
AuHashMap<unsigned char, AuString> parseTable;
AuMach startingIndex = 0;
unsigned char startingCharacter = '!';
for (AuMach i = 0; i < locale.size(); i++)
{
unsigned char curCh = locale[i];
if (!(isCharacterSplitter(curCh)))
{
continue;
}
parseTable.insert(AuMakePair(startingCharacter, locale.substr(startingIndex, i - startingIndex)));
startingIndex = i + 1;
startingCharacter = curCh;
}
parseTable.insert(AuMakePair(startingCharacter, locale.substr(startingIndex, locale.size() - startingIndex)));
return parseTable;
}
static void SetLanguageUnix()
{
#if 0
// this doesn't seem to work with libc++ lol?
auto locale = -std::--locale("").name();
#else
setlocale(LC_ALL, "");
AuString locale = setlocale(LC_ALL, NULL);
#endif
if (locale == "C")
{
AuLogWarn("Improperly configured UNIX environment.");
AuLogWarn("This localization detection code was written in 2020, please follow the `language[_territory][.codeset][@modifier]` convention for user/sys locales.");
AuLogWarn("'C' is not a language, country, or anything with which we can discern anything meaningful from. Fix your scuffed unix operating system and try again later...");
SysPanic("You fools");
}
auto parseTable = ParseLocaleString(locale);
AuString *lc;
if ((AuTryFind(parseTable, '!', lc)) && (lc->size()))
{
gLanguageCode = *lc;
}
else
{
AuLogWarn("Improperly configured UNIX environment.");
AuLogWarn("Couldn't discern language from localization string: {}", locale);
SysPanic("You fools");
}
AuString *cc;
if ((AuTryFind(parseTable, '_', cc)) && (cc->size()))
{
gCountryCode = *cc;
}
else
{
gCountryCode = "GB";
}
AuString *cs;
if ((AuTryFind(parseTable, '.', cs)) && (cs->size()))
{
gCodeset = *cs;
}
else
{
gCodeset = "UTF-8"; //also technically not true, but most UNIX/Linux applications expect UTF8 byte stirngs or UTF-32 wchar_t strings. this assumption shouldn't break anything
}
}
#define AURORA_HAS_UNIXLOCALE
#endif
#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_PLATFORM_LINUX) || defined(AURORA_PLATFORM_BSD)
static void SetLanguageEnvBlock()
{
const char *language;
if ((language = getenv("AURORA_ENV_LANGUAGE")))
{
gLanguageCode = language;
}
const char *countryCode;
if ((countryCode = getenv("AURORA_ENV_COUNTRY")))
{
gCountryCode = countryCode;
}
// You may not overload codeset on win32 targets
const char *codeSet;
if ((codeSet = getenv("AURORA_ENV_CODESET")))
{
gCodeset = codeSet;
}
}
#define AURORA_HAS_ENVBLOCK
#endif
static void GuessSystemECodePage()
{
if (gInternalCodePage != ECodePage::eEnumInvalid)
{
return;
}
if (gCodeset == "UTF-8")
{
gInternalCodePage = ECodePage::eUTF8;
}
else if (gCodeset == "UTF-16")
{
// TODO: is big endian
gInternalCodePage = ECodePage::eUTF16;
}
else if (gCodeset == "UTF-32")
{
// TODO: is big endian
gInternalCodePage = ECodePage::eUTF32;
}
else if (gCodeset == "SJIS")
{
gInternalCodePage = ECodePage::eSJIS;
}
// a history of chinese locales
else if (gCodeset == "GB18030") // is the new legally defined standard
{
gInternalCodePage = ECodePage::e18030;
}
else if (gCodeset == "GBK") // GB18030 is derived from GBK, GBK is drived from GB2312
{
gInternalCodePage = ECodePage::eGBK;
}
else if (gCodeset == "GB2312") // GBK is drived from GB2312, GB2312 is derived from telegraph shid
{
gInternalCodePage = ECodePage::e2312;
}
else
{
gInternalCodePage = ECodePage::eSysUnk;
}
}
void InitPlatformLocale()
{
#if defined(AU_HAS_MSFT_NATIONALLANGSUPPORT)
SetLanguageWin32();
#elif defined(AURORA_HAS_UNIXLOCALE)
SetLanguageUnix();
#endif
#if defined(AURORA_HAS_ENVBLOCK)
SetLanguageEnvBlock();
#endif
GuessSystemECodePage();
gLanguageCode = AuToLower(gLanguageCode);
gCountryCode = AuToUpper(gCountryCode);
gCodeset = gCodeset;
Encoding::InitIConv();
}
AUKN_SYM void RuntimeOverloadLocality(const AuPair<AuString, AuString> &locality)
{
SysAssert(!AuExchange(gLockLocale, true), "Locality has been locked");
gLanguageCode = AuToLower(locality.first);
gCountryCode = AuToUpper(locality.second);
}
AUKN_SYM LocalizationInfo GetLocale()
{
gLockLocale = true;
return LocalizationInfo(gLanguageCode, gCountryCode, gCodeset, gInternalCodePage);
}
}