X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..729e4ab9bc6618bc3d8a898e575df7f4019e29ca:/icuSources/common/uloc.c diff --git a/icuSources/common/uloc.c b/icuSources/common/uloc.c index c1ac14e3..64465032 100644 --- a/icuSources/common/uloc.c +++ b/icuSources/common/uloc.c @@ -1,6 +1,6 @@ /* ********************************************************************** -* Copyright (C) 1997-2008, International Business Machines +* Copyright (C) 1997-2010, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -31,12 +31,10 @@ #include "unicode/utypes.h" #include "unicode/ustring.h" #include "unicode/uloc.h" -#include "unicode/ures.h" #include "putilimp.h" #include "ustr_imp.h" #include "ulocimp.h" -#include "uresimp.h" #include "umutex.h" #include "cstring.h" #include "cmemory.h" @@ -61,24 +59,6 @@ locale_getKeywords(const char *localeID, UBool valuesToo, UErrorCode *status); -/* ### Constants **************************************************/ - -/* These strings describe the resources we attempt to load from - the locale ResourceBundle data file.*/ -static const char _kLanguages[] = "Languages"; -static const char _kScripts[] = "Scripts"; -static const char _kCountries[] = "Countries"; -static const char _kVariants[] = "Variants"; -static const char _kKeys[] = "Keys"; -static const char _kTypes[] = "Types"; -static const char _kIndexLocaleName[] = "res_index"; -static const char _kRootName[] = "root"; -static const char _kIndexTag[] = "InstalledLocales"; -static const char _kCurrency[] = "currency"; -static const char _kCurrencies[] = "Currencies"; -static char** _installedLocales = NULL; -static int32_t _installedLocalesCount = 0; - /* ### Data tables **************************************************/ /** @@ -146,7 +126,7 @@ static const char * const LANGUAGES[] = { "lah", "lam", "lb", "lez", "lg", "li", "ln", "lo", "lol", "loz", "lt", "lu", "lua", "lui", "lun", "luo", "lus", "lv", "mad", "mag", "mai", "mak", "man", "map", "mas", - "mdf", "mdr", "men", "mg", "mga", "mh", "mi", "mic", "min", + "mdf", "mdr", "men", "mfe", "mg", "mga", "mh", "mi", "mic", "min", "mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno", "mo", "moh", "mos", "mr", "ms", "mt", "mul", "mun", "mus", "mwl", "mwr", "my", "myn", "myv", "na", "nah", "nai", "nap", @@ -165,7 +145,7 @@ static const char * const LANGUAGES[] = { "srn", "srr", "ss", "ssa", "st", "su", "suk", "sus", "sux", "sv", "sw", "syc", "syr", "ta", "tai", "te", "tem", "ter", "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl", - "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", + "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", "trv", "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", "ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur", "uz", "vai", "ve", "vi", "vo", "vot", "wa", "wak", @@ -262,8 +242,8 @@ static const char * const LANGUAGES_3[] = { "loz", "lit", "lub", "lua", "lui", "lun", "luo", "lus", /* "lv", "mad", "mag", "mai", "mak", "man", "map", "mas", */ "lav", "mad", "mag", "mai", "mak", "man", "map", "mas", -/* "mdf", "mdr", "men", "mg", "mga", "mh", "mi", "mic", "min", */ - "mdf", "mdr", "men", "mlg", "mga", "mah", "mri", "mic", "min", +/* "mdf", "mdr", "men", "mfe", "mg", "mga", "mh", "mi", "mic", "min", */ + "mdf", "mdr", "men", "mfe", "mlg", "mga", "mah", "mri", "mic", "min", /* "mis", "mk", "mkh", "ml", "mn", "mnc", "mni", "mno", */ "mis", "mkd", "mkh", "mal", "mon", "mnc", "mni", "mno", /* "mo", "moh", "mos", "mr", "ms", "mt", "mul", "mun", */ @@ -300,8 +280,8 @@ static const char * const LANGUAGES_3[] = { "swe", "swa", "syc", "syr", "tam", "tai", "tel", "tem", "ter", /* "tet", "tg", "th", "ti", "tig", "tiv", "tk", "tkl", */ "tet", "tgk", "tha", "tir", "tig", "tiv", "tuk", "tkl", -/* "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", */ - "tgl", "tlh", "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur", +/* "tl", "tlh", "tli", "tmh", "tn", "to", "tog", "tpi", "tr", "trv", */ + "tgl", "tlh", "tli", "tmh", "tsn", "ton", "tog", "tpi", "tur", "trv", /* "ts", "tsi", "tt", "tum", "tup", "tut", "tvl", "tw", */ "tso", "tsi", "tat", "tum", "tup", "tut", "tvl", "twi", /* "ty", "tyv", "udm", "ug", "uga", "uk", "umb", "und", "ur", */ @@ -481,7 +461,7 @@ typedef struct CanonicalizationMap { */ static const CanonicalizationMap CANONICALIZE_MAP[] = { { "", "en_US_POSIX", NULL, NULL }, /* .NET name */ - { "C", "en_US_POSIX", NULL, NULL }, /* POSIX name */ + { "c", "en_US_POSIX", NULL, NULL }, /* POSIX name */ { "posix", "en_US_POSIX", NULL, NULL }, /* POSIX name (alias of C) */ { "art_LOJBAN", "jbo", NULL, NULL }, /* registered name */ { "az_AZ_CYRL", "az_Cyrl_AZ", NULL, NULL }, /* .NET name */ @@ -547,12 +527,48 @@ static const VariantMap VARIANT_MAP[] = { { "STROKE", "collation", "stroke" } /* Solaris variant */ }; +/* ### BCP47 Conversion *******************************************/ +/* Test if the locale id has BCP47 u extension and does not have '@' */ +#define _hasBCP47Extension(id) (id && uprv_strstr(id, "@") == NULL && getShortestSubtagLength(localeID) == 1) +/* Converts the BCP47 id to Unicode id. Does nothing to id if conversion fails */ +#define _ConvertBCP47(finalID, id, buffer, length,err) \ + if (uloc_forLanguageTag(id, buffer, length, NULL, err) <= 0 || U_FAILURE(*err)) { \ + finalID=id; \ + } else { \ + finalID=buffer; \ + } +/* Gets the size of the shortest subtag in the given localeID. */ +static int32_t getShortestSubtagLength(const char *localeID) { + int32_t localeIDLength = uprv_strlen(localeID); + int32_t length = localeIDLength; + int32_t tmpLength = 0; + int32_t i; + UBool reset = TRUE; + + for (i = 0; i < localeIDLength; i++) { + if (localeID[i] != '_' && localeID[i] != '-') { + if (reset) { + tmpLength = 0; + reset = FALSE; + } + tmpLength++; + } else { + if (tmpLength != 0 && tmpLength < length) { + length = tmpLength; + } + reset = TRUE; + } + } + + return length; +} + /* ### Keywords **************************************************/ #define ULOC_KEYWORD_BUFFER_LEN 25 #define ULOC_MAX_NO_KEYWORDS 25 -static const char * +U_CAPI const char * U_EXPORT2 locale_getKeywordsStart(const char *localeID) { const char *result = NULL; if((result = uprv_strchr(localeID, '@')) != NULL) { @@ -807,6 +823,7 @@ uloc_getKeywordValue(const char* localeID, char* buffer, int32_t bufferCapacity, UErrorCode* status) { + const char* startSearchHere = NULL; const char* nextSeparator = NULL; char keywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; char localeKeywordNameBuffer[ULOC_KEYWORD_BUFFER_LEN]; @@ -814,8 +831,16 @@ uloc_getKeywordValue(const char* localeID, int32_t result = 0; if(status && U_SUCCESS(*status) && localeID) { + char tempBuffer[ULOC_FULLNAME_CAPACITY]; + const char* tmpLocaleID; + + if (_hasBCP47Extension(localeID)) { + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status); + } else { + tmpLocaleID=localeID; + } - const char* startSearchHere = uprv_strchr(localeID, '@'); /* TODO: REVISIT: shouldn't this be locale_getKeywordsStart ? */ + startSearchHere = uprv_strchr(tmpLocaleID, '@'); /* TODO: REVISIT: shouldn't this be locale_getKeywordsStart ? */ if(startSearchHere == NULL) { /* no keywords, return at once */ return 0; @@ -1087,9 +1112,6 @@ uloc_setKeywordValue(const char* keywordName, /* ### ID parsing implementation **************************************************/ -/*returns TRUE if a is an ID separator FALSE otherwise*/ -#define _isIDSeparator(a) (a == '_' || a == '-') - #define _isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I')) /*returns TRUE if one of the special prefixes is here (s=string) @@ -1160,7 +1182,7 @@ _copyCount(char *dest, int32_t destCapacity, const char *src) { } } -static const char* +U_CFUNC const char* uloc_getCurrentCountryID(const char* oldID){ int32_t offset = _findIndex(DEPRECATED_COUNTRIES, oldID); if (offset >= 0) { @@ -1168,7 +1190,7 @@ uloc_getCurrentCountryID(const char* oldID){ } return oldID; } -static const char* +U_CFUNC const char* uloc_getCurrentLanguageID(const char* oldID){ int32_t offset = _findIndex(DEPRECATED_LANGUAGES, oldID); if (offset >= 0) { @@ -1184,10 +1206,10 @@ uloc_getCurrentLanguageID(const char* oldID){ * * TODO try to use this in Locale */ -static int32_t -_getLanguage(const char *localeID, - char *language, int32_t languageCapacity, - const char **pEnd) { +U_CFUNC int32_t +ulocimp_getLanguage(const char *localeID, + char *language, int32_t languageCapacity, + const char **pEnd) { int32_t i=0; int32_t offset; char lang[4]={ 0, 0, 0, 0 }; /* temporary buffer to hold language code for searching */ @@ -1230,10 +1252,10 @@ _getLanguage(const char *localeID, return i; } -static int32_t -_getScript(const char *localeID, - char *script, int32_t scriptCapacity, - const char **pEnd) +U_CFUNC int32_t +ulocimp_getScript(const char *localeID, + char *script, int32_t scriptCapacity, + const char **pEnd) { int32_t idLen = 0; @@ -1268,39 +1290,52 @@ _getScript(const char *localeID, return idLen; } -static int32_t -_getCountry(const char *localeID, - char *country, int32_t countryCapacity, - const char **pEnd) +U_CFUNC int32_t +ulocimp_getCountry(const char *localeID, + char *country, int32_t countryCapacity, + const char **pEnd) { - int32_t i=0; + int32_t idLen=0; char cnty[ULOC_COUNTRY_CAPACITY]={ 0, 0, 0, 0 }; int32_t offset; /* copy the country as far as possible and count its length */ - while(!_isTerminator(*localeID) && !_isIDSeparator(*localeID)) { - if(i=0) { - i=_copyCount(country, countryCapacity, COUNTRIES[offset]); + /* the country should be either length 2 or 3 */ + if (idLen == 2 || idLen == 3) { + UBool gotCountry = FALSE; + /* convert 3 character code to 2 character code if possible *CWB*/ + if(idLen==3) { + offset=_findIndex(COUNTRIES_3, cnty); + if(offset>=0) { + idLen=_copyCount(country, countryCapacity, COUNTRIES[offset]); + gotCountry = TRUE; + } + } + if (!gotCountry) { + int32_t i = 0; + for (i = 0; i < idLen; i++) { + if (i < countryCapacity) { + country[i]=(char)uprv_toupper(localeID[i]); + } + } } + localeID+=idLen; + } else { + idLen = 0; } if(pEnd!=NULL) { *pEnd=localeID; } - return i; + + return idLen; } /** @@ -1514,36 +1549,44 @@ uloc_openKeywords(const char* localeID, int32_t i=0; char keywords[256]; int32_t keywordsCapacity = 256; + char tempBuffer[ULOC_FULLNAME_CAPACITY]; + const char* tmpLocaleID; + if(status==NULL || U_FAILURE(*status)) { return 0; } - if(localeID==NULL) { - localeID=uloc_getDefault(); + if (_hasBCP47Extension(localeID)) { + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), status); + } else { + if (localeID==NULL) { + localeID=uloc_getDefault(); + } + tmpLocaleID=localeID; } /* Skip the language */ - _getLanguage(localeID, NULL, 0, &localeID); - if(_isIDSeparator(*localeID)) { + ulocimp_getLanguage(tmpLocaleID, NULL, 0, &tmpLocaleID); + if(_isIDSeparator(*tmpLocaleID)) { const char *scriptID; /* Skip the script if available */ - _getScript(localeID+1, NULL, 0, &scriptID); - if(scriptID != localeID+1) { + ulocimp_getScript(tmpLocaleID+1, NULL, 0, &scriptID); + if(scriptID != tmpLocaleID+1) { /* Found optional script */ - localeID = scriptID; + tmpLocaleID = scriptID; } /* Skip the Country */ - if (_isIDSeparator(*localeID)) { - _getCountry(localeID+1, NULL, 0, &localeID); - if(_isIDSeparator(*localeID)) { - _getVariant(localeID+1, *localeID, NULL, 0); + if (_isIDSeparator(*tmpLocaleID)) { + ulocimp_getCountry(tmpLocaleID+1, NULL, 0, &tmpLocaleID); + if(_isIDSeparator(*tmpLocaleID)) { + _getVariant(tmpLocaleID+1, *tmpLocaleID, NULL, 0); } } } /* keywords are located after '@' */ - if((localeID = locale_getKeywordsStart(localeID)) != NULL) { - i=locale_getKeywords(localeID+1, '@', keywords, keywordsCapacity, NULL, 0, NULL, FALSE, status); + if((tmpLocaleID = locale_getKeywordsStart(tmpLocaleID)) != NULL) { + i=locale_getKeywords(tmpLocaleID+1, '@', keywords, keywordsCapacity, NULL, 0, NULL, FALSE, status); } if(i) { @@ -1578,7 +1621,9 @@ _canonicalize(const char* localeID, UErrorCode* err) { int32_t j, len, fieldCount=0, scriptSize=0, variantSize=0, nameCapacity; char localeBuffer[ULOC_FULLNAME_CAPACITY]; + char tempBuffer[ULOC_FULLNAME_CAPACITY]; const char* origLocaleID; + const char* tmpLocaleID; const char* keywordAssign = NULL; const char* separatorIndicator = NULL; const char* addKeyword = NULL; @@ -1590,14 +1635,20 @@ _canonicalize(const char* localeID, return 0; } - if (localeID==NULL) { - localeID=uloc_getDefault(); + if (_hasBCP47Extension(localeID)) { + _ConvertBCP47(tmpLocaleID, localeID, tempBuffer, sizeof(tempBuffer), err); + } else { + if (localeID==NULL) { + localeID=uloc_getDefault(); + } + tmpLocaleID=localeID; } - origLocaleID=localeID; + + origLocaleID=tmpLocaleID; /* if we are doing a full canonicalization, then put results in localeBuffer, if necessary; otherwise send them to result. */ - if (OPTION_SET(options, _ULOC_CANONICALIZE) && + if (/*OPTION_SET(options, _ULOC_CANONICALIZE) &&*/ (result == NULL || resultCapacity < sizeof(localeBuffer))) { name = localeBuffer; nameCapacity = sizeof(localeBuffer); @@ -1607,17 +1658,17 @@ _canonicalize(const char* localeID, } /* get all pieces, one after another, and separate with '_' */ - len=_getLanguage(localeID, name, nameCapacity, &localeID); + len=ulocimp_getLanguage(tmpLocaleID, name, nameCapacity, &tmpLocaleID); if(len == I_DEFAULT_LENGTH && uprv_strncmp(origLocaleID, i_default, len) == 0) { const char *d = uloc_getDefault(); - len = uprv_strlen(d); + len = (int32_t)uprv_strlen(d); if (name != NULL) { uprv_strncpy(name, d, len); } - } else if(_isIDSeparator(*localeID)) { + } else if(_isIDSeparator(*tmpLocaleID)) { const char *scriptID; ++fieldCount; @@ -1626,13 +1677,13 @@ _canonicalize(const char* localeID, } ++len; - scriptSize=_getScript(localeID+1, name+len, nameCapacity-len, &scriptID); + scriptSize=ulocimp_getScript(tmpLocaleID+1, name+len, nameCapacity-len, &scriptID); if(scriptSize > 0) { /* Found optional script */ - localeID = scriptID; + tmpLocaleID = scriptID; ++fieldCount; len+=scriptSize; - if (_isIDSeparator(*localeID)) { + if (_isIDSeparator(*tmpLocaleID)) { /* If there is something else, then we add the _ */ if(len 0) { + /* Found optional country */ + tmpLocaleID = cntryID; + len+=cntrySize; + } + if(_isIDSeparator(*tmpLocaleID)) { + /* If there is something else, then we add the _ if we found country before.*/ + if (cntrySize > 0) { + ++fieldCount; + if(len 0) { variant = name+len; len += variantSize; - localeID += variantSize + 1; /* skip '_' and variant */ + tmpLocaleID += variantSize + 1; /* skip '_' and variant */ } } } } /* Copy POSIX-style charset specifier, if any [mr.utf8] */ - if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *localeID == '.') { + if (!OPTION_SET(options, _ULOC_CANONICALIZE) && *tmpLocaleID == '.') { UBool done = FALSE; do { - char c = *localeID; + char c = *tmpLocaleID; switch (c) { case 0: case '@': @@ -1674,24 +1735,24 @@ _canonicalize(const char* localeID, name[len] = c; } ++len; - ++localeID; + ++tmpLocaleID; break; } } while (!done); } /* Scan ahead to next '@' and determine if it is followed by '=' and/or ';' - After this, localeID either points to '@' or is NULL */ - if ((localeID=locale_getKeywordsStart(localeID))!=NULL) { - keywordAssign = uprv_strchr(localeID, '='); - separatorIndicator = uprv_strchr(localeID, ';'); + After this, tmpLocaleID either points to '@' or is NULL */ + if ((tmpLocaleID=locale_getKeywordsStart(tmpLocaleID))!=NULL) { + keywordAssign = uprv_strchr(tmpLocaleID, '='); + separatorIndicator = uprv_strchr(tmpLocaleID, ';'); } /* Copy POSIX-style variant, if any [mr@FOO] */ if (!OPTION_SET(options, _ULOC_CANONICALIZE) && - localeID != NULL && keywordAssign == NULL) { + tmpLocaleID != NULL && keywordAssign == NULL) { for (;;) { - char c = *localeID; + char c = *tmpLocaleID; if (c == 0) { break; } @@ -1699,13 +1760,13 @@ _canonicalize(const char* localeID, name[len] = c; } ++len; - ++localeID; + ++tmpLocaleID; } } if (OPTION_SET(options, _ULOC_CANONICALIZE)) { /* Handle @FOO variant if @ is present and not followed by = */ - if (localeID!=NULL && keywordAssign==NULL) { + if (tmpLocaleID!=NULL && keywordAssign==NULL) { int32_t posixVariantSize; /* Add missing '_' if needed */ if (fieldCount < 2 || (fieldCount < 3 && scriptSize > 0)) { @@ -1717,7 +1778,7 @@ _canonicalize(const char* localeID, ++fieldCount; } while(fieldCount<2); } - posixVariantSize = _getVariantEx(localeID+1, '@', name+len, nameCapacity-len, + posixVariantSize = _getVariantEx(tmpLocaleID+1, '@', name+len, nameCapacity-len, (UBool)(variantSize > 0)); if (posixVariantSize > 0) { if (variant == NULL) { @@ -1754,7 +1815,7 @@ _canonicalize(const char* localeID, const char* id = CANONICALIZE_MAP[j].id; int32_t n = (int32_t)uprv_strlen(id); if (len == n && uprv_strncmp(name, id, n) == 0) { - if (n == 0 && localeID != NULL) { + if (n == 0 && tmpLocaleID != NULL) { break; /* Don't remap "" if keywords present */ } len = _copyCount(name, nameCapacity, CANONICALIZE_MAP[j].canonicalID); @@ -1768,14 +1829,14 @@ _canonicalize(const char* localeID, } if (!OPTION_SET(options, _ULOC_STRIP_KEYWORDS)) { - if (localeID!=NULL && keywordAssign!=NULL && + if (tmpLocaleID!=NULL && keywordAssign!=NULL && (!separatorIndicator || separatorIndicator > keywordAssign)) { if(lenfallback->default->failure) */ - *pErrorCode=errorCode; - } + return COUNTRIES; +} - for(;;){ - ures_initStackObject(&table); - ures_initStackObject(&subTable); - ures_getByKeyWithFallback(rb, tableKey, &table, &errorCode); - if (subTableKey != NULL) { - /* - ures_getByKeyWithFallback(&table,subTableKey, &subTable, &errorCode); - item = ures_getStringByKeyWithFallback(&subTable, itemKey, pLength, &errorCode); - if(U_FAILURE(errorCode)){ - *pErrorCode = errorCode; - } - - break;*/ - - ures_getByKeyWithFallback(&table,subTableKey, &table, &errorCode); - } - if(U_SUCCESS(errorCode)){ - item = ures_getStringByKeyWithFallback(&table, itemKey, pLength, &errorCode); - if(U_FAILURE(errorCode)){ - const char* replacement = NULL; - *pErrorCode = errorCode; /*save the errorCode*/ - errorCode = U_ZERO_ERROR; - /* may be a deprecated code */ - if(uprv_strcmp(tableKey, "Countries")==0){ - replacement = uloc_getCurrentCountryID(itemKey); - }else if(uprv_strcmp(tableKey, "Languages")==0){ - replacement = uloc_getCurrentLanguageID(itemKey); - } - /*pointer comparison is ok since uloc_getCurrentCountryID & uloc_getCurrentLanguageID return the key itself is replacement is not found*/ - if(replacement!=NULL && itemKey != replacement){ - item = ures_getStringByKeyWithFallback(&table, replacement, pLength, &errorCode); - if(U_SUCCESS(errorCode)){ - *pErrorCode = errorCode; - break; - } - } - }else{ - break; - } - } - - if(U_FAILURE(errorCode)){ - /* still can't figure out ?.. try the fallback mechanism */ - int32_t len = 0; - const UChar* fallbackLocale = NULL; - *pErrorCode = errorCode; - errorCode = U_ZERO_ERROR; +/* this function to be moved into cstring.c later */ +static char gDecimal = 0; - fallbackLocale = ures_getStringByKeyWithFallback(&table, "Fallback", &len, &errorCode); - if(U_FAILURE(errorCode)){ - *pErrorCode = errorCode; - break; - } - - u_UCharsToChars(fallbackLocale, explicitFallbackName, len); - - /* guard against recursive fallback */ - if(uprv_strcmp(explicitFallbackName, locale)==0){ - *pErrorCode = U_INTERNAL_PROGRAM_ERROR; - break; - } - ures_close(rb); - rb = ures_open(NULL, explicitFallbackName, &errorCode); - if(U_FAILURE(errorCode)){ - *pErrorCode = errorCode; - break; - } - /* succeeded in opening the fallback bundle .. continue and try to fetch the item */ - }else{ - break; - } +static /* U_CAPI */ +double +/* U_EXPORT2 */ +_uloc_strtod(const char *start, char **end) { + char *decimal; + char *myEnd; + char buf[30]; + double rv; + if (!gDecimal) { + char rep[5]; + /* For machines that decide to change the decimal on you, + and try to be too smart with localization. + This normally should be just a '.'. */ + sprintf(rep, "%+1.1f", 1.0); + gDecimal = rep[2]; } - /* done with the locale string - ready to close table and rb */ - ures_close(&subTable); - ures_close(&table); - ures_close(rb); - return item; -} -static int32_t -_getStringOrCopyKey(const char *path, const char *locale, - const char *tableKey, - const char* subTableKey, - const char *itemKey, - const char *substitute, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - const UChar *s = NULL; - int32_t length = 0; - - if(itemKey==NULL) { - /* top-level item: normal resource bundle access */ - UResourceBundle *rb; - - rb=ures_open(path, locale, pErrorCode); - if(U_SUCCESS(*pErrorCode)) { - s=ures_getStringByKey(rb, tableKey, &length, pErrorCode); - /* see comment about closing rb near "return item;" in _res_getTableStringWithFallback() */ - ures_close(rb); - } + if(gDecimal == '.') { + return uprv_strtod(start, end); /* fall through to OS */ } else { - /* Language code should not be a number. If it is, set the error code. */ - if (!uprv_strncmp(tableKey, "Languages", 9) && uprv_strtol(itemKey, NULL, 10)) { - *pErrorCode = U_MISSING_RESOURCE_ERROR; + uprv_strncpy(buf, start, 29); + buf[29]=0; + decimal = uprv_strchr(buf, '.'); + if(decimal) { + *decimal = gDecimal; } else { - /* second-level item, use special fallback */ - s=_res_getTableStringWithFallback(path, locale, - tableKey, - subTableKey, - itemKey, - &length, - pErrorCode); + return uprv_strtod(start, end); /* no decimal point */ } - } - if(U_SUCCESS(*pErrorCode)) { - int32_t copyLength=uprv_min(length, destCapacity); - if(copyLength>0 && s != NULL) { - u_memcpy(dest, s, copyLength); + rv = uprv_strtod(buf, &myEnd); + if(end) { + *end = (char*)(start+(myEnd-buf)); /* cast away const (to follow uprv_strtod API.) */ } - } else { - /* no string from a resource bundle: convert the substitute */ - length=(int32_t)uprv_strlen(substitute); - u_charsToUChars(substitute, dest, uprv_min(length, destCapacity)); - *pErrorCode=U_USING_DEFAULT_WARNING; + return rv; } - - return u_terminateUChars(dest, destCapacity, length, pErrorCode); } -static int32_t -_getDisplayNameForComponent(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - int32_t (*getter)(const char *, char *, int32_t, UErrorCode *), - const char *tag, - UErrorCode *pErrorCode) { - char localeBuffer[ULOC_FULLNAME_CAPACITY*4]; - int32_t length; - UErrorCode localStatus; +typedef struct { + float q; + int32_t dummy; /* to avoid uninitialized memory copy from qsort */ + char *locale; +} _acceptLangItem; - /* argument checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } +static int32_t U_CALLCONV +uloc_acceptLanguageCompare(const void *context, const void *a, const void *b) +{ + const _acceptLangItem *aa = (const _acceptLangItem*)a; + const _acceptLangItem *bb = (const _acceptLangItem*)b; - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; + int32_t rc = 0; + if(bb->q < aa->q) { + rc = -1; /* A > B */ + } else if(bb->q > aa->q) { + rc = 1; /* A < B */ + } else { + rc = 0; /* A = B */ } - localStatus = U_ZERO_ERROR; - length=(*getter)(locale, localeBuffer, sizeof(localeBuffer), &localStatus); - if(U_FAILURE(localStatus) || localStatus==U_STRING_NOT_TERMINATED_WARNING) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - if(length==0) { - return u_terminateUChars(dest, destCapacity, 0, pErrorCode); + if(rc==0) { + rc = uprv_stricmp(aa->locale, bb->locale); } - return _getStringOrCopyKey(NULL, displayLocale, - tag, NULL, localeBuffer, - localeBuffer, - dest, destCapacity, - pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayLanguage(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getLanguage, _kLanguages, pErrorCode); -} - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayScript(const char* locale, - const char* displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) -{ - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getScript, _kScripts, pErrorCode); -} +#if defined(ULOC_DEBUG) + /* fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n", + aa->locale, aa->q, + bb->locale, bb->q, + rc);*/ +#endif -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayCountry(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getCountry, _kCountries, pErrorCode); + return rc; } -/* - * TODO separate variant1_variant2_variant3... - * by getting each tag's display string and concatenating them with ", " - * in between - similar to uloc_getDisplayName() - */ -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayVariant(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) { - return _getDisplayNameForComponent(locale, displayLocale, dest, destCapacity, - uloc_getVariant, _kVariants, pErrorCode); -} +/* +mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, nl-nl;q=0.55, nl;q=0.53 +*/ U_CAPI int32_t U_EXPORT2 -uloc_getDisplayName(const char *locale, - const char *displayLocale, - UChar *dest, int32_t destCapacity, - UErrorCode *pErrorCode) +uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, UAcceptResult *outResult, + const char *httpAcceptLanguage, + UEnumeration* availableLocales, + UErrorCode *status) { - int32_t length, length2, length3 = 0; - UBool hasLanguage, hasScript, hasCountry, hasVariant, hasKeywords; - UEnumeration* keywordEnum = NULL; - int32_t keywordCount = 0; - const char *keyword = NULL; - int32_t keywordLen = 0; - char keywordValue[256]; - int32_t keywordValueLen = 0; - - /* argument checking */ - if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { - return 0; - } + _acceptLangItem *j; + _acceptLangItem smallBuffer[30]; + char **strs; + char tmp[ULOC_FULLNAME_CAPACITY +1]; + int32_t n = 0; + const char *itemEnd; + const char *paramEnd; + const char *s; + const char *t; + int32_t res; + int32_t i; + int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage); + int32_t jSize; + char *tempstr; /* Use for null pointer check */ - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR; - return 0; + j = smallBuffer; + jSize = sizeof(smallBuffer)/sizeof(smallBuffer[0]); + if(U_FAILURE(*status)) { + return -1; } - /* - * if there is a language, then write "language (country, variant)" - * otherwise write "country, variant" - */ - - /* write the language */ - length=uloc_getDisplayLanguage(locale, displayLocale, - dest, destCapacity, - pErrorCode); - hasLanguage= length>0; - - if(hasLanguage) { - /* append " (" */ - if(length0; - length+=length2; - - if(hasScript) { - /* append ", " */ - if(length0; - length+=length2; - - if(hasCountry) { - /* append ", " */ - if(length0; - length+=length2; - - if(hasVariant) { - /* append ", " */ - if(length 0 ; keywordCount--){ - if(U_FAILURE(*pErrorCode)){ - break; - } - /* the uenum_next returns NUL terminated string */ - keyword = uenum_next(keywordEnum, &keywordLen, pErrorCode); - if(length + length3 < destCapacity) { - length3 += uloc_getDisplayKeyword(keyword, displayLocale, dest+length+length3, destCapacity-length-length3, pErrorCode); - } else { - length3 += uloc_getDisplayKeyword(keyword, displayLocale, NULL, 0, pErrorCode); - } - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { - /* keep preflighting */ - *pErrorCode=U_ZERO_ERROR; - } - keywordValueLen = uloc_getKeywordValue(locale, keyword, keywordValue, 256, pErrorCode); - if(keywordValueLen) { - if(length + length3 < destCapacity) { - dest[length + length3] = 0x3D; - } - length3++; - if(length + length3 < destCapacity) { - length3 += uloc_getDisplayKeywordValue(locale, keyword, displayLocale, dest+length+length3, destCapacity-length-length3, pErrorCode); - } else { - length3 += uloc_getDisplayKeywordValue(locale, keyword, displayLocale, NULL, 0, pErrorCode); - } - if(*pErrorCode==U_BUFFER_OVERFLOW_ERROR) { - /* keep preflighting */ - *pErrorCode=U_ZERO_ERROR; - } - } - if(keywordCount > 1) { - if(length + length3 + 1 < destCapacity && keywordCount) { - dest[length + length3]=0x2c; - dest[length + length3+1]=0x20; - } - length3++; /* ',' */ - length3++; /* ' ' */ - } - } - uenum_close(keywordEnum); - - hasKeywords = length3 > 0; - length += length3; - - - - if ((hasScript && !hasCountry) - || ((hasScript || hasCountry) && !hasVariant && !hasKeywords) - || ((hasScript || hasCountry || hasVariant) && !hasKeywords) - || (hasLanguage && !hasScript && !hasCountry && !hasVariant && !hasKeywords)) - { - /* remove ", " or " (" */ - length-=2; - } - - if (hasLanguage && (hasScript || hasCountry || hasVariant || hasKeywords)) { - /* append ")" */ - if(length0 && dest==NULL)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - - /* pass itemKey=NULL to look for a top-level item */ - return _getStringOrCopyKey(NULL, displayLocale, - _kKeys, NULL, - keyword, - keyword, - dest, destCapacity, - status); - -} - - -#define UCURRENCY_DISPLAY_NAME_INDEX 1 - -U_CAPI int32_t U_EXPORT2 -uloc_getDisplayKeywordValue( const char* locale, - const char* keyword, - const char* displayLocale, - UChar* dest, - int32_t destCapacity, - UErrorCode* status){ - - - char keywordValue[ULOC_FULLNAME_CAPACITY*4]; - int32_t capacity = ULOC_FULLNAME_CAPACITY*4; - int32_t keywordValueLen =0; - - /* argument checking */ - if(status==NULL || U_FAILURE(*status)) { - return 0; - } - - if(destCapacity<0 || (destCapacity>0 && dest==NULL)) { - *status=U_ILLEGAL_ARGUMENT_ERROR; - return 0; - } - - /* get the keyword value */ - keywordValue[0]=0; - keywordValueLen = uloc_getKeywordValue(locale, keyword, keywordValue, capacity, status); - - /* - * if the keyword is equal to currency .. then to get the display name - * we need to do the fallback ourselves - */ - if(uprv_stricmp(keyword, _kCurrency)==0){ - - int32_t dispNameLen = 0; - const UChar *dispName = NULL; - - UResourceBundle *bundle = ures_open(NULL, displayLocale, status); - UResourceBundle *currencies = ures_getByKey(bundle, _kCurrencies, NULL, status); - UResourceBundle *currency = ures_getByKeyWithFallback(currencies, keywordValue, NULL, status); - - dispName = ures_getStringByIndex(currency, UCURRENCY_DISPLAY_NAME_INDEX, &dispNameLen, status); - - /*close the bundles */ - ures_close(currency); - ures_close(currencies); - ures_close(bundle); - - if(U_FAILURE(*status)){ - if(*status == U_MISSING_RESOURCE_ERROR){ - /* we just want to write the value over if nothing is available */ - *status = U_USING_DEFAULT_WARNING; - }else{ - return 0; - } - } - - /* now copy the dispName over if not NULL */ - if(dispName != NULL){ - if(dispNameLen <= destCapacity){ - uprv_memcpy(dest, dispName, dispNameLen * U_SIZEOF_UCHAR); - return u_terminateUChars(dest, destCapacity, dispNameLen, status); - }else{ - *status = U_BUFFER_OVERFLOW_ERROR; - return dispNameLen; - } - }else{ - /* we have not found the display name for the value .. just copy over */ - if(keywordValueLen <= destCapacity){ - u_charsToUChars(keywordValue, dest, keywordValueLen); - return u_terminateUChars(dest, destCapacity, keywordValueLen, status); - }else{ - *status = U_BUFFER_OVERFLOW_ERROR; - return keywordValueLen; - } - } - - - }else{ - - return _getStringOrCopyKey(NULL, displayLocale, - _kTypes, keyword, - keywordValue, - keywordValue, - dest, destCapacity, - status); - } -} - -/* ### Get available **************************************************/ - -static UBool U_CALLCONV uloc_cleanup(void) { - char ** temp; - - if (_installedLocales) { - temp = _installedLocales; - _installedLocales = NULL; - - _installedLocalesCount = 0; - - uprv_free(temp); - } - return TRUE; -} - -static void _load_installedLocales() -{ - UBool localesLoaded; - - UMTX_CHECK(NULL, _installedLocales != NULL, localesLoaded); - - if (localesLoaded == FALSE) { - UResourceBundle *index = NULL; - UResourceBundle installed; - UErrorCode status = U_ZERO_ERROR; - char ** temp; - int32_t i = 0; - int32_t localeCount; - - ures_initStackObject(&installed); - index = ures_openDirect(NULL, _kIndexLocaleName, &status); - ures_getByKey(index, _kIndexTag, &installed, &status); - - if(U_SUCCESS(status)) { - localeCount = ures_getSize(&installed); - temp = (char **) uprv_malloc(sizeof(char*) * (localeCount+1)); - /* Check for null pointer */ - if (temp != NULL) { - ures_resetIterator(&installed); - while(ures_hasNext(&installed)) { - ures_getNextString(&installed, NULL, (const char **)&temp[i++], &status); - } - temp[i] = NULL; - - umtx_lock(NULL); - if (_installedLocales == NULL) - { - _installedLocalesCount = localeCount; - _installedLocales = temp; - temp = NULL; - ucln_common_registerCleanup(UCLN_COMMON_ULOC, uloc_cleanup); - } - umtx_unlock(NULL); - - uprv_free(temp); - } - } - ures_close(&installed); - ures_close(index); - } -} - -U_CAPI const char* U_EXPORT2 -uloc_getAvailable(int32_t offset) -{ - - _load_installedLocales(); - - if (offset > _installedLocalesCount) - return NULL; - return _installedLocales[offset]; -} - -U_CAPI int32_t U_EXPORT2 -uloc_countAvailable() -{ - _load_installedLocales(); - return _installedLocalesCount; -} - -/** - * Returns a list of all language codes defined in ISO 639. This is a pointer - * to an array of pointers to arrays of char. All of these pointers are owned - * by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - */ -U_CAPI const char* const* U_EXPORT2 -uloc_getISOLanguages() -{ - return LANGUAGES; -} - -/** - * Returns a list of all 2-letter country codes defined in ISO 639. This is a - * pointer to an array of pointers to arrays of char. All of these pointers are - * owned by ICU-- do not delete them, and do not write through them. The array is - * terminated with a null pointer. - */ -U_CAPI const char* const* U_EXPORT2 -uloc_getISOCountries() -{ - return COUNTRIES; -} - - -/* this function to be moved into cstring.c later */ -static char gDecimal = 0; - -static /* U_CAPI */ -double -/* U_EXPORT2 */ -_uloc_strtod(const char *start, char **end) { - char *decimal; - char *myEnd; - char buf[30]; - double rv; - if (!gDecimal) { - char rep[5]; - /* For machines that decide to change the decimal on you, - and try to be too smart with localization. - This normally should be just a '.'. */ - sprintf(rep, "%+1.1f", 1.0); - gDecimal = rep[2]; - } - - if(gDecimal == '.') { - return uprv_strtod(start, end); /* fall through to OS */ - } else { - uprv_strncpy(buf, start, 29); - buf[29]=0; - decimal = uprv_strchr(buf, '.'); - if(decimal) { - *decimal = gDecimal; - } else { - return uprv_strtod(start, end); /* no decimal point */ - } - rv = uprv_strtod(buf, &myEnd); - if(end) { - *end = (char*)(start+(myEnd-buf)); /* cast away const (to follow uprv_strtod API.) */ - } - return rv; - } -} - -typedef struct { - float q; - int32_t dummy; /* to avoid uninitialized memory copy from qsort */ - char *locale; -} _acceptLangItem; - -static int32_t U_CALLCONV -uloc_acceptLanguageCompare(const void *context, const void *a, const void *b) -{ - const _acceptLangItem *aa = (const _acceptLangItem*)a; - const _acceptLangItem *bb = (const _acceptLangItem*)b; - - int32_t rc = 0; - if(bb->q < aa->q) { - rc = -1; /* A > B */ - } else if(bb->q > aa->q) { - rc = 1; /* A < B */ - } else { - rc = 0; /* A = B */ - } - - if(rc==0) { - rc = uprv_stricmp(aa->locale, bb->locale); - } - -#if defined(ULOC_DEBUG) - /* fprintf(stderr, "a:[%s:%g], b:[%s:%g] -> %d\n", - aa->locale, aa->q, - bb->locale, bb->q, - rc);*/ -#endif - - return rc; -} - -static ULayoutType -_uloc_getOrientationHelper(const char* localeId, - const char* key, - UErrorCode *status) -{ - ULayoutType result = ULOC_LAYOUT_UNKNOWN; - - if (!U_FAILURE(*status)) { - int32_t length = 0; - char localeBuffer[ULOC_FULLNAME_CAPACITY]; - - uloc_canonicalize(localeId, localeBuffer, sizeof(localeBuffer), status); - - if (!U_FAILURE(*status)) { - const UChar* const value = - _res_getTableStringWithFallback( - NULL, - localeBuffer, - "layout", - NULL, - key, - &length, - status); - - if (!U_FAILURE(*status) && length != 0) { - switch(value[0]) - { - case 0x0062: /* 'b' */ - result = ULOC_LAYOUT_BTT; - break; - case 0x006C: /* 'l' */ - result = ULOC_LAYOUT_LTR; - break; - case 0x0072: /* 'r' */ - result = ULOC_LAYOUT_RTL; - break; - case 0x0074: /* 't' */ - result = ULOC_LAYOUT_TTB; - break; - default: - *status = U_INTERNAL_PROGRAM_ERROR; - break; - } - } - } - } - - return result; -} - -U_DRAFT ULayoutType U_EXPORT2 -uloc_getCharacterOrientation(const char* localeId, - UErrorCode *status) -{ - return _uloc_getOrientationHelper(localeId, "characters", status); -} - -/** - * Get the layout line orientation for the specified locale. - * - * @param localeID locale name - * @param status Error status - * @return an enum indicating the layout orientation for lines. - * @draft ICU 4.0 - */ -U_DRAFT ULayoutType U_EXPORT2 -uloc_getLineOrientation(const char* localeId, - UErrorCode *status) -{ - return _uloc_getOrientationHelper(localeId, "lines", status); -} - -/* -mt-mt, ja;q=0.76, en-us;q=0.95, en;q=0.92, en-gb;q=0.89, fr;q=0.87, iu-ca;q=0.84, iu;q=0.82, ja-jp;q=0.79, mt;q=0.97, de-de;q=0.74, de;q=0.71, es;q=0.68, it-it;q=0.66, it;q=0.63, vi-vn;q=0.61, vi;q=0.58, nl-nl;q=0.55, nl;q=0.53 -*/ - -U_CAPI int32_t U_EXPORT2 -uloc_acceptLanguageFromHTTP(char *result, int32_t resultAvailable, UAcceptResult *outResult, - const char *httpAcceptLanguage, - UEnumeration* availableLocales, - UErrorCode *status) -{ - _acceptLangItem *j; - _acceptLangItem smallBuffer[30]; - char **strs; - char tmp[ULOC_FULLNAME_CAPACITY +1]; - int32_t n = 0; - const char *itemEnd; - const char *paramEnd; - const char *s; - const char *t; - int32_t res; - int32_t i; - int32_t l = (int32_t)uprv_strlen(httpAcceptLanguage); - int32_t jSize; - char *tempstr; /* Use for null pointer check */ - - j = smallBuffer; - jSize = sizeof(smallBuffer)/sizeof(smallBuffer[0]); - if(U_FAILURE(*status)) { - return -1; - } - - for(s=httpAcceptLanguage;s&&*s;) { - while(isspace(*s)) /* eat space at the beginning */ - s++; - itemEnd=uprv_strchr(s,','); - paramEnd=uprv_strchr(s,';'); - if(!itemEnd) { - itemEnd = httpAcceptLanguage+l; /* end of string */ - } - if(paramEnd && paramEnd= bufferLength) { - /* The buffer should never overflow. */ - *err = U_INTERNAL_PROGRAM_ERROR; - } - else { - u_UCharsToChars(s, buffer, resLen + 1); - result = buffer; - } - - ures_close(subtags); - } - } - - return result; -} - -/** - * Append a tag to a buffer, adding the separator if necessary. The buffer - * must be large enough to contain the resulting tag plus any separator - * necessary. The tag must not be a zero-length string. - * - * @param tag The tag to add. - * @param tagLength The length of the tag. - * @param buffer The output buffer. - * @param bufferLength The length of the output buffer. This is an input/ouput parameter. - **/ -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; -} - -/** - * These are the canonical strings for unknown languages, scripts and regions. - **/ -static const char* const unknownLanguage = "und"; -static const char* const unknownScript = "Zzzz"; -static const char* const unknownRegion = "ZZ"; - -/** - * Create a tag string from the supplied parameters. The lang, script and region - * parameters may be NULL pointers. If they are, their corresponding length parameters - * must be less than or equal to 0. - * - * If any of the language, script or region parameters are empty, and the alternateTags - * parameter is not NULL, it will be parsed for potential language, script and region tags - * to be used when constructing the new tag. If the alternateTags parameter is NULL, or - * it contains no language tag, the default tag for the unknown language is used. - * - * If the length of the new string exceeds the capacity of the output buffer, - * the function copies as many bytes to the output buffer as it can, and returns - * the error U_BUFFER_OVERFLOW_ERROR. - * - * If an illegal argument is provided, the function returns the error - * U_ILLEGAL_ARGUMENT_ERROR. - * - * Note that this function can return the warning U_STRING_NOT_TERMINATED_WARNING if - * the tag string fits in the output buffer, but the null terminator doesn't. - * - * @param lang The language tag to use. - * @param langLength The length of the language tag. - * @param script The script tag to use. - * @param scriptLength The length of the script tag. - * @param region The region tag to use. - * @param regionLength The length of the region tag. - * @param trailing Any trailing data to append to the new tag. - * @param trailingLength The length of the trailing data. - * @param alternateTags A string containing any alternate tags. - * @param tag The output buffer. - * @param tagCapacity The capacity of the output buffer. - * @param err A pointer to a UErrorCode for error reporting. - * @return The length of the tag string, which may be greater than tagCapacity, or -1 on error. - **/ -static int32_t 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, - char* tag, - int32_t tagCapacity, - UErrorCode* err) { - - if (U_FAILURE(*err)) { - goto error; - } - else if (tag == NULL || - tagCapacity <= 0 || - langLength >= ULOC_LANG_CAPACITY || - scriptLength >= ULOC_SCRIPT_CAPACITY || - regionLength >= ULOC_COUNTRY_CAPACITY) { - goto error; - } - else { - /** - * ULOC_FULLNAME_CAPACITY will provide enough capacity - * that we can build a string that contains the language, - * script and region code without worrying about overrunning - * the user-supplied buffer. - **/ - char tagBuffer[ULOC_FULLNAME_CAPACITY]; - int32_t tagLength = 0; - int32_t capacityRemaining = tagCapacity; - UBool regionAppended = FALSE; - - if (langLength > 0) { - appendTag( - lang, - langLength, - tagBuffer, - &tagLength); - } - else if (alternateTags == NULL) { - /* - * Append the value for an unknown language, if - * we found no language. - */ - appendTag( - unknownLanguage, - uprv_strlen(unknownLanguage), - tagBuffer, - &tagLength); - } - else { - /* - * Parse the alternateTags string for the language. - */ - 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) { - /* - * Append the value for an unknown language, if - * we found no language. - */ - appendTag( - unknownLanguage, - uprv_strlen(unknownLanguage), - tagBuffer, - &tagLength); - } - else { - appendTag( - alternateLang, - alternateLangLength, - tagBuffer, - &tagLength); - } - } - - if (scriptLength > 0) { - appendTag( - script, - scriptLength, - tagBuffer, - &tagLength); - } - else if (alternateTags != NULL) { - /* - * Parse the alternateTags string for the script. - */ - 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) { - /* - * Parse the alternateTags string for the region. - */ - 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; - } - } - - { - const int32_t toCopy = - tagLength >= tagCapacity ? tagCapacity : tagLength; - - /** - * Copy the partial tag from our internal buffer to the supplied - * target. - **/ - uprv_memcpy( - tag, - tagBuffer, - toCopy); - - capacityRemaining -= toCopy; - } - - if (trailingLength > 0) { - if (capacityRemaining > 0 && !regionAppended) { - tag[tagLength++] = '_'; - --capacityRemaining; - } - - if (capacityRemaining > 0) { - /* - * Copy the trailing data into the supplied buffer. Use uprv_memmove, since we - * don't know if the user-supplied buffers overlap. - */ - const int32_t toCopy = - trailingLength >= capacityRemaining ? capacityRemaining : trailingLength; - - uprv_memmove( - &tag[tagLength], - trailing, - toCopy); - } - } - - tagLength += trailingLength; - - return u_terminateChars( - tag, - tagCapacity, - tagLength, - err); - } - -error: - - /** - * An overflow indicates the locale ID passed in - * is ill-formed. If we got here, and there was - * no previous error, it's an implicit overflow. - **/ - if (*err == U_BUFFER_OVERFLOW_ERROR || - U_SUCCESS(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - return -1; -} - -/** - * Create a tag string from the supplied parameters. The lang, script and region - * parameters may be NULL pointers. If they are, their corresponding length parameters - * must be less than or equal to 0. If the lang parameter is an empty string, the - * default value for an unknown language is written to the output buffer. - * - * If the length of the new string exceeds the capacity of the output buffer, - * the function copies as many bytes to the output buffer as it can, and returns - * the error U_BUFFER_OVERFLOW_ERROR. - * - * If an illegal argument is provided, the function returns the error - * U_ILLEGAL_ARGUMENT_ERROR. - * - * @param lang The language tag to use. - * @param langLength The length of the language tag. - * @param script The script tag to use. - * @param scriptLength The length of the script tag. - * @param region The region tag to use. - * @param regionLength The length of the region tag. - * @param trailing Any trailing data to append to the new tag. - * @param trailingLength The length of the trailing data. - * @param tag The output buffer. - * @param tagCapacity The capacity of the output buffer. - * @param err A pointer to a UErrorCode for error reporting. - * @return The length of the tag string, which may be greater than tagCapacity. - **/ -static int32_t 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, - char* tag, - int32_t tagCapacity, - UErrorCode* err) -{ - return createTagStringWithAlternates( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - trailing, - trailingLength, - NULL, - tag, - tagCapacity, - err); -} - -/** - * Parse the language, script, and region subtags from a tag string, and copy the - * results into the corresponding output parameters. The buffers are null-terminated, - * unless overflow occurs. - * - * The langLength, scriptLength, and regionLength parameters are input/output - * parameters, and must contain the capacity of their corresponding buffers on - * input. On output, they will contain the actual length of the buffers, not - * including the null terminator. - * - * If the length of any of the output subtags exceeds the capacity of the corresponding - * buffer, the function copies as many bytes to the output buffer as it can, and returns - * the error U_BUFFER_OVERFLOW_ERROR. It will not parse any more subtags once overflow - * occurs. - * - * If an illegal argument is provided, the function returns the error - * U_ILLEGAL_ARGUMENT_ERROR. - * - * @param localeID The locale ID to parse. - * @param lang The language tag buffer. - * @param langLength The length of the language tag. - * @param script The script tag buffer. - * @param scriptLength The length of the script tag. - * @param region The region tag buffer. - * @param regionLength The length of the region tag. - * @param err A pointer to a UErrorCode for error reporting. - * @return The number of chars of the localeID parameter consumed. - **/ -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 = _getLanguage(position, lang, *langLength, &position); - u_terminateChars(lang, *langLength, subtagLength, err); - - /* - * Note that we explicit consider U_STRING_NOT_TERMINATED_WARNING - * to be an error, because it indicates the user-supplied tag is - * not well-formed. - */ - if(*err != U_ZERO_ERROR) { - goto error; - } - - *langLength = subtagLength; - - /* - * If no language was present, use the value of unknownLanguage - * instead. Otherwise, move past any separator. - */ - if (*langLength == 0) { - uprv_strcpy( - lang, - unknownLanguage); - *langLength = uprv_strlen(lang); - } - else if (_isIDSeparator(*position)) { - ++position; - } - - subtagLength = _getScript(position, script, *scriptLength, &position); - u_terminateChars(script, *scriptLength, subtagLength, err); - - if(*err != U_ZERO_ERROR) { - goto error; - } - - *scriptLength = subtagLength; - - if (*scriptLength > 0) { - if (uprv_strnicmp(script, unknownScript, *scriptLength) == 0) { - /** - * If the script part is the "unknown" script, then don't return it. - **/ - *scriptLength = 0; - } - - /* - * Move past any separator. - */ - if (_isIDSeparator(*position)) { - ++position; - } - } - - subtagLength = _getCountry(position, region, *regionLength, &position); - u_terminateChars(region, *regionLength, subtagLength, err); - - if(*err != U_ZERO_ERROR) { - goto error; - } - - *regionLength = subtagLength; - - if (*regionLength > 0) { - if (uprv_strnicmp(region, unknownRegion, *regionLength) == 0) { - /** - * If the region part is the "unknown" region, then don't return it. - **/ - *regionLength = 0; - } - } - -exit: - - return (int32_t)(position - localeID); - -error: - - /** - * If we get here, we have no explicit error, it's the result of an - * illegal argument. - **/ - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - goto exit; -} - -static int32_t 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, - char* tag, - int32_t tagCapacity, - UErrorCode* err) -{ - /** - * ULOC_FULLNAME_CAPACITY will provide enough capacity - * that we can build a string that contains the language, - * script and region code without worrying about overrunning - * the user-supplied buffer. - **/ - char tagBuffer[ULOC_FULLNAME_CAPACITY]; - char likelySubtagsBuffer[ULOC_FULLNAME_CAPACITY]; - int32_t tagBufferLength = 0; - - if(U_FAILURE(*err)) { - goto error; - } - - /** - * Try the language with the script and region first. - **/ - if (scriptLength > 0 && regionLength > 0) { - - const char* likelySubtags = NULL; - - tagBufferLength = createTagString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer, - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - return createTagStringWithAlternates( - NULL, - 0, - NULL, - 0, - NULL, - 0, - variants, - variantsLength, - likelySubtags, - tag, - tagCapacity, - err); - } - } - - /** - * Try the language with just the script. - **/ - if (scriptLength > 0) { - - const char* likelySubtags = NULL; - - tagBufferLength = createTagString( - lang, - langLength, - script, - scriptLength, - NULL, - 0, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer, - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - return createTagStringWithAlternates( - NULL, - 0, - NULL, - 0, - region, - regionLength, - variants, - variantsLength, - likelySubtags, - tag, - tagCapacity, - err); - } - } - - /** - * Try the language with just the region. - **/ - if (regionLength > 0) { - - const char* likelySubtags = NULL; - - createTagString( - lang, - langLength, - NULL, - 0, - region, - regionLength, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer, - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - return createTagStringWithAlternates( - NULL, - 0, - script, - scriptLength, - NULL, - 0, - variants, - variantsLength, - likelySubtags, - tag, - tagCapacity, - err); - } - } - - /** - * Finally, try just the language. - **/ - { - const char* likelySubtags = NULL; - - createTagString( - lang, - langLength, - NULL, - 0, - NULL, - 0, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - likelySubtags = - findLikelySubtags( - tagBuffer, - likelySubtagsBuffer, - sizeof(likelySubtagsBuffer), - err); - if(U_FAILURE(*err)) { - goto error; - } - - if (likelySubtags != NULL) { - /* Always use the language tag from the - maximal string, since it may be more - specific than the one provided. */ - return createTagStringWithAlternates( - NULL, - 0, - script, - scriptLength, - region, - regionLength, - variants, - variantsLength, - likelySubtags, - tag, - tagCapacity, - err); - } - } - - return u_terminateChars( - tag, - tagCapacity, - 0, - err); - -error: - - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - return -1; -} - -static int32_t -_uloc_addLikelySubtags(const char* localeID, - char* maximizedLocaleID, - int32_t maximizedLocaleIDCapacity, - 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; - int32_t resultLength = 0; - - if(U_FAILURE(*err)) { - goto error; - } - else if (localeID == NULL || - maximizedLocaleID == NULL || - maximizedLocaleIDCapacity <= 0) { - goto error; - } - - trailingIndex = parseTagString( - localeID, - lang, - &langLength, - script, - &scriptLength, - region, - ®ionLength, - err); - if(U_FAILURE(*err)) { - /* Overflow indicates an illegal argument error */ - if (*err == U_BUFFER_OVERFLOW_ERROR) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - goto error; - } - - /* Find the length of the trailing portion. */ - trailing = &localeID[trailingIndex]; - trailingLength = uprv_strlen(trailing); - - resultLength = - createLikelySubtagsString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - trailing, - trailingLength, - maximizedLocaleID, - maximizedLocaleIDCapacity, - err); - - if (resultLength == 0) { - const int32_t localIDLength = - uprv_strlen(localeID); - - /* - * If we get here, we need to return localeID. - */ - uprv_memcpy( - maximizedLocaleID, - localeID, - localIDLength <= maximizedLocaleIDCapacity ? - localIDLength : maximizedLocaleIDCapacity); - - resultLength = - u_terminateChars( - maximizedLocaleID, - maximizedLocaleIDCapacity, - localIDLength, - err); - } - - return resultLength; - -error: - - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - return -1; -} - -static int32_t -_uloc_minimizeSubtags(const char* localeID, - char* minimizedLocaleID, - int32_t minimizedLocaleIDCapacity, - UErrorCode* err) -{ - /** - * ULOC_FULLNAME_CAPACITY will provide enough capacity - * that we can build a string that contains the language, - * script and region code without worrying about overrunning - * the user-supplied buffer. - **/ - char maximizedTagBuffer[ULOC_FULLNAME_CAPACITY]; - int32_t maximizedTagBufferLength = sizeof(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 || - minimizedLocaleID == NULL || - minimizedLocaleIDCapacity <= 0) { - goto error; - } - - trailingIndex = - parseTagString( - localeID, - lang, - &langLength, - script, - &scriptLength, - region, - ®ionLength, - err); - if(U_FAILURE(*err)) { - - /* Overflow indicates an illegal argument error */ - if (*err == U_BUFFER_OVERFLOW_ERROR) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - goto error; - } - - /* Find the spot where the variants begin, if any. */ - trailing = &localeID[trailingIndex]; - trailingLength = uprv_strlen(trailing); - - createTagString( - lang, - langLength, - script, - scriptLength, - region, - regionLength, - NULL, - 0, - maximizedTagBuffer, - maximizedTagBufferLength, - err); - if(U_FAILURE(*err)) { - goto error; - } - - /** - * First, we need to first get the maximization - * from AddLikelySubtags. - **/ - maximizedTagBufferLength = - uloc_addLikelySubtags( - maximizedTagBuffer, - maximizedTagBuffer, - maximizedTagBufferLength, - err); - - if(U_FAILURE(*err)) { - goto error; - } - - /** - * Start first with just the language. - **/ - { - char tagBuffer[ULOC_FULLNAME_CAPACITY]; - - const int32_t tagBufferLength = - createLikelySubtagsString( - lang, - langLength, - NULL, - 0, - NULL, - 0, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - - if(U_FAILURE(*err)) { - goto error; - } - else if (uprv_strnicmp( - maximizedTagBuffer, - tagBuffer, - tagBufferLength) == 0) { - - return createTagString( - lang, - langLength, - NULL, - 0, - NULL, - 0, - trailing, - trailingLength, - minimizedLocaleID, - minimizedLocaleIDCapacity, - err); - } - } - - /** - * Next, try the language and region. - **/ - if (regionLength > 0) { - - char tagBuffer[ULOC_FULLNAME_CAPACITY]; - - const int32_t tagBufferLength = - createLikelySubtagsString( - lang, - langLength, - NULL, - 0, - region, - regionLength, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - - if(U_FAILURE(*err)) { - goto error; - } - else if (uprv_strnicmp( - maximizedTagBuffer, - tagBuffer, - tagBufferLength) == 0) { - - return createTagString( - lang, - langLength, - NULL, - 0, - region, - regionLength, - trailing, - trailingLength, - minimizedLocaleID, - minimizedLocaleIDCapacity, - err); - } - } - - /** - * Finally, try the language and script. This is our last chance, - * since trying with all three subtags would only yield the - * maximal version that we already have. - **/ - if (scriptLength > 0 && regionLength > 0) { - char tagBuffer[ULOC_FULLNAME_CAPACITY]; - - const int32_t tagBufferLength = - createLikelySubtagsString( - lang, - langLength, - script, - scriptLength, - NULL, - 0, - NULL, - 0, - tagBuffer, - sizeof(tagBuffer), - err); - - if(U_FAILURE(*err)) { - goto error; - } - else if (uprv_strnicmp( - maximizedTagBuffer, - tagBuffer, - tagBufferLength) == 0) { - - return createTagString( - lang, - langLength, - script, - scriptLength, - NULL, - 0, - trailing, - trailingLength, - minimizedLocaleID, - minimizedLocaleIDCapacity, - err); - } - } - - { - /** - * If we got here, return the locale ID parameter. - **/ - const int32_t localeIDLength = uprv_strlen(localeID); - - uprv_memcpy( - minimizedLocaleID, - localeID, - localeIDLength <= minimizedLocaleIDCapacity ? - localeIDLength : minimizedLocaleIDCapacity); - - return u_terminateChars( - minimizedLocaleID, - minimizedLocaleIDCapacity, - localeIDLength, - err); - } - -error: - - if (!U_FAILURE(*err)) { - *err = U_ILLEGAL_ARGUMENT_ERROR; - } - - return -1; - - -} - -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_DRAFT int32_t U_EXPORT2 -uloc_addLikelySubtags(const char* localeID, - char* maximizedLocaleID, - int32_t maximizedLocaleIDCapacity, - UErrorCode* err) -{ - char localeBuffer[ULOC_FULLNAME_CAPACITY]; - - if (!do_canonicalize( - localeID, - localeBuffer, - sizeof(localeBuffer), - err)) { - return -1; - } - else { - return _uloc_addLikelySubtags( - localeBuffer, - maximizedLocaleID, - maximizedLocaleIDCapacity, - err); - } -} - -U_DRAFT int32_t U_EXPORT2 -uloc_minimizeSubtags(const char* localeID, - char* minimizedLocaleID, - int32_t minimizedLocaleIDCapacity, - UErrorCode* err) -{ - char localeBuffer[ULOC_FULLNAME_CAPACITY]; - - if (!do_canonicalize( - localeID, - localeBuffer, - sizeof(localeBuffer), - err)) { - return -1; - } - else { - return _uloc_minimizeSubtags( - localeBuffer, - minimizedLocaleID, - minimizedLocaleIDCapacity, - err); - } -} - /*eof*/