blob: 2d781abfb2dcf73c585f0f16896070724e93530c [file] [log] [blame]
/*
**********************************************************************
* Copyright (C) 1997-2001, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
* File ULOC.CPP
*
* Modification History:
*
* Date Name Description
* 04/01/97 aliu Creation.
* 08/21/98 stephen JDK 1.2 sync
* 12/08/98 rtg New Locale implementation and C API
* 03/15/99 damiba overhaul.
* 04/06/99 stephen changed setDefault() to realloc and copy
* 06/14/99 stephen Changed calls to ures_open for new params
* 07/21/99 stephen Modified setDefault() to propagate to C++
*****************************************************************************/
/*
POSIX's locale format, from putil.c: [no spaces]
ll [ _CC ] [ . MM ] [ @ VV]
l = lang, C = ctry, M = charmap, V = variant
*/
#include "unicode/uloc.h"
#include "unicode/locid.h"
#include "unicode/utypes.h"
#include "uresimp.h"
#include "unicode/uchar.h"
#include "umutex.h"
#include "cstring.h"
#include "unicode/ustring.h"
#include "cmemory.h"
/****************************************************************************
Global variable and type definitions
*****************************************************************************/
/* UnicodeString stuff */
typedef struct UnicodeString UnicodeString;
U_CFUNC int32_t T_UnicodeString_length(const UnicodeString *s);
U_CAPI int32_t
T_UnicodeString_extract(const UnicodeString *s, char *dst);
/* Locale stuff from locid.cpp */
U_CAPI void locale_set_default(const char *id);
/* These strings describe the resources we attempt to load from
the locale ResourceBundle data file.*/
static const char* _kLocaleID = "LocaleID";
static const char* _kLanguages = "Languages";
static const char* _kCountries = "Countries";
#if 0
/* We don't use these resources currently */
static const char* _kLocaleString = "LocaleString";
static const char* _kShortLanguage = "ShortLanguage";
static const char* _kShortCountry = "ShortCountry";
#endif
#define TEMPBUFSIZE 8
/*Some static strings needed in the getDisplay* functions*/
static const UChar openParen[] = { (UChar)0x0020 /* space */, (UChar)0x0028 /* ( */, (UChar)0x0000};
static const UChar comma[] = { (UChar)0x002C /* , */, (UChar)0x0020 /* space */, (UChar)0x0000};
static const UChar closeParen[] = { (UChar)0x0029 /* ) */, (UChar)0x0000};
static char _defaultLocale[ULOC_FULLNAME_CAPACITY] = "en_US";
static UBool _emptyDefaultLocale = TRUE;
static char** _installedLocales = NULL;
static int32_t _installedLocalesCount = 0;
static const char _languages[] =
"aa\0ab\0af\0am\0ar\0as\0ay\0az\0"
"ba\0be\0bg\0bh\0bi\0bn\0bo\0br\0"
"ca\0co\0cs\0cy\0da\0de\0dz\0"
"el\0en\0eo\0es\0et\0eu\0fa\0fi\0fj\0fo\0fr\0fy\0"
"ga\0gd\0gl\0gn\0gu\0ha\0he\0hi\0hr\0hu\0hy\0"
"ia\0id\0ie\0ik\0in\0is\0it\0iu\0iw\0"
"ja\0ji\0jw\0ka\0kk\0kl\0km\0kn\0ko\0ks\0ku\0ky\0"
"la\0ln\0lo\0lt\0lv\0"
"mg\0mi\0mk\0ml\0mn\0mo\0mr\0ms\0mt\0my\0"
"na\0ne\0nl\0no\0oc\0om\0or\0"
"pa\0pl\0ps\0pt\0qu\0rm\0rn\0ro\0ru\0rw\0"
"sa\0sd\0sg\0sh\0si\0sk\0sl\0sm\0sn\0so\0sq\0sr\0ss\0st\0su\0sv\0sw\0"
"ta\0te\0tg\0th\0ti\0tk\0tl\0tn\0to\0tr\0ts\0tt\0tw\0"
"ug\0uk\0ur\0uz\0vi\0vo\0wo\0xh\0yi\0yo\0za\0zh\0zu";
/* This list MUST be in sorted order, and MUST contain only two-letter codes! */
static const char _languages3[] =
"aar\0abk\0afr\0amh\0ara\0asm\0aym\0aze\0"
"bak\0bel\0bul\0bih\0bis\0ben\0bod\0bre\0"
"cat\0cos\0ces\0cym\0dan\0deu\0dzo\0"
"ell\0eng\0epo\0spa\0est\0eus\0fas\0fin\0fij\0fao\0fra\0fry\0"
"gai\0gdh\0glg\0grn\0guj\0hau\0heb\0hin\0hrv\0hun\0hye\0"
"ina\0ind\0ile\0ipk\0ind\0isl\0ita\0iku\0heb\0"
"jpn\0yid\0jaw\0kat\0kaz\0kal\0khm\0kan\0kor\0kas\0kur\0kir\0"
"lat\0lin\0lao\0lit\0lav\0"
"mlg\0mri\0mkd\0mal\0mon\0mol\0mar\0msa\0mlt\0mya\0"
"nau\0nep\0nld\0nor\0oci\0orm\0ori\0"
"pan\0pol\0pus\0por\0que\0roh\0run\0ron\0rus\0kin\0"
"san\0snd\0sag\0srp\0sin\0slk\0slv\0smo\0sna\0som\0sqi\0srp\0ssw\0sot\0sun\0swe\0swa\0"
"tat\0tel\0tgk\0tha\0tir\0tuk\0tgl\0tsn\0ton\0tur\0tsn\0tat\0twi\0"
"uig\0ukr\0urd\0uzb\0vie\0vol\0wol\0xho\0yid\0yor\0zha\0zho\0zul";
/* This list MUST contain a three-letter code for every two-letter code in the
list above, and they MUST ne in the same order (i.e., the same language must
be in the same place in both lists)! */
static const char _countries[] =
"AD\0AE\0AF\0AG\0AI\0AL\0AM\0AN\0AO\0AQ\0AR\0AS\0AT\0AU\0AW\0AZ\0"
"BA\0BB\0BD\0BE\0BF\0BG\0BH\0BI\0BJ\0BM\0BN\0BO\0BR\0BS\0BT\0BV\0BW\0BY\0BZ\0"
"CA\0CC\0CF\0CG\0CH\0CI\0CK\0CL\0CM\0CN\0CO\0CR\0CU\0CV\0CX\0CY\0CZ\0"
"DE\0DJ\0DK\0DM\0DO\0DZ\0EC\0EE\0EG\0EH\0ER\0ES\0ET\0"
"FI\0FJ\0FK\0FM\0FO\0FR\0FX\0"
"GA\0GB\0GD\0GE\0GF\0GH\0GI\0GL\0GM\0GN\0GP\0GQ\0GR\0GS\0GT\0GU\0GW\0GY\0"
"HK\0HM\0HN\0HR\0HT\0HU\0ID\0IE\0IL\0IN\0IO\0IQ\0IR\0IS\0IT\0"
"JM\0JO\0JP\0KE\0KG\0KH\0KI\0KM\0KN\0KP\0KR\0KW\0KY\0KZ\0"
"LA\0LB\0LC\0LI\0LK\0LR\0LS\0LT\0LU\0LV\0LY\0"
"MA\0MC\0MD\0MG\0MH\0MK\0ML\0MM\0MN\0MO\0MP\0MQ\0MR\0MS\0MT\0MU\0MV\0MW\0MX\0MY\0MZ\0"
"NA\0NC\0NE\0NF\0NG\0NI\0NL\0NO\0NP\0NR\0NU\0NZ\0OM\0"
"PA\0PE\0PF\0PG\0PH\0PK\0PL\0PM\0PN\0PR\0PT\0PW\0PY\0QA\0RE\0RO\0RU\0RW\0"
"SA\0SB\0SC\0SD\0SE\0SG\0SH\0SI\0SJ\0SK\0SL\0SM\0SN\0SO\0SR\0ST\0SV\0SY\0SZ\0"
"TC\0TD\0TF\0TG\0TH\0TJ\0TK\0TM\0TN\0TO\0TP\0TR\0TT\0TV\0TW\0TZ\0"
"UA\0UG\0UM\0US\0UY\0UZ\0VA\0VC\0VE\0VG\0VI\0VN\0VU\0"
"WF\0WS\0YE\0YT\0YU\0ZA\0ZM\0ZR\0ZW";
/* This list MUST be in sorted order, and MUST contain only two-letter codes! */
static const char _countries3[] =
"AND\0ARE\0AFG\0ATG\0AIA\0ALB\0ARM\0ANT\0AGO\0ATA\0ARG\0ASM\0AUT\0AUS\0ABW\0AZE\0"
"BIH\0BRB\0BGD\0BEL\0BFA\0BGR\0BHR\0BDI\0BEN\0BMU\0BRN\0BOL\0BRA\0BHS\0BTN\0BVT\0BWA"
"\0BLR\0BLZ\0"
"CAN\0CCK\0CAF\0COG\0CHE\0CIV\0COK\0CHL\0CMR\0CHN\0COL\0CRI\0CUB\0CPV\0CXR\0CYP\0CZE\0"
"DEU\0DJI\0DNK\0DMA\0DOM\0DZA\0ECU\0EST\0EGY\0ESH\0ERI\0ESP\0ETH\0"
"FIN\0FJI\0FLK\0FSM\0FRO\0FRA\0FXX\0"
"GAB\0GBR\0GRD\0GEO\0GUF\0GHA\0GIB\0GRL\0GMB\0GIN\0GLP\0GNQ\0GRC\0SGS\0GTM\0GUM"
"\0GNB\0GUY\0"
"HKG\0HMD\0HND\0HRV\0HTI\0HUN\0IDN\0IRL\0ISR\0IND\0IOT\0IRQ\0IRN\0ISL\0ITA\0"
"JAM\0JOR\0JPN\0KEN\0KGZ\0KHM\0KIR\0COM\0KNA\0PRK\0KOR\0KWT\0CYM\0KAZ\0"
"LAO\0LBN\0LCA\0LIE\0LKA\0LBR\0LSO\0LTU\0LUX\0LVA\0LBY\0"
"MAR\0MCO\0MDA\0MDG\0MHL\0MKD\0MLI\0MMR\0MNG\0MAC\0MNP\0MTQ\0MRT\0MSR\0MLT\0MUS\0"
"MDV\0MWI\0MEX\0MYS\0MOZ\0"
"NAM\0NCL\0NER\0NFK\0NGA\0NIC\0NLD\0NOR\0NPL\0NRU\0NIU\0NZL\0OMN\0"
"PAN\0PER\0PYF\0PNG\0PHL\0PAK\0POL\0SPM\0PCN\0PRI\0PRT\0PLW\0PRY\0QAT\0REU\0ROM"
"\0RUS\0RWA\0"
"SAU\0SLB\0SYC\0SDN\0SWE\0SGP\0SHN\0SVN\0SJM\0SVK\0SLE\0SMR\0SEN\0SOM\0SUR\0STP"
"\0SLV\0SYR\0SWZ\0"
"TCA\0TCD\0ATF\0TGO\0THA\0TJK\0TKL\0TKM\0TUN\0TON\0TMP\0TUR\0TTO\0TUV\0TWN\0TZA\0"
"UKR\0UGA\0UMI\0USA\0URY\0UZB\0VAT\0VCT\0VEN\0VGB\0VIR\0VNM\0VUT\0"
"WLF\0WSM\0YEM\0MYT\0YUG\0ZAF\0ZMB\0ZAR\0ZWE";
/* This list MUST contain a three-letter code for every two-letter code in
the above list, and they MUST be listed in the same order! */
static char** _isoLanguages = NULL;
static char** _isoCountries = NULL;
/*******************************************************************************
Implementation function definitions
*******************************************************************************/
static int16_t _findIndex(const char* list, int32_t listLength, const char* key);
/*Works like strchr with '_' pr '-'*/
static const char* _findCharSeparator(const char* string);
/*Lazy evaluated the list of installed locales*/
static void _lazyEvaluate_installedLocales(void);
/*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)
'x-' or 'i-' */
#define _isIDPrefix(s) (_isPrefixLetter(s[0])&&_isIDSeparator(s[1]))
/* Dot terminates it because of POSIX form where dot precedes the codepage
* except for variant
*/
#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
/*******************************************************************************
API function definitions
*******************************************************************************/
static const char* _findCharSeparator(const char* string)
{
if (string == NULL)
return NULL;
/*Keeps iterating until an ID separator is found*/
while (*string && !_isIDSeparator(*string))
string++;
if (*string)
return string;
else
return NULL;
}
static int16_t _findIndex(const char* list, int32_t listLength, const char* key)
{
const char* anchor = list;
const char* listEnd = anchor + listLength;
UBool found = FALSE;
int tokenSize = uprv_strlen(list)+1; /*gets the size of the tokens*/
while (!found && list<listEnd)
{
if (uprv_strcmp(key, list) == 0)
{
found = TRUE;
break;
}
list += tokenSize;
}
if (found == TRUE)
return (int16_t)((list - anchor)/tokenSize);
else
return -1;
}
const char* uloc_getDefault()
{
UErrorCode err = U_ZERO_ERROR;
/*lazy evaluates _defaultLocale*/
if (_emptyDefaultLocale)
{
uloc_setDefault(NULL, &err);
}
return _defaultLocale;
}
void uloc_setDefault(const char* newDefaultLocale,
UErrorCode* err)
{
if (U_FAILURE(*err))
return;
/* the error code isn't currently used for anything by this function*/
if (newDefaultLocale == NULL)
{
newDefaultLocale = uprv_getDefaultLocaleID();
}
umtx_lock(NULL);
uprv_strncpy(_defaultLocale, newDefaultLocale, ULOC_FULLNAME_CAPACITY - 1);
_defaultLocale[ULOC_FULLNAME_CAPACITY - 1] = 0;
_emptyDefaultLocale = FALSE;
umtx_unlock(NULL);
/* propagate change to C++ */
locale_set_default(newDefaultLocale);
}
int32_t uloc_getParent(const char* localeID,
char* parent,
int32_t parentCapacity,
UErrorCode* err)
{
int i=0;
int offset = 0;
int count = 0;
if (U_FAILURE(*err))
return 0;
if (localeID == NULL)
localeID = uloc_getDefault();
while (localeID[offset]&&(count < 2))
{
if (_isIDSeparator(localeID[offset++]))
count++;
}
/*finds the second IDSeparator*/
while (offset && !_isIDSeparator(localeID[offset]))
{
offset--;
}
/*Loop updates i to the size of the parent
but only copies into the buffer as much as the buffer can bare*/
while (i < offset)
{
if (parentCapacity > i)
parent[i] = localeID[i];
i++;
}
/*Sets the error code on case of need*/
if (i >= parentCapacity )
{
*err = U_BUFFER_OVERFLOW_ERROR;
}
if (parentCapacity>0)
parent[uprv_min(i,parentCapacity-1)] = '\0';
return i+1;
}
int32_t
uloc_getLanguage(const char* localeID,
char* language,
int32_t languageCapacity,
UErrorCode* err)
{
int i=0;
if (U_FAILURE(*err))
return 0;
if (localeID == NULL)
localeID = uloc_getDefault();
/* If it starts with i- or x- */
if(_isIDPrefix(localeID))
{
if(languageCapacity > i)
{
language[i] = (char)uprv_tolower(*localeID);
}
i++;
localeID++;
if(languageCapacity > i)
{
language[i] = '-';
}
i++;
localeID++;
}
/*Loop updates i to the size of the language
but only copies into the buffer as much as the buffer can bare*/
while (!_isTerminator(*localeID) && !_isIDSeparator(*localeID))
{
if (languageCapacity > i)
language[i] = (char)uprv_tolower(*localeID);
i++;
localeID++;
}
if (i >= languageCapacity )
{
*err = U_BUFFER_OVERFLOW_ERROR;
}
if (languageCapacity > 0)
{
language[uprv_min(i,languageCapacity-1)] = '\0';
}
return i+1;
}
int32_t uloc_getCountry(const char* localeID,
char* country,
int32_t countryCapacity,
UErrorCode* err)
{
int i=0;
if (U_FAILURE(*err))
return 0;
if (localeID == NULL)
localeID = uloc_getDefault();
/* skip over i- or x- */
if(_isIDPrefix(localeID))
{
localeID += 2;
}
localeID = _findCharSeparator(localeID);
/*Loop updates i to the size of the language
but only copies into the buffer as much as the buffer can bare*/
if (localeID)
{
++localeID;
while (!_isTerminator(*localeID) && !_isIDSeparator(*localeID))
{
if (countryCapacity > i)
country[i] = (char)uprv_toupper(*localeID);
i++;
localeID++;
}
}
if (i >= countryCapacity )
{
*err = U_BUFFER_OVERFLOW_ERROR;
}
if (countryCapacity > 0) {
country[uprv_min(i,countryCapacity-1)] = '\0';
}
return i+1;
}
int32_t uloc_getVariant(const char* localeID,
char* variant,
int32_t variantCapacity,
UErrorCode* err)
{
int i=0;
const char *p = localeID;
if (U_FAILURE(*err))
return 0;
if (localeID == NULL)
{
localeID = uloc_getDefault();
}
/* skip over i- or x- */
if(_isIDPrefix(localeID))
{
localeID += 2;
}
localeID = _findCharSeparator(localeID);
if (localeID)
{
localeID = _findCharSeparator(++localeID);
}
if (localeID)
{
++localeID;
/*Loop updates i to the size of the language
but only copies into the buffer as much as the buffer can bear*/
while (!_isTerminator(*localeID))
{
if (variantCapacity > i)
variant[i] = (char)uprv_toupper(*localeID);
i++;
localeID++;
}
}
/* But wait, there's more!
**IFF** no variant was otherwise found, take one from @...
*/
if ( (i == 0) && /* Found nothing (zero chars copied) */
(localeID = uprv_strrchr(p, '@')) != NULL)
{
localeID++; /* point after the @ */
/* Note that we will stop at a period if the user accidentally
put a period after the @ sign */
/* repeat above copying loop */
while (!_isTerminator(*localeID))
{
if (variantCapacity > i)
variant[i] = (char)uprv_toupper(*localeID);
i++;
localeID++;
}
}
if (i >= variantCapacity )
{
*err = U_BUFFER_OVERFLOW_ERROR;
}
if (variantCapacity>0) {
variant[uprv_min(i,variantCapacity-1)] = '\0';
}
return i+1;
}
int32_t uloc_getName(const char* localeID,
char* name,
int32_t nameCapacity,
UErrorCode* err)
{
int i= 0; /* total required size */
int n= 0; /* How much has been copied currently */
int varSze = 0; /* How big the variant is */
int cntSze = 0; /* How big the country is */
UErrorCode int_err = U_ZERO_ERROR;
if (U_FAILURE(*err)) return 0;
/*First we preflight the components in order to ensure a valid return value*/
if (localeID == NULL) localeID = uloc_getDefault();
cntSze = uloc_getCountry(localeID,
NULL ,
0,
&int_err);
int_err = U_ZERO_ERROR;
varSze = uloc_getVariant(localeID,
NULL ,
0,
&int_err);
int_err = U_ZERO_ERROR;
i = uloc_getLanguage(localeID,
NULL,
0,
&int_err);
/*Adjust for the zero terminators*/
--varSze;
--cntSze;
/* i is still languagesize+1 for the terminator */
/* Add space for underscores */
if (varSze)
{
i+= 2; /* if theres a variant, it will ALWAYS contain two underscores. */
}
else
{
if (cntSze)
{
i++; /* Otherwise - only language _ country. */
}
}
/* Update i (total req'd size) */
i += cntSze + varSze;
if(nameCapacity) /* If size is zero, skip the actual copy */
{
/* Now, the real copying */
int_err = U_ZERO_ERROR;
uloc_getLanguage(localeID,
name,
nameCapacity /* -(n=0) */,
&int_err);
n += uprv_strlen(name);
/*We fill in the users buffer*/
if ((n<nameCapacity) && cntSze)
{
if(U_SUCCESS(int_err))
{
name[n++] = '_';
}
uloc_getCountry(localeID,
name + n,
nameCapacity - n,
&int_err);
n += cntSze;
if (varSze && (n<nameCapacity))
{
if(U_SUCCESS(int_err))
{
name[n++] = '_';
}
uloc_getVariant(localeID,
name + n,
nameCapacity - n,
&int_err);
}
}
else if((n<nameCapacity) && varSze)
{
if (U_SUCCESS(int_err))
{
name[n++] = '_';
if(n<nameCapacity)
name[n++] = '_';
}
uloc_getVariant(localeID,
name + n,
nameCapacity - n,
&int_err);
}
/* Tie it off */
name[uprv_min(i,nameCapacity-1)] = '\0';
} /* end (if nameCapacity > 0) */
*err = int_err;
return i;
}
const char* uloc_getISO3Language(const char* localeID)
{
int16_t offset;
char lang[TEMPBUFSIZE];
UErrorCode err = U_ZERO_ERROR;
if (localeID == NULL)
{
localeID = uloc_getDefault();
}
uloc_getLanguage(localeID, lang, TEMPBUFSIZE, &err);
if (U_FAILURE(err))
return "";
offset = _findIndex(_languages, sizeof(_languages),lang);
if (offset < 0)
return "";
return &(_languages3[offset * 4]);
}
const char* uloc_getISO3Country(const char* localeID)
{
int16_t offset;
char cntry[TEMPBUFSIZE];
UErrorCode err = U_ZERO_ERROR;
if (localeID == NULL)
{
localeID = uloc_getDefault();
}
uloc_getCountry(localeID, cntry, TEMPBUFSIZE, &err);
if (U_FAILURE(err))
return "";
offset = _findIndex(_countries, sizeof(_countries), cntry);
if (offset < 0)
return "";
return &(_countries3[offset * 4]);
}
uint32_t uloc_getLCID(const char* localeID)
{
UErrorCode err = U_ZERO_ERROR;
char temp[30];
const UChar* lcid = NULL;
int32_t lcidLen = 0;
uint32_t result = 0;
UResourceBundle* bundle = ures_open(NULL, localeID, &err);
if (U_SUCCESS(err))
{
lcid = ures_getStringByKey(bundle, _kLocaleID, &lcidLen, &err);
ures_close(bundle);
if (U_FAILURE(err) || !lcid || lcidLen == 0)
{
return 0;
}
u_austrcpy(temp, lcid);
result = (uint32_t)T_CString_stringToInteger(temp, 16);
}
return result;
}
int32_t uloc_getDisplayLanguage(const char* locale,
const char* inLocale,
UChar* language,
int32_t languageCapacity,
UErrorCode* status)
{
const UChar* result = NULL;
int32_t resultLen = 0;
int langBufSize;
char inLanguageBuffer[TEMPBUFSIZE];
char inLocaleBuffer[TEMPBUFSIZE];
UErrorCode err = U_ZERO_ERROR;
UResourceBundle* bundle;
UBool isDefaultLocale = FALSE;
UBool done = FALSE;
if (U_FAILURE(*status))
return 0;
if (inLocale == NULL)
{
inLocale = uloc_getDefault();
isDefaultLocale = TRUE;
}
else if (uprv_strcmp(inLocale, uloc_getDefault()) == 0)
{
isDefaultLocale = TRUE;
}
/*truncates the fallback mechanism if we start out with a defaultLocale*/
if (locale == NULL)
locale = uloc_getDefault();
/*extracts the language*/
langBufSize = uloc_getLanguage(locale,
inLanguageBuffer,
TEMPBUFSIZE,
&err);
/*We need to implement a fallback mechanism here because we are getting keys out of a
tagged array, there is no capability of doing this with fallback through the resource
bundle API*/
if (langBufSize > 1)
{
do
{
/*
If we are at the root locale ("")
The first time we fall back to the full default locale
As we iterate down the latter, if we hit the root locale ("")
we pass it to the resource bundle api so it checks default.txt
*/
if (inLocale[0] == '\0')
{
if (!isDefaultLocale)
{
isDefaultLocale = TRUE;
inLocale = uloc_getDefault();
}
else {
done = TRUE;
}
}
bundle = ures_open(NULL, inLocale, &err);
if (U_SUCCESS(err))
{
const UChar* temp = NULL;
UResourceBundle* langBundle;
err = U_ZERO_ERROR;
langBundle = ures_getByKey(bundle, _kLanguages, NULL, &err);
if (U_SUCCESS(err))
{
temp = ures_getStringByKey(langBundle,
inLanguageBuffer,
&resultLen,
&err);
resultLen++;
if (U_SUCCESS(err))
result = temp;
ures_close(langBundle);
}
ures_close(bundle);
}
err = U_ZERO_ERROR;
/*Iterates down the Locale ID*/
uloc_getParent(inLocale, inLocaleBuffer, TEMPBUFSIZE, &err);
inLocale = inLocaleBuffer;
} while ((result == NULL) && !done);
}
if (result)
{
if (resultLen > languageCapacity)
{
*status = U_BUFFER_OVERFLOW_ERROR;
if (languageCapacity >= 1)
{
u_strncpy(language, result, languageCapacity-1);
language[languageCapacity-1] = (UChar)0x0000;
}
}
else {
u_strcpy(language, result);
}
}
else
{
/*Falls back to ISO Name*/
resultLen = langBufSize;
if (resultLen > languageCapacity)
{
*status = U_BUFFER_OVERFLOW_ERROR;
if (languageCapacity >= 1)
{
language[languageCapacity-1] = (UChar)0x0000;
u_uastrncpy(language, inLanguageBuffer, languageCapacity-1);
}
}
else u_uastrcpy(language, inLanguageBuffer);
}
return resultLen;
}
int32_t uloc_getDisplayCountry(const char* locale,
const char* inLocale,
UChar* country,
int32_t countryCapacity,
UErrorCode* status)
{
/* NULL may be used to specify the default */
const UChar* result = NULL;
int32_t resultLen = 0;
int cntryBufSize;
char inCountryBuffer[TEMPBUFSIZE];
UErrorCode err = U_ZERO_ERROR;
UResourceBundle* bundle = NULL;
char inLocaleBuffer[TEMPBUFSIZE];
UBool isDefaultLocale = FALSE;
UBool done = FALSE;
if (U_FAILURE(*status))
return 0;
if (inLocale == NULL)
{
inLocale = uloc_getDefault();
isDefaultLocale = TRUE;
}
else if (uprv_strcmp(inLocale, uloc_getDefault()) == 0)
{
isDefaultLocale = TRUE;
}
/*truncates the fallback mechanism if we start out with a defaultLocale*/
if (locale == NULL) {
locale = uloc_getDefault();
}
/*extracts the country*/
cntryBufSize = uloc_getCountry(locale, inCountryBuffer, TEMPBUFSIZE, &err);
if (cntryBufSize > 1)
{
/*
We need to implement a fallback mechanism here because we are getting keys out of a
tagged array, there is no capability of doing this with fallback through the resource
bundle API
*/
do
{
/*
If we are at the root locale ("")
The first time we fall back to the full default locale
As we iterate down the latter, if we hit the root locale ("")
we pass it to the resource bundle api so it checks default.txt
*/
if (inLocale[0] == '\0')
{
if (!isDefaultLocale)
{
isDefaultLocale = TRUE;
inLocale = uloc_getDefault();
}
else
done = TRUE;
}
bundle = ures_open(NULL, inLocale, &err);
if (U_SUCCESS(err))
{
const UChar* temp;
UResourceBundle* countryBundle;
err = U_ZERO_ERROR;
countryBundle = ures_getByKey(bundle, _kCountries, NULL, &err);
if (U_SUCCESS(err))
{
temp = ures_getStringByKey(countryBundle,
inCountryBuffer,
&resultLen,
&err);
resultLen++;
if (U_SUCCESS(err))
result = temp;
ures_close(countryBundle);
}
ures_close(bundle);
}
err = U_ZERO_ERROR;
uloc_getParent(inLocale, inLocaleBuffer, TEMPBUFSIZE, &err);
inLocale = inLocaleBuffer;
} while ((result == NULL) && !done);
}
if (result)
{
if (resultLen > countryCapacity)
{
*status = U_BUFFER_OVERFLOW_ERROR;
if (countryCapacity >= 1)
{
country[countryCapacity-1] = (UChar)0x0000;
u_strncpy(country, result, countryCapacity-1);
}
}
else u_strcpy(country, result);
}
else
{
/*Falls back to ISO Name*/
resultLen = cntryBufSize;
if (resultLen > countryCapacity)
{
*status = U_BUFFER_OVERFLOW_ERROR;
if (countryCapacity >= 1)
{
u_uastrncpy(country, inCountryBuffer, countryCapacity-1);
country[countryCapacity-1] = (UChar)0x0000;
}
}
else u_uastrcpy(country, inCountryBuffer);
}
return resultLen;
}
int32_t uloc_getDisplayVariant(const char* locale,
const char* inLocale,
UChar* variant,
int32_t variantCapacity,
UErrorCode* status)
{
const UChar* result = NULL;
int32_t resultLen = 0;
int varBufSize;
char inVariantBuffer[TEMPBUFSIZE];
char* inVariant = inVariantBuffer;
UErrorCode err = U_ZERO_ERROR;
UResourceBundle* bundle;
char inLocaleBuffer[TEMPBUFSIZE];
UBool isDefaultLocale = FALSE;
char inVariantTagBuffer[TEMPBUFSIZE+2];
char* inVariantTag = inVariantTagBuffer;
UBool done = FALSE;
if (U_FAILURE(*status))
return 0;
inVariantTagBuffer[0] = '\0';
if (inLocale == NULL)
{
inLocale = uloc_getDefault();
isDefaultLocale = TRUE;
}
else if (uprv_strcmp(inLocale, uloc_getDefault()) == 0)
{
isDefaultLocale = TRUE;
}
/*truncates the fallback mechanism if we start out with a defaultLocale*/
if (locale == NULL)
locale = uloc_getDefault();
/*extracts the variant*/
varBufSize = uloc_getVariant(locale, inVariant, TEMPBUFSIZE, &err);
if (varBufSize > 1)
{
/*In case the variant is longer than our stack buffers*/
if (err == U_BUFFER_OVERFLOW_ERROR)
{
inVariant = (char*)uprv_malloc(varBufSize*sizeof(char)+1);
if (inVariant == NULL)
goto NO_MEMORY;
inVariantTag = (char*)uprv_malloc(varBufSize*sizeof(char)+uprv_strlen("%%")+1);
if (inVariantTag == NULL)
{
uprv_free(inVariant);
goto NO_MEMORY;
}
err = U_ZERO_ERROR;
uloc_getVariant(locale, inVariant, varBufSize, &err);
}
uprv_strcpy(inVariantTag,"%%");
uprv_strcat(inVariantTag, inVariant);
/*We need to implement a fallback mechanism here because we are getting keys out of a
tagged array, there is no capability of doing this with fallback through the resource
bundle API*/
do {
/*
If we are at the root locale ("")
The first time we fall back to the full default locale
As we iterate down the latter, if we hit the root locale ("")
we pass it to the resource bundle api so it checks default.txt
*/
if (inLocale[0] == '\0')
{
if (!isDefaultLocale)
{
isDefaultLocale = TRUE;
inLocale = uloc_getDefault();
}
else
done = TRUE;
}
bundle = ures_open(NULL, inLocale, &err);
if (U_SUCCESS(err))
{
const UChar* temp;
temp = ures_getStringByKey(bundle,
inVariantTag,
&resultLen,
&err);
resultLen++;
if (U_SUCCESS(err))
result = temp;
ures_close(bundle);
}
err = U_ZERO_ERROR;
uloc_getParent(inLocale, inLocaleBuffer, TEMPBUFSIZE, &err);
inLocale = inLocaleBuffer;
} while ((result == NULL) && !done);
}
if (result)
{
if (resultLen > variantCapacity)
{
*status = U_BUFFER_OVERFLOW_ERROR;
if (variantCapacity >= 1)
{
variant[variantCapacity-1] = (UChar)0x0000;
u_strncpy(variant, result, variantCapacity-1);
}
}
else
{
u_strcpy(variant, result);
}
}
else
{
/*Falls back to user's Name*/
resultLen = varBufSize;
if (resultLen > variantCapacity)
{
*status = U_BUFFER_OVERFLOW_ERROR;
if (variantCapacity >= 1)
{
u_uastrncpy(variant, inVariant, variantCapacity-1);
variant[variantCapacity-1] = (UChar)0x0000;
}
}
else
{
u_uastrcpy(variant, inVariant);
}
}
/*Clean up memory*/
if (inVariant != inVariantBuffer)
{
uprv_free(inVariant);
uprv_free(inVariantTag);
}
return resultLen;
NO_MEMORY:
*status = U_MEMORY_ALLOCATION_ERROR;
return 0;
}
int32_t uloc_getDisplayName(const char* locale,
const char* inLocale,
UChar* result,
int32_t nameCapacity,
UErrorCode* err)
{
UErrorCode int_err = U_ZERO_ERROR;
int i = 0;
int cntSze, varSze;
UBool has_lang = TRUE;
int result_size;
int_err = U_ZERO_ERROR;
/*Preflights all the components*/
cntSze = uloc_getDisplayCountry(locale,
inLocale,
NULL ,
0,
&int_err);
int_err = U_ZERO_ERROR;
varSze = uloc_getDisplayVariant(locale,
inLocale,
NULL ,
0,
&int_err);
int_err = U_ZERO_ERROR;
i = uloc_getDisplayLanguage(locale,
inLocale,
NULL,
0,
&int_err);
/*Decrement duplicative zero-terminators*/
--varSze;
--cntSze;
/*Logic below adjusts pre-flight information with additional characters "(", ",", " ", ")"
when neeed be*/
if ((i-1 == 0) && (varSze == 0)) /*No language field*/
{
has_lang = FALSE;
i = cntSze+1;
}
else if (cntSze)
{
if (varSze)
{
i += cntSze + varSze + 5;
}
else
{
i += cntSze + 3;
}
}
int_err = U_ZERO_ERROR;
result_size = uloc_getDisplayLanguage(locale,
inLocale,
result,
nameCapacity,
&int_err) - 1;
if (U_SUCCESS(int_err)&&cntSze)
{
if (U_SUCCESS(int_err))
{
if (has_lang)
{
u_strcat(result, openParen);
result_size += 2;
}
result_size += uloc_getDisplayCountry(locale,
inLocale,
result + result_size,
nameCapacity - result_size,
&int_err) - 1;
}
if (varSze)
{
if (U_SUCCESS(int_err))
{
u_strcat(result, comma);
result_size += 2;
result_size += uloc_getDisplayVariant(locale,
inLocale,
result + result_size,
nameCapacity - result_size,
&int_err) - 1;
}
}
if (U_SUCCESS(int_err)&&has_lang)
{
u_strcat(result, closeParen);
}
}
*err = int_err;
return i;
}
const char*
uloc_getAvailable(int32_t offset)
{
if (_installedLocales == NULL)
_lazyEvaluate_installedLocales();
if (offset > _installedLocalesCount)
return NULL;
else
return _installedLocales[offset];
}
int32_t uloc_countAvailable()
{
if (_installedLocales == NULL) _lazyEvaluate_installedLocales();
return _installedLocalesCount;
}
static void _lazyEvaluate_installedLocales()
{
UResourceBundle *index = NULL;
UResourceBundle installed;
UErrorCode status = U_ZERO_ERROR;
const UChar *lname;
char ** temp;
int32_t i = 0;
int32_t len = 0;
ures_setIsStackObject(&installed, TRUE);
index = ures_open(NULL, kIndexLocaleName, &status);
ures_getByKey(index, kIndexTag, &installed, &status);
if(U_SUCCESS(status)) {
_installedLocalesCount = ures_getSize(&installed);
temp = (char **) uprv_malloc(sizeof(char*) * (_installedLocalesCount+1));
ures_resetIterator(&installed);
while(ures_hasNext(&installed)) {
lname = ures_getNextString(&installed, &len, NULL, &status);
temp[i] = (char*) uprv_malloc(sizeof(char) * (len + 1));
u_UCharsToChars(lname, temp[i], len);
temp[i][len] = 0; /* Terminate the string */
i++;
}
{
umtx_lock(NULL);
if (_installedLocales == NULL)
{
_installedLocales = temp;
temp = NULL;
} else {
for (i = 0; i < _installedLocalesCount; i++) uprv_free(temp[i]);
uprv_free(temp);
}
umtx_unlock(NULL);
}
ures_close(&installed);
}
ures_close(index);
}
/**
* 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.
*/
const char* const* uloc_getISOLanguages()
{
const char *from, *end;
char **to;
if (_isoLanguages == NULL)
{
{
umtx_lock(NULL);
if (_isoLanguages == NULL)
{
_isoLanguages = (char**) uprv_malloc(sizeof(char*)*(1+(sizeof(_languages) / 3)));
end = _languages + (sizeof(_languages));
from = _languages;
to = _isoLanguages;
while (from < end)
{
*to = (char*)from;
++to;
from += 3;
}
*to = NULL;
}
umtx_unlock(NULL);
}
}
return (const char* const*)_isoLanguages;
}
/**
* 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.
*/
const char* const* uloc_getISOCountries()
{
if (_isoCountries == NULL)
{
const char *from, *end;
char **to;
{
umtx_lock(NULL);
if (_isoCountries == NULL)
{
_isoCountries = (char**) uprv_malloc(sizeof(char*)*(1+(sizeof(_countries) / 3)));
end = _countries + (sizeof(_countries));
from = _countries;
to = _isoCountries;
while (from < end)
{
*to = (char*)from;
++to;
from += 3;
}
*to = NULL;
}
umtx_unlock(NULL);
}
}
return (const char* const*)_isoCountries;
}