⛏️ index : haiku.git

/*
 * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de.
 * Distributed under the terms of the MIT License.
 */


#include "ICULocaleBackend.h"

#include <new>

#include <langinfo.h>
#include <locale.h>
#include <string.h>
#include <strings.h>

#include <ErrnoMaintainer.h>


U_NAMESPACE_USE


namespace BPrivate {
namespace Libroot {


extern "C" LocaleBackend*
CreateInstance()
{
	return new(std::nothrow) ICULocaleBackend();
}


extern "C" void
DestroyInstance(LocaleBackend* instance)
{
	delete instance;
}


ICULocaleBackend::ICULocaleBackend()
	:
	fThreadLocalStorageKey(_CreateThreadLocalStorageKey()),
	fCollateData(fThreadLocalStorageKey),
	fCtypeData(fThreadLocalStorageKey),
	fMessagesData(fThreadLocalStorageKey),
	fMonetaryData(fThreadLocalStorageKey, fLocaleConv),
	fNumericData(fThreadLocalStorageKey, fLocaleConv),
	fTimeData(fThreadLocalStorageKey, fLCTimeInfo, fMessagesData),
	fTimeConversion(fTimeData)
{
}


ICULocaleBackend::~ICULocaleBackend()
{
	pthread_key_delete(fThreadLocalStorageKey);
}


void
ICULocaleBackend::Initialize(LocaleDataBridge* dataBridge)
{
	ErrnoMaintainer errnoMaintainer;

	fCtypeData.Initialize(&dataBridge->ctypeDataBridge);
	fMessagesData.Initialize(&dataBridge->messagesDataBridge);
	fMonetaryData.Initialize(&dataBridge->monetaryDataBridge);
	fNumericData.Initialize(&dataBridge->numericDataBridge);
	fTimeData.Initialize(&dataBridge->timeDataBridge);
	fTimeConversion.Initialize(&dataBridge->timeConversionDataBridge);

	fPosixLanginfo = dataBridge->posixLanginfo;

	_SetPosixLocale(LC_ALL);
}


const char*
ICULocaleBackend::SetLocale(int category, const char* posixLocaleName)
{
	ErrnoMaintainer errnoMaintainer;

	if (posixLocaleName == NULL)
		return _QueryLocale(category);

	if (strcasecmp(posixLocaleName, "POSIX") == 0
		|| strcasecmp(posixLocaleName, "C") == 0)
		return _SetPosixLocale(category);

	Locale locale = Locale::createCanonical(posixLocaleName);

	// Locale::create doesn't check validity, so make sure the locale
	// has a real ISO-3166 language code to validate that it actually exists.
	const char* iso3166 = locale.getISO3Language();
	if (iso3166 == NULL || iso3166[0] == '\0')
		return NULL;

	switch (category) {
		case LC_ALL:
			if (fCollateData.SetTo(locale, posixLocaleName) != B_OK
				|| fCtypeData.SetTo(locale, posixLocaleName) != B_OK
				|| fMessagesData.SetTo(locale, posixLocaleName) != B_OK
				|| fMonetaryData.SetTo(locale, posixLocaleName) != B_OK
				|| fNumericData.SetTo(locale, posixLocaleName) != B_OK
				|| fTimeData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		case LC_COLLATE:
			if (fCollateData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		case LC_CTYPE:
			if (fCtypeData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		case LC_MESSAGES:
			if (fMessagesData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		case LC_MONETARY:
			if (fMonetaryData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		case LC_NUMERIC:
			if (fNumericData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		case LC_TIME:
			if (fTimeData.SetTo(locale, posixLocaleName) != B_OK)
				return NULL;
			break;
		default:
			// unsupported category
			return NULL;
	}

	return posixLocaleName;
}


const struct lconv*
ICULocaleBackend::LocaleConv()
{
	return &fLocaleConv;
}


const struct lc_time_t*
ICULocaleBackend::LCTimeInfo()
{
	return &fLCTimeInfo;
}


int
ICULocaleBackend::IsWCType(wint_t wc, wctype_t charClass)
{
	ErrnoMaintainer errnoMaintainer;

	return fCtypeData.IsWCType(wc, charClass);
}


status_t
ICULocaleBackend::ToWCTrans(wint_t wc, wctrans_t transition, wint_t& result)
{
	ErrnoMaintainer errnoMaintainer;

	return fCtypeData.ToWCTrans(wc, transition, result);
}


status_t
ICULocaleBackend::MultibyteToWchar(wchar_t* wcOut, const char* mb,
	size_t mbLength, mbstate_t* mbState, size_t& lengthOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fCtypeData.MultibyteToWchar(wcOut, mb, mbLength, mbState, lengthOut);
}


status_t
ICULocaleBackend::MultibyteStringToWchar(wchar_t* wcDest, size_t wcDestLength,
	const char** mbSource, size_t mbSourceLength, mbstate_t* mbState,
	size_t& lengthOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fCtypeData.MultibyteStringToWchar(wcDest, wcDestLength, mbSource,
		mbSourceLength, mbState, lengthOut);
}


status_t
ICULocaleBackend::WcharToMultibyte(char* mbOut, wchar_t wc, mbstate_t* mbState,
	size_t& lengthOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fCtypeData.WcharToMultibyte(mbOut, wc, mbState, lengthOut);
}


status_t
ICULocaleBackend::WcharStringToMultibyte(char* mbDest, size_t mbDestLength,
	const wchar_t** wcSource, size_t wcSourceLength, mbstate_t* mbState,
	size_t& lengthOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fCtypeData.WcharStringToMultibyte(mbDest, mbDestLength, wcSource,
		wcSourceLength, mbState, lengthOut);
}


const char*
ICULocaleBackend::GetLanginfo(int index)
{
	ErrnoMaintainer errnoMaintainer;

	switch(index) {
		case CODESET:
			return fCtypeData.GetLanginfo(index);

		case D_T_FMT:
		case D_FMT:
		case T_FMT:
		case T_FMT_AMPM:
		case AM_STR:
		case PM_STR:
		case DAY_1:
		case DAY_2:
		case DAY_3:
		case DAY_4:
		case DAY_5:
		case DAY_6:
		case DAY_7:
		case ABDAY_1:
		case ABDAY_2:
		case ABDAY_3:
		case ABDAY_4:
		case ABDAY_5:
		case ABDAY_6:
		case ABDAY_7:
		case MON_1:
		case MON_2:
		case MON_3:
		case MON_4:
		case MON_5:
		case MON_6:
		case MON_7:
		case MON_8:
		case MON_9:
		case MON_10:
		case MON_11:
		case MON_12:
		case ABMON_1:
		case ABMON_2:
		case ABMON_3:
		case ABMON_4:
		case ABMON_5:
		case ABMON_6:
		case ABMON_7:
		case ABMON_8:
		case ABMON_9:
		case ABMON_10:
		case ABMON_11:
		case ABMON_12:
			return fTimeData.GetLanginfo(index);

		case ERA:
		case ERA_D_FMT:
		case ERA_D_T_FMT:
		case ERA_T_FMT:
		case ALT_DIGITS:
			return fPosixLanginfo[index];

		case RADIXCHAR:
		case THOUSEP:
			return fNumericData.GetLanginfo(index);

		case YESEXPR:
		case NOEXPR:
			return fMessagesData.GetLanginfo(index);

		case CRNCYSTR:
			return fMonetaryData.GetLanginfo(index);

		default:
			return "";
	}
}


status_t
ICULocaleBackend::Strcoll(const char* a, const char* b, int& result)
{
	ErrnoMaintainer errnoMaintainer;

	return fCollateData.Strcoll(a, b, result);
}


status_t
ICULocaleBackend::Strxfrm(char* out, const char* in,
	size_t outSize, size_t& requiredSize)
{
	ErrnoMaintainer errnoMaintainer;

	return fCollateData.Strxfrm(out, in, outSize, requiredSize);
}


status_t
ICULocaleBackend::Wcscoll(const wchar_t* a, const wchar_t* b, int& result)
{
	ErrnoMaintainer errnoMaintainer;

	return fCollateData.Wcscoll(a, b, result);
}


status_t
ICULocaleBackend::Wcsxfrm(wchar_t* out, const wchar_t* in, size_t outSize,
	size_t& requiredSize)
{
	ErrnoMaintainer errnoMaintainer;

	return fCollateData.Wcsxfrm(out, in, outSize, requiredSize);
}


status_t
ICULocaleBackend::TZSet(const char* timeZoneID, const char* tz)
{
	ErrnoMaintainer errnoMaintainer;

	return fTimeConversion.TZSet(timeZoneID, tz);
}


status_t
ICULocaleBackend::Localtime(const time_t* inTime, struct tm* tmOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fTimeConversion.Localtime(inTime, tmOut);
}


status_t
ICULocaleBackend::Gmtime(const time_t* inTime, struct tm* tmOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fTimeConversion.Gmtime(inTime, tmOut);
}


status_t
ICULocaleBackend::Mktime(struct tm* inOutTm, time_t& timeOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fTimeConversion.Mktime(inOutTm, timeOut);
}


status_t
ICULocaleBackend::Timegm(struct tm* inOutTm, time_t& timeOut)
{
	ErrnoMaintainer errnoMaintainer;

	return fTimeConversion.Timegm(inOutTm, timeOut);
}


const char*
ICULocaleBackend::_QueryLocale(int category)
{
	switch (category) {
		case LC_ALL:
		{
			// if all categories have the same locale, return that
			const char* locale = fCollateData.PosixLocaleName();
			if (strcmp(locale, fCtypeData.PosixLocaleName()) == 0
				&& strcmp(locale, fMessagesData.PosixLocaleName()) == 0
				&& strcmp(locale, fMonetaryData.PosixLocaleName()) == 0
				&& strcmp(locale, fNumericData.PosixLocaleName()) == 0
				&& strcmp(locale, fTimeData.PosixLocaleName()) == 0)
				return locale;

			// build a string with all info (at least glibc does it, too)
			snprintf(fLocaleDescription, sizeof(fLocaleDescription),
				"LC_COLLATE=%s;LC_CTYPE=%s;LC_MESSAGES=%s;LC_MONETARY=%s;"
				"LC_NUMERIC=%s;LC_TIME=%s",
				fCollateData.PosixLocaleName(), fCtypeData.PosixLocaleName(),
				fMessagesData.PosixLocaleName(),
				fMonetaryData.PosixLocaleName(), fNumericData.PosixLocaleName(),
				fTimeData.PosixLocaleName());
			return fLocaleDescription;
		}
		case LC_COLLATE:
			return fCollateData.PosixLocaleName();
		case LC_CTYPE:
			return fCtypeData.PosixLocaleName();
		case LC_MESSAGES:
			return fMessagesData.PosixLocaleName();
		case LC_MONETARY:
			return fMonetaryData.PosixLocaleName();
		case LC_NUMERIC:
			return fNumericData.PosixLocaleName();
		case LC_TIME:
			return fTimeData.PosixLocaleName();
		default:
			// unsupported category
			return NULL;
	}
}


const char*
ICULocaleBackend::_SetPosixLocale(int category)
{
	switch (category) {
		case LC_ALL:
			if (fCollateData.SetToPosix() != B_OK
				|| fCtypeData.SetToPosix() != B_OK
				|| fMessagesData.SetToPosix() != B_OK
				|| fMonetaryData.SetToPosix() != B_OK
				|| fNumericData.SetToPosix() != B_OK
				|| fTimeData.SetToPosix() != B_OK)
				return NULL;
			break;
		case LC_COLLATE:
			if (fCollateData.SetToPosix() != B_OK)
				return NULL;
			break;
		case LC_CTYPE:
			if (fCtypeData.SetToPosix() != B_OK)
				return NULL;
			break;
		case LC_MESSAGES:
			if (fMessagesData.SetToPosix() != B_OK)
				return NULL;
			break;
		case LC_MONETARY:
			if (fMonetaryData.SetToPosix() != B_OK)
				return NULL;
			break;
		case LC_NUMERIC:
			if (fNumericData.SetToPosix() != B_OK)
				return NULL;
			break;
		case LC_TIME:
			if (fTimeData.SetToPosix() != B_OK)
				return NULL;
			break;
		default:
			// unsupported category
			return NULL;
	}

	return "POSIX";
}


pthread_key_t
ICULocaleBackend::_CreateThreadLocalStorageKey()
{
	pthread_key_t key;

	pthread_key_create(&key, ICULocaleBackend::_DestroyThreadLocalStorageValue);

	return key;
}


void
ICULocaleBackend::_DestroyThreadLocalStorageValue(void* value)
{
	ICUThreadLocalStorageValue* tlsValue
		= static_cast<ICUThreadLocalStorageValue*>(value);

	delete tlsValue;
}


}	// namespace Libroot
}	// namespace BPrivate