#include "unicode/bytestream.h"
#include "unicode/utypes.h"
#include "unicode/locid.h"
#include "unicode/putil.h"
#include "unicode/uchar.h"
#include "unicode/uloc.h"
#include "unicode/ures.h"
#include "unicode/uscript.h"
#include "bytesinkutil.h"
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
#include "ulocimp.h"
#include "ustr_imp.h"
static const char* const unknownLanguage = "und";
static const char* const unknownScript = "Zzzz";
static const char* const unknownRegion = "ZZ";
static const char* U_CALLCONV
findLikelySubtags(const char* localeID,
char* buffer,
int32_t bufferLength,
UErrorCode* err) {
const char* result = NULL;
if (!U_FAILURE(*err)) {
int32_t resLen = 0;
const UChar* s = NULL;
UErrorCode tmpErr = U_ZERO_ERROR;
icu::LocalUResourceBundlePointer subtags(ures_openDirect(NULL, "likelySubtags", &tmpErr));
if (U_SUCCESS(tmpErr)) {
icu::CharString und;
if (localeID != NULL) {
if (*localeID == '\0') {
localeID = unknownLanguage;
} else if (*localeID == '_') {
und.append(unknownLanguage, *err);
und.append(localeID, *err);
if (U_FAILURE(*err)) {
return NULL;
}
localeID = und.data();
}
}
s = ures_getStringByKey(subtags.getAlias(), localeID, &resLen, &tmpErr);
if (U_FAILURE(tmpErr)) {
if (tmpErr != U_MISSING_RESOURCE_ERROR) {
*err = tmpErr;
}
}
else if (resLen >= bufferLength) {
*err = U_INTERNAL_PROGRAM_ERROR;
}
else {
u_UCharsToChars(s, buffer, resLen + 1);
result = buffer;
}
} else {
*err = tmpErr;
}
}
return result;
}
static void U_CALLCONV
appendTag(
const char* tag,
int32_t tagLength,
char* buffer,
int32_t* bufferLength) {
if (*bufferLength > 0) {
buffer[*bufferLength] = '_';
++(*bufferLength);
}
uprv_memmove(
&buffer[*bufferLength],
tag,
tagLength);
*bufferLength += tagLength;
}
static void U_CALLCONV
createTagStringWithAlternates(
const char* lang,
int32_t langLength,
const char* script,
int32_t scriptLength,
const char* region,
int32_t regionLength,
const char* trailing,
int32_t trailingLength,
const char* alternateTags,
icu::ByteSink& sink,
UErrorCode* err) {
if (U_FAILURE(*err)) {
goto error;
}
else if (langLength >= ULOC_LANG_CAPACITY ||
scriptLength >= ULOC_SCRIPT_CAPACITY ||
regionLength >= ULOC_COUNTRY_CAPACITY) {
goto error;
}
else {
char tagBuffer[ULOC_FULLNAME_CAPACITY];
int32_t tagLength = 0;
UBool regionAppended = FALSE;
if (langLength > 0) {
appendTag(
lang,
langLength,
tagBuffer,
&tagLength);
}
else if (alternateTags == NULL) {
appendTag(
unknownLanguage,
(int32_t)uprv_strlen(unknownLanguage),
tagBuffer,
&tagLength);
}
else {
char alternateLang[ULOC_LANG_CAPACITY];
int32_t alternateLangLength = sizeof(alternateLang);
alternateLangLength =
uloc_getLanguage(
alternateTags,
alternateLang,
alternateLangLength,
err);
if(U_FAILURE(*err) ||
alternateLangLength >= ULOC_LANG_CAPACITY) {
goto error;
}
else if (alternateLangLength == 0) {
appendTag(
unknownLanguage,
(int32_t)uprv_strlen(unknownLanguage),
tagBuffer,
&tagLength);
}
else {
appendTag(
alternateLang,
alternateLangLength,
tagBuffer,
&tagLength);
}
}
if (scriptLength > 0) {
appendTag(
script,
scriptLength,
tagBuffer,
&tagLength);
}
else if (alternateTags != NULL) {
char alternateScript[ULOC_SCRIPT_CAPACITY];
const int32_t alternateScriptLength =
uloc_getScript(
alternateTags,
alternateScript,
sizeof(alternateScript),
err);
if (U_FAILURE(*err) ||
alternateScriptLength >= ULOC_SCRIPT_CAPACITY) {
goto error;
}
else if (alternateScriptLength > 0) {
appendTag(
alternateScript,
alternateScriptLength,
tagBuffer,
&tagLength);
}
}
if (regionLength > 0) {
appendTag(
region,
regionLength,
tagBuffer,
&tagLength);
regionAppended = TRUE;
}
else if (alternateTags != NULL) {
char alternateRegion[ULOC_COUNTRY_CAPACITY];
const int32_t alternateRegionLength =
uloc_getCountry(
alternateTags,
alternateRegion,
sizeof(alternateRegion),
err);
if (U_FAILURE(*err) ||
alternateRegionLength >= ULOC_COUNTRY_CAPACITY) {
goto error;
}
else if (alternateRegionLength > 0) {
appendTag(
alternateRegion,
alternateRegionLength,
tagBuffer,
&tagLength);
regionAppended = TRUE;
}
}
sink.Append(tagBuffer, tagLength);
if (trailingLength > 0) {
if (*trailing != '@') {
sink.Append("_", 1);
if (!regionAppended) {
sink.Append("_", 1);
}
}
sink.Append(trailing, trailingLength);
}
return;
}
error:
if (*err == U_BUFFER_OVERFLOW_ERROR ||
U_SUCCESS(*err)) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
}
static void U_CALLCONV
createTagString(
const char* lang,
int32_t langLength,
const char* script,
int32_t scriptLength,
const char* region,
int32_t regionLength,
const char* trailing,
int32_t trailingLength,
icu::ByteSink& sink,
UErrorCode* err)
{
createTagStringWithAlternates(
lang,
langLength,
script,
scriptLength,
region,
regionLength,
trailing,
trailingLength,
NULL,
sink,
err);
}
static int32_t U_CALLCONV
parseTagString(
const char* localeID,
char* lang,
int32_t* langLength,
char* script,
int32_t* scriptLength,
char* region,
int32_t* regionLength,
UErrorCode* err)
{
const char* position = localeID;
int32_t subtagLength = 0;
if(U_FAILURE(*err) ||
localeID == NULL ||
lang == NULL ||
langLength == NULL ||
script == NULL ||
scriptLength == NULL ||
region == NULL ||
regionLength == NULL) {
goto error;
}
subtagLength = ulocimp_getLanguage(position, lang, *langLength, &position);
u_terminateChars(lang, *langLength, subtagLength, err);
if(U_FAILURE(*err)) {
goto error;
}
*langLength = subtagLength;
if (*langLength == 0) {
uprv_strcpy(
lang,
unknownLanguage);
*langLength = (int32_t)uprv_strlen(lang);
}
if (_isIDSeparator(*position)) {
++position;
}
subtagLength = ulocimp_getScript(position, script, *scriptLength, &position);
u_terminateChars(script, *scriptLength, subtagLength, err);
if(U_FAILURE(*err)) {
goto error;
}
*scriptLength = subtagLength;
if (*scriptLength > 0) {
if (uprv_strnicmp(script, unknownScript, *scriptLength) == 0) {
*scriptLength = 0;
}
if (_isIDSeparator(*position)) {
++position;
}
}
subtagLength = ulocimp_getCountry(position, region, *regionLength, &position);
u_terminateChars(region, *regionLength, subtagLength, err);
if(U_FAILURE(*err)) {
goto error;
}
*regionLength = subtagLength;
if (*regionLength > 0) {
if (uprv_strnicmp(region, unknownRegion, *regionLength) == 0) {
*regionLength = 0;
}
} else if (*position != 0 && *position != '@') {
--position;
}
exit:
return (int32_t)(position - localeID);
error:
if (!U_FAILURE(*err)) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
goto exit;
}
static UBool U_CALLCONV
createLikelySubtagsString(
const char* lang,
int32_t langLength,
const char* script,
int32_t scriptLength,
const char* region,
int32_t regionLength,
const char* variants,
int32_t variantsLength,
icu::ByteSink& sink,
UErrorCode* err) {
char likelySubtagsBuffer[ULOC_FULLNAME_CAPACITY];
if(U_FAILURE(*err)) {
goto error;
}
if (scriptLength > 0 && regionLength > 0) {
const char* likelySubtags = NULL;
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createTagString(
lang,
langLength,
script,
scriptLength,
region,
regionLength,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
likelySubtags =
findLikelySubtags(
tagBuffer.data(),
likelySubtagsBuffer,
sizeof(likelySubtagsBuffer),
err);
if(U_FAILURE(*err)) {
goto error;
}
if (likelySubtags != NULL) {
createTagStringWithAlternates(
NULL,
0,
NULL,
0,
NULL,
0,
variants,
variantsLength,
likelySubtags,
sink,
err);
return TRUE;
}
}
if (scriptLength > 0) {
const char* likelySubtags = NULL;
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createTagString(
lang,
langLength,
script,
scriptLength,
NULL,
0,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
likelySubtags =
findLikelySubtags(
tagBuffer.data(),
likelySubtagsBuffer,
sizeof(likelySubtagsBuffer),
err);
if(U_FAILURE(*err)) {
goto error;
}
if (likelySubtags != NULL) {
createTagStringWithAlternates(
NULL,
0,
NULL,
0,
region,
regionLength,
variants,
variantsLength,
likelySubtags,
sink,
err);
return TRUE;
}
}
if (regionLength > 0) {
const char* likelySubtags = NULL;
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createTagString(
lang,
langLength,
NULL,
0,
region,
regionLength,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
likelySubtags =
findLikelySubtags(
tagBuffer.data(),
likelySubtagsBuffer,
sizeof(likelySubtagsBuffer),
err);
if(U_FAILURE(*err)) {
goto error;
}
if (likelySubtags != NULL) {
createTagStringWithAlternates(
NULL,
0,
script,
scriptLength,
NULL,
0,
variants,
variantsLength,
likelySubtags,
sink,
err);
return TRUE;
}
}
{
const char* likelySubtags = NULL;
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createTagString(
lang,
langLength,
NULL,
0,
NULL,
0,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
likelySubtags =
findLikelySubtags(
tagBuffer.data(),
likelySubtagsBuffer,
sizeof(likelySubtagsBuffer),
err);
if(U_FAILURE(*err)) {
goto error;
}
if (likelySubtags != NULL) {
createTagStringWithAlternates(
NULL,
0,
script,
scriptLength,
region,
regionLength,
variants,
variantsLength,
likelySubtags,
sink,
err);
return TRUE;
}
}
return FALSE;
error:
if (!U_FAILURE(*err)) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
return FALSE;
}
#define CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength) \
{ int32_t count = 0; \
int32_t i; \
for (i = 0; i < trailingLength; i++) { \
if (trailing[i] == '-' || trailing[i] == '_') { \
count = 0; \
if (count > 8) { \
goto error; \
} \
} else if (trailing[i] == '@') { \
break; \
} else if (count > 8) { \
goto error; \
} else { \
count++; \
} \
} \
}
static void
_uloc_addLikelySubtags(const char* localeID,
icu::ByteSink& sink,
UErrorCode* err) {
char lang[ULOC_LANG_CAPACITY];
int32_t langLength = sizeof(lang);
char script[ULOC_SCRIPT_CAPACITY];
int32_t scriptLength = sizeof(script);
char region[ULOC_COUNTRY_CAPACITY];
int32_t regionLength = sizeof(region);
const char* trailing = "";
int32_t trailingLength = 0;
int32_t trailingIndex = 0;
UBool success = FALSE;
if(U_FAILURE(*err)) {
goto error;
}
if (localeID == NULL) {
goto error;
}
trailingIndex = parseTagString(
localeID,
lang,
&langLength,
script,
&scriptLength,
region,
®ionLength,
err);
if(U_FAILURE(*err)) {
if (*err == U_BUFFER_OVERFLOW_ERROR) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
goto error;
}
while (_isIDSeparator(localeID[trailingIndex])) {
trailingIndex++;
}
trailing = &localeID[trailingIndex];
trailingLength = (int32_t)uprv_strlen(trailing);
CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength);
success =
createLikelySubtagsString(
lang,
langLength,
script,
scriptLength,
region,
regionLength,
trailing,
trailingLength,
sink,
err);
if (!success) {
const int32_t localIDLength = (int32_t)uprv_strlen(localeID);
sink.Append(localeID, localIDLength);
}
return;
error:
if (!U_FAILURE(*err)) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
}
static void
_uloc_minimizeSubtags(const char* localeID,
icu::ByteSink& sink,
UErrorCode* err) {
icu::CharString maximizedTagBuffer;
char lang[ULOC_LANG_CAPACITY];
int32_t langLength = sizeof(lang);
char script[ULOC_SCRIPT_CAPACITY];
int32_t scriptLength = sizeof(script);
char region[ULOC_COUNTRY_CAPACITY];
int32_t regionLength = sizeof(region);
const char* trailing = "";
int32_t trailingLength = 0;
int32_t trailingIndex = 0;
if(U_FAILURE(*err)) {
goto error;
}
else if (localeID == NULL) {
goto error;
}
trailingIndex =
parseTagString(
localeID,
lang,
&langLength,
script,
&scriptLength,
region,
®ionLength,
err);
if(U_FAILURE(*err)) {
if (*err == U_BUFFER_OVERFLOW_ERROR) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
goto error;
}
while (_isIDSeparator(localeID[trailingIndex])) {
trailingIndex++;
}
trailing = &localeID[trailingIndex];
trailingLength = (int32_t)uprv_strlen(trailing);
CHECK_TRAILING_VARIANT_SIZE(trailing, trailingLength);
{
icu::CharString base;
{
icu::CharStringByteSink sink(&base);
createTagString(
lang,
langLength,
script,
scriptLength,
region,
regionLength,
NULL,
0,
sink,
err);
}
{
icu::CharStringByteSink sink(&maximizedTagBuffer);
ulocimp_addLikelySubtags(base.data(), sink, err);
}
}
if(U_FAILURE(*err)) {
goto error;
}
{
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createLikelySubtagsString(
lang,
langLength,
NULL,
0,
NULL,
0,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
else if (uprv_strnicmp(
maximizedTagBuffer.data(),
tagBuffer.data(),
tagBuffer.length()) == 0) {
createTagString(
lang,
langLength,
NULL,
0,
NULL,
0,
trailing,
trailingLength,
sink,
err);
return;
}
}
if (regionLength > 0) {
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createLikelySubtagsString(
lang,
langLength,
NULL,
0,
region,
regionLength,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
else if (uprv_strnicmp(
maximizedTagBuffer.data(),
tagBuffer.data(),
tagBuffer.length()) == 0) {
createTagString(
lang,
langLength,
NULL,
0,
region,
regionLength,
trailing,
trailingLength,
sink,
err);
return;
}
}
if (scriptLength > 0 && regionLength > 0) {
icu::CharString tagBuffer;
{
icu::CharStringByteSink sink(&tagBuffer);
createLikelySubtagsString(
lang,
langLength,
script,
scriptLength,
NULL,
0,
NULL,
0,
sink,
err);
}
if(U_FAILURE(*err)) {
goto error;
}
else if (uprv_strnicmp(
maximizedTagBuffer.data(),
tagBuffer.data(),
tagBuffer.length()) == 0) {
createTagString(
lang,
langLength,
script,
scriptLength,
NULL,
0,
trailing,
trailingLength,
sink,
err);
return;
}
}
{
const int32_t localeIDLength = (int32_t)uprv_strlen(localeID);
sink.Append(localeID, localeIDLength);
return;
}
error:
if (!U_FAILURE(*err)) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
}
}
static UBool
do_canonicalize(const char* localeID,
char* buffer,
int32_t bufferCapacity,
UErrorCode* err)
{
uloc_canonicalize(
localeID,
buffer,
bufferCapacity,
err);
if (*err == U_STRING_NOT_TERMINATED_WARNING ||
*err == U_BUFFER_OVERFLOW_ERROR) {
*err = U_ILLEGAL_ARGUMENT_ERROR;
return FALSE;
}
else if (U_FAILURE(*err)) {
return FALSE;
}
else {
return TRUE;
}
}
U_CAPI int32_t U_EXPORT2
uloc_addLikelySubtags(const char* localeID,
char* maximizedLocaleID,
int32_t maximizedLocaleIDCapacity,
UErrorCode* status) {
if (U_FAILURE(*status)) {
return 0;
}
icu::CheckedArrayByteSink sink(
maximizedLocaleID, maximizedLocaleIDCapacity);
ulocimp_addLikelySubtags(localeID, sink, status);
int32_t reslen = sink.NumberOfBytesAppended();
if (U_FAILURE(*status)) {
return sink.Overflowed() ? reslen : -1;
}
if (sink.Overflowed()) {
*status = U_BUFFER_OVERFLOW_ERROR;
} else {
u_terminateChars(
maximizedLocaleID, maximizedLocaleIDCapacity, reslen, status);
}
return reslen;
}
U_CAPI void U_EXPORT2
ulocimp_addLikelySubtags(const char* localeID,
icu::ByteSink& sink,
UErrorCode* status) {
char localeBuffer[ULOC_FULLNAME_CAPACITY];
if (do_canonicalize(localeID, localeBuffer, sizeof localeBuffer, status)) {
_uloc_addLikelySubtags(localeBuffer, sink, status);
}
}
U_CAPI int32_t U_EXPORT2
uloc_minimizeSubtags(const char* localeID,
char* minimizedLocaleID,
int32_t minimizedLocaleIDCapacity,
UErrorCode* status) {
if (U_FAILURE(*status)) {
return 0;
}
icu::CheckedArrayByteSink sink(
minimizedLocaleID, minimizedLocaleIDCapacity);
ulocimp_minimizeSubtags(localeID, sink, status);
int32_t reslen = sink.NumberOfBytesAppended();
if (U_FAILURE(*status)) {
return sink.Overflowed() ? reslen : -1;
}
if (sink.Overflowed()) {
*status = U_BUFFER_OVERFLOW_ERROR;
} else {
u_terminateChars(
minimizedLocaleID, minimizedLocaleIDCapacity, reslen, status);
}
return reslen;
}
U_CAPI void U_EXPORT2
ulocimp_minimizeSubtags(const char* localeID,
icu::ByteSink& sink,
UErrorCode* status) {
char localeBuffer[ULOC_FULLNAME_CAPACITY];
if (do_canonicalize(localeID, localeBuffer, sizeof localeBuffer, status)) {
_uloc_minimizeSubtags(localeBuffer, sink, status);
}
}
static const char LANG_DIR_STRING[] =
"root-en-es-pt-zh-ja-ko-de-fr-it-ar+he+fa+ru-nl-pl-th-tr-";
U_CAPI UBool U_EXPORT2
uloc_isRightToLeft(const char *locale) {
UErrorCode errorCode = U_ZERO_ERROR;
char script[8];
int32_t scriptLength = uloc_getScript(locale, script, UPRV_LENGTHOF(script), &errorCode);
if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING ||
scriptLength == 0) {
errorCode = U_ZERO_ERROR;
char lang[8];
int32_t langLength = uloc_getLanguage(locale, lang, UPRV_LENGTHOF(lang), &errorCode);
if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) {
return FALSE;
}
if (langLength > 0) {
const char* langPtr = uprv_strstr(LANG_DIR_STRING, lang);
if (langPtr != NULL) {
switch (langPtr[langLength]) {
case '-': return FALSE;
case '+': return TRUE;
default: break; }
}
}
errorCode = U_ZERO_ERROR;
icu::CharString likely;
{
icu::CharStringByteSink sink(&likely);
ulocimp_addLikelySubtags(locale, sink, &errorCode);
}
if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING) {
return FALSE;
}
scriptLength = uloc_getScript(likely.data(), script, UPRV_LENGTHOF(script), &errorCode);
if (U_FAILURE(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING ||
scriptLength == 0) {
return FALSE;
}
}
if (uprv_strcmp(script,"Aran")==0) {
uprv_strcpy(script,"Arab"); }
UScriptCode scriptCode = (UScriptCode)u_getPropertyValueEnum(UCHAR_SCRIPT, script);
return uscript_isRightToLeft(scriptCode);
}
U_NAMESPACE_BEGIN
UBool
Locale::isRightToLeft() const {
return uloc_isRightToLeft(getBaseName());
}
U_NAMESPACE_END
#define ULOC_RG_BUFLEN 8
U_CAPI int32_t U_EXPORT2
ulocimp_getRegionForSupplementalData(const char *localeID, UBool inferRegion,
char *region, int32_t regionCapacity, UErrorCode* status) {
if (U_FAILURE(*status)) {
return 0;
}
char rgBuf[ULOC_RG_BUFLEN];
UErrorCode rgStatus = U_ZERO_ERROR;
int32_t rgLen = uloc_getKeywordValue(localeID, "rg", rgBuf, ULOC_RG_BUFLEN, &rgStatus);
if (U_FAILURE(rgStatus) || rgLen != 6) {
rgLen = 0;
} else {
char *rgPtr = rgBuf;
for (; *rgPtr!= 0; rgPtr++) {
*rgPtr = uprv_toupper(*rgPtr);
}
rgLen = (uprv_strcmp(rgBuf+2, "ZZZZ") == 0)? 2: 0;
}
if (rgLen == 0) {
rgLen = uloc_getCountry(localeID, rgBuf, ULOC_RG_BUFLEN, status);
if (U_FAILURE(*status)) {
rgLen = 0;
} else if (rgLen == 0 && inferRegion) {
rgStatus = U_ZERO_ERROR;
icu::CharString locBuf;
{
icu::CharStringByteSink sink(&locBuf);
ulocimp_addLikelySubtags(localeID, sink, &rgStatus);
}
if (U_SUCCESS(rgStatus)) {
rgLen = uloc_getCountry(locBuf.data(), rgBuf, ULOC_RG_BUFLEN, status);
if (U_FAILURE(*status)) {
rgLen = 0;
}
}
}
}
rgBuf[rgLen] = 0;
uprv_strncpy(region, rgBuf, regionCapacity);
return u_terminateChars(region, regionCapacity, rgLen, status);
}