* Copyright 2022, Trung Nguyen, trungnt282910@gmail.com
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <new>
#include <errno.h>
#include <locale.h>
#include <strings.h>
#include <ErrnoMaintainer.h>
#include "LocaleBackend.h"
#include "LocaleInternal.h"
using BPrivate::Libroot::gGlobalLocaleBackend;
using BPrivate::Libroot::gGlobalLocaleDataBridge;
using BPrivate::Libroot::GetCurrentLocaleInfo;
using BPrivate::Libroot::GetLocalesFromEnvironment;
using BPrivate::Libroot::LocaleBackend;
using BPrivate::Libroot::LocaleBackendData;
using BPrivate::Libroot::LocaleDataBridge;
extern "C" locale_t
duplocale(locale_t l)
{
LocaleBackendData* locObj = (LocaleBackendData*)l;
LocaleBackendData* newObj = new (std::nothrow) LocaleBackendData;
if (newObj == NULL) {
errno = ENOMEM;
return (locale_t)0;
}
newObj->magic = LOCALE_T_MAGIC;
newObj->backend = NULL;
newObj->databridge = NULL;
LocaleBackend* backend = (l == LC_GLOBAL_LOCALE) ?
gGlobalLocaleBackend : (LocaleBackend*)locObj->backend;
if (backend == NULL)
return (locale_t)newObj;
const char* localeDescription = backend->SetLocale(LC_ALL, NULL);
if ((strcasecmp(localeDescription, "POSIX") == 0)
|| (strcasecmp(localeDescription, "C") == 0)) {
return (locale_t)newObj;
}
LocaleBackend*& newBackend = newObj->backend;
LocaleDataBridge*& newDataBridge = newObj->databridge;
status_t status = LocaleBackend::CreateBackend(newBackend);
if (newBackend == NULL) {
errno = status;
delete newObj;
return (locale_t)0;
}
newDataBridge = new (std::nothrow) LocaleDataBridge(false);
if (newDataBridge == NULL) {
errno = ENOMEM;
LocaleBackend::DestroyBackend(newBackend);
delete newObj;
return (locale_t)0;
}
newBackend->Initialize(newDataBridge);
for (int lc = 1; lc <= LC_LAST; ++lc) {
newBackend->SetLocale(lc, backend->SetLocale(lc, NULL));
}
return (locale_t)newObj;
}
extern "C" void
freelocale(locale_t l)
{
LocaleBackendData* locobj = (LocaleBackendData*)l;
if (locobj->backend) {
LocaleBackend::DestroyBackend(locobj->backend);
LocaleDataBridge* databridge = locobj->databridge;
delete databridge;
}
delete locobj;
}
extern "C" locale_t
newlocale(int category_mask, const char* locale, locale_t base)
{
if (((category_mask | LC_ALL_MASK) != LC_ALL_MASK) || (locale == NULL)) {
errno = EINVAL;
return (locale_t)0;
}
bool newObject = false;
LocaleBackendData* localeObject = (LocaleBackendData*)base;
if (localeObject == NULL) {
localeObject = new (std::nothrow) LocaleBackendData;
if (localeObject == NULL) {
errno = ENOMEM;
return (locale_t)0;
}
localeObject->magic = LOCALE_T_MAGIC;
localeObject->backend = NULL;
localeObject->databridge = NULL;
newObject = true;
}
LocaleBackend*& backend = localeObject->backend;
LocaleDataBridge*& databridge = localeObject->databridge;
const char* locales[LC_LAST + 1];
for (int lc = 0; lc <= LC_LAST; lc++)
locales[lc] = NULL;
if (*locale == '\0') {
if (category_mask == LC_ALL_MASK) {
GetLocalesFromEnvironment(LC_ALL, locales);
} else {
for (int lc = 1; lc <= LC_LAST; ++lc) {
if (category_mask & (1 << (lc - 1)))
GetLocalesFromEnvironment(lc, locales);
}
}
} else {
if (category_mask == LC_ALL_MASK) {
locales[LC_ALL] = locale;
}
for (int lc = 1; lc <= LC_LAST; ++lc) {
if (category_mask & (1 << (lc - 1)))
locales[lc] = locale;
}
}
if (backend == NULL) {
bool needBackend = false;
for (int lc = 0; lc <= LC_LAST; lc++) {
if (locales[lc] != NULL && strcasecmp(locales[lc], "POSIX") != 0
&& strcasecmp(locales[lc], "C") != 0) {
needBackend = true;
break;
}
}
if (needBackend) {
status_t status = LocaleBackend::CreateBackend(backend);
if (backend == NULL) {
errno = status;
if (newObject) {
delete localeObject;
}
return (locale_t)0;
}
databridge = new (std::nothrow) LocaleDataBridge(false);
if (databridge == NULL) {
errno = ENOMEM;
LocaleBackend::DestroyBackend(backend);
if (newObject) {
delete localeObject;
}
return (locale_t)0;
}
backend->Initialize(databridge);
}
}
BPrivate::ErrnoMaintainer errnoMaintainer;
if (backend != NULL) {
for (int lc = 0; lc <= LC_LAST; lc++) {
if (locales[lc] != NULL) {
locale = backend->SetLocale(lc, locales[lc]);
if (lc == LC_ALL) {
break;
}
}
}
}
return (locale_t)localeObject;
}
extern "C" locale_t
uselocale(locale_t newLoc)
{
locale_t oldLoc = (locale_t)GetCurrentLocaleInfo();
if (oldLoc == NULL) {
oldLoc = LC_GLOBAL_LOCALE;
}
if (newLoc != (locale_t)0) {
locale_t appliedLoc = oldLoc;
if (newLoc == LC_GLOBAL_LOCALE) {
appliedLoc = NULL;
} else {
if (((LocaleBackendData*)newLoc)->magic != LOCALE_T_MAGIC) {
errno = EINVAL;
return (locale_t)0;
}
appliedLoc = newLoc;
}
SetCurrentLocaleInfo((LocaleBackendData*)appliedLoc);
if (appliedLoc != NULL) {
LocaleDataBridge*& databridge = ((LocaleBackendData*)appliedLoc)->databridge;
if (databridge == NULL) {
LocaleBackend*& backend = ((LocaleBackendData*)appliedLoc)->backend;
status_t status = LocaleBackend::CreateBackend(backend);
if (backend == NULL) {
if (status == B_MISSING_LIBRARY) {
return oldLoc;
}
errno = status;
return (locale_t)0;
}
databridge = new (std::nothrow) LocaleDataBridge(false);
if (databridge == NULL) {
LocaleBackend::DestroyBackend(backend);
errno = ENOMEM;
return (locale_t)0;
}
backend->Initialize(databridge);
}
databridge->ApplyToCurrentThread();
} else {
gGlobalLocaleDataBridge.ApplyToCurrentThread();
}
}
return oldLoc;
}
extern "C" const char*
getlocalename_l(int category, locale_t l)
{
LocaleBackend* backend = (l == LC_GLOBAL_LOCALE) ?
gGlobalLocaleBackend : (LocaleBackend*)((LocaleBackendData*)l)->backend;
if (backend == NULL)
return "POSIX";
return backend->SetLocale(category, NULL);
}
extern "C" locale_t
__current_locale_t()
{
locale_t locale = (locale_t)GetCurrentLocaleInfo();
if (locale == NULL) {
static LocaleBackendData global_locale_t;
global_locale_t.backend = gGlobalLocaleBackend;
global_locale_t.databridge = &gGlobalLocaleDataBridge;
return (locale_t)&global_locale_t;
}
return locale;
}
extern "C" locale_t
__posix_locale_t()
{
static LocaleBackendData posix_locale_t;
posix_locale_t.backend = NULL;
posix_locale_t.databridge = NULL;
return &posix_locale_t;
}