michael@0: /* michael@0: ********************************************************************** michael@0: * Copyright (C) 1997-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: ********************************************************************** michael@0: * michael@0: * File locid.cpp michael@0: * michael@0: * Created by: Richard Gillam michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 02/11/97 aliu Changed gLocPath to fgDataDirectory and added michael@0: * methods to get and set it. michael@0: * 04/02/97 aliu Made operator!= inline; fixed return value michael@0: * of getName(). michael@0: * 04/15/97 aliu Cleanup for AIX/Win32. michael@0: * 04/24/97 aliu Numerous changes per code review. michael@0: * 08/18/98 stephen Changed getDisplayName() michael@0: * Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE michael@0: * Added getISOCountries(), getISOLanguages(), michael@0: * getLanguagesForCountry() michael@0: * 03/16/99 bertrand rehaul. michael@0: * 07/21/99 stephen Added U_CFUNC setDefault michael@0: * 11/09/99 weiv Added const char * getName() const; michael@0: * 04/12/00 srl removing unicodestring api's and cached hash code michael@0: * 08/10/01 grhoten Change the static Locales to accessor functions michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: michael@0: #include "unicode/locid.h" michael@0: #include "unicode/uloc.h" michael@0: #include "putilimp.h" michael@0: #include "mutex.h" michael@0: #include "umutex.h" michael@0: #include "uassert.h" michael@0: #include "cmemory.h" michael@0: #include "cstring.h" michael@0: #include "uhash.h" michael@0: #include "ucln_cmn.h" michael@0: #include "ustr_imp.h" michael@0: michael@0: #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) michael@0: michael@0: U_CDECL_BEGIN michael@0: static UBool U_CALLCONV locale_cleanup(void); michael@0: U_CDECL_END michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: static Locale *gLocaleCache = NULL; michael@0: michael@0: // gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale. michael@0: static UMutex gDefaultLocaleMutex = U_MUTEX_INITIALIZER; michael@0: static UHashtable *gDefaultLocalesHashT = NULL; michael@0: static Locale *gDefaultLocale = NULL; michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: typedef enum ELocalePos { michael@0: eENGLISH, michael@0: eFRENCH, michael@0: eGERMAN, michael@0: eITALIAN, michael@0: eJAPANESE, michael@0: eKOREAN, michael@0: eCHINESE, michael@0: michael@0: eFRANCE, michael@0: eGERMANY, michael@0: eITALY, michael@0: eJAPAN, michael@0: eKOREA, michael@0: eCHINA, /* Alias for PRC */ michael@0: eTAIWAN, michael@0: eUK, michael@0: eUS, michael@0: eCANADA, michael@0: eCANADA_FRENCH, michael@0: eROOT, michael@0: michael@0: michael@0: //eDEFAULT, michael@0: eMAX_LOCALES michael@0: } ELocalePos; michael@0: michael@0: U_CFUNC int32_t locale_getKeywords(const char *localeID, michael@0: char prev, michael@0: char *keywords, int32_t keywordCapacity, michael@0: char *values, int32_t valuesCapacity, int32_t *valLen, michael@0: UBool valuesToo, michael@0: UErrorCode *status); michael@0: michael@0: U_CDECL_BEGIN michael@0: // michael@0: // Deleter function for Locales owned by the default Locale hash table/ michael@0: // michael@0: static void U_CALLCONV michael@0: deleteLocale(void *obj) { michael@0: delete (icu::Locale *) obj; michael@0: } michael@0: michael@0: static UBool U_CALLCONV locale_cleanup(void) michael@0: { michael@0: U_NAMESPACE_USE michael@0: michael@0: if (gLocaleCache) { michael@0: delete [] gLocaleCache; michael@0: gLocaleCache = NULL; michael@0: } michael@0: michael@0: if (gDefaultLocalesHashT) { michael@0: uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func. michael@0: gDefaultLocalesHashT = NULL; michael@0: gDefaultLocale = NULL; michael@0: } michael@0: michael@0: return TRUE; michael@0: } michael@0: U_CDECL_END michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: Locale *locale_set_default_internal(const char *id, UErrorCode& status) { michael@0: // Synchronize this entire function. michael@0: Mutex lock(&gDefaultLocaleMutex); michael@0: michael@0: UBool canonicalize = FALSE; michael@0: michael@0: // If given a NULL string for the locale id, grab the default michael@0: // name from the system. michael@0: // (Different from most other locale APIs, where a null name means use michael@0: // the current ICU default locale.) michael@0: if (id == NULL) { michael@0: id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify. michael@0: canonicalize = TRUE; // always canonicalize host ID michael@0: } michael@0: michael@0: char localeNameBuf[512]; michael@0: michael@0: if (canonicalize) { michael@0: uloc_canonicalize(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); michael@0: } else { michael@0: uloc_getName(id, localeNameBuf, sizeof(localeNameBuf)-1, &status); michael@0: } michael@0: localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of michael@0: // a long name filling the buffer. michael@0: // (long names are truncated.) michael@0: // michael@0: if (U_FAILURE(status)) { michael@0: return gDefaultLocale; michael@0: } michael@0: michael@0: if (gDefaultLocalesHashT == NULL) { michael@0: gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, NULL, &status); michael@0: if (U_FAILURE(status)) { michael@0: return gDefaultLocale; michael@0: } michael@0: uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale); michael@0: ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); michael@0: } michael@0: michael@0: Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf); michael@0: if (newDefault == NULL) { michael@0: newDefault = new Locale(Locale::eBOGUS); michael@0: if (newDefault == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: return gDefaultLocale; michael@0: } michael@0: newDefault->init(localeNameBuf, FALSE); michael@0: uhash_put(gDefaultLocalesHashT, (char*) newDefault->getName(), newDefault, &status); michael@0: if (U_FAILURE(status)) { michael@0: return gDefaultLocale; michael@0: } michael@0: } michael@0: gDefaultLocale = newDefault; michael@0: return gDefaultLocale; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: /* sfb 07/21/99 */ michael@0: U_CFUNC void michael@0: locale_set_default(const char *id) michael@0: { michael@0: U_NAMESPACE_USE michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: locale_set_default_internal(id, status); michael@0: } michael@0: /* end */ michael@0: michael@0: U_CFUNC const char * michael@0: locale_get_default(void) michael@0: { michael@0: U_NAMESPACE_USE michael@0: return Locale::getDefault().getName(); michael@0: } michael@0: michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale) michael@0: michael@0: /*Character separating the posix id fields*/ michael@0: // '_' michael@0: // In the platform codepage. michael@0: #define SEP_CHAR '_' michael@0: michael@0: Locale::~Locale() michael@0: { michael@0: /*if fullName is on the heap, we free it*/ michael@0: if (fullName != fullNameBuffer) michael@0: { michael@0: uprv_free(fullName); michael@0: fullName = NULL; michael@0: } michael@0: if (baseName && baseName != baseNameBuffer) { michael@0: uprv_free(baseName); michael@0: baseName = NULL; michael@0: } michael@0: } michael@0: michael@0: Locale::Locale() michael@0: : UObject(), fullName(fullNameBuffer), baseName(NULL) michael@0: { michael@0: init(NULL, FALSE); michael@0: } michael@0: michael@0: /* michael@0: * Internal constructor to allow construction of a locale object with michael@0: * NO side effects. (Default constructor tries to get michael@0: * the default locale.) michael@0: */ michael@0: Locale::Locale(Locale::ELocaleType) michael@0: : UObject(), fullName(fullNameBuffer), baseName(NULL) michael@0: { michael@0: setToBogus(); michael@0: } michael@0: michael@0: michael@0: Locale::Locale( const char * newLanguage, michael@0: const char * newCountry, michael@0: const char * newVariant, michael@0: const char * newKeywords) michael@0: : UObject(), fullName(fullNameBuffer), baseName(NULL) michael@0: { michael@0: if( (newLanguage==NULL) && (newCountry == NULL) && (newVariant == NULL) ) michael@0: { michael@0: init(NULL, FALSE); /* shortcut */ michael@0: } michael@0: else michael@0: { michael@0: MaybeStackArray togo; michael@0: int32_t size = 0; michael@0: int32_t lsize = 0; michael@0: int32_t csize = 0; michael@0: int32_t vsize = 0; michael@0: int32_t ksize = 0; michael@0: char *p; michael@0: michael@0: // Calculate the size of the resulting string. michael@0: michael@0: // Language michael@0: if ( newLanguage != NULL ) michael@0: { michael@0: lsize = (int32_t)uprv_strlen(newLanguage); michael@0: size = lsize; michael@0: } michael@0: michael@0: // _Country michael@0: if ( newCountry != NULL ) michael@0: { michael@0: csize = (int32_t)uprv_strlen(newCountry); michael@0: size += csize; michael@0: } michael@0: michael@0: // _Variant michael@0: if ( newVariant != NULL ) michael@0: { michael@0: // remove leading _'s michael@0: while(newVariant[0] == SEP_CHAR) michael@0: { michael@0: newVariant++; michael@0: } michael@0: michael@0: // remove trailing _'s michael@0: vsize = (int32_t)uprv_strlen(newVariant); michael@0: while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR) ) michael@0: { michael@0: vsize--; michael@0: } michael@0: } michael@0: michael@0: if( vsize > 0 ) michael@0: { michael@0: size += vsize; michael@0: } michael@0: michael@0: // Separator rules: michael@0: if ( vsize > 0 ) michael@0: { michael@0: size += 2; // at least: __v michael@0: } michael@0: else if ( csize > 0 ) michael@0: { michael@0: size += 1; // at least: _v michael@0: } michael@0: michael@0: if ( newKeywords != NULL) michael@0: { michael@0: ksize = (int32_t)uprv_strlen(newKeywords); michael@0: size += ksize + 1; michael@0: } michael@0: michael@0: michael@0: // NOW we have the full locale string.. michael@0: michael@0: /*if the whole string is longer than our internal limit, we need michael@0: to go to the heap for temporary buffers*/ michael@0: if (size >= togo.getCapacity()) michael@0: { michael@0: // If togo_heap could not be created, initialize with default settings. michael@0: if (togo.resize(size+1) == NULL) { michael@0: init(NULL, FALSE); michael@0: } michael@0: } michael@0: michael@0: togo[0] = 0; michael@0: michael@0: // Now, copy it back. michael@0: p = togo.getAlias(); michael@0: if ( lsize != 0 ) michael@0: { michael@0: uprv_strcpy(p, newLanguage); michael@0: p += lsize; michael@0: } michael@0: michael@0: if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v michael@0: { // ^ michael@0: *p++ = SEP_CHAR; michael@0: } michael@0: michael@0: if ( csize != 0 ) michael@0: { michael@0: uprv_strcpy(p, newCountry); michael@0: p += csize; michael@0: } michael@0: michael@0: if ( vsize != 0) michael@0: { michael@0: *p++ = SEP_CHAR; // at least: __v michael@0: michael@0: uprv_strncpy(p, newVariant, vsize); // Must use strncpy because michael@0: p += vsize; // of trimming (above). michael@0: *p = 0; // terminate michael@0: } michael@0: michael@0: if ( ksize != 0) michael@0: { michael@0: if (uprv_strchr(newKeywords, '=')) { michael@0: *p++ = '@'; /* keyword parsing */ michael@0: } michael@0: else { michael@0: *p++ = '_'; /* Variant parsing with a script */ michael@0: if ( vsize == 0) { michael@0: *p++ = '_'; /* No country found */ michael@0: } michael@0: } michael@0: uprv_strcpy(p, newKeywords); michael@0: p += ksize; michael@0: } michael@0: michael@0: // Parse it, because for example 'language' might really be a complete michael@0: // string. michael@0: init(togo.getAlias(), FALSE); michael@0: } michael@0: } michael@0: michael@0: Locale::Locale(const Locale &other) michael@0: : UObject(other), fullName(fullNameBuffer), baseName(NULL) michael@0: { michael@0: *this = other; michael@0: } michael@0: michael@0: Locale &Locale::operator=(const Locale &other) michael@0: { michael@0: if (this == &other) { michael@0: return *this; michael@0: } michael@0: michael@0: if (&other == NULL) { michael@0: this->setToBogus(); michael@0: return *this; michael@0: } michael@0: michael@0: /* Free our current storage */ michael@0: if(fullName != fullNameBuffer) { michael@0: uprv_free(fullName); michael@0: fullName = fullNameBuffer; michael@0: } michael@0: michael@0: /* Allocate the full name if necessary */ michael@0: if(other.fullName != other.fullNameBuffer) { michael@0: fullName = (char *)uprv_malloc(sizeof(char)*(uprv_strlen(other.fullName)+1)); michael@0: if (fullName == NULL) { michael@0: return *this; michael@0: } michael@0: } michael@0: /* Copy the full name */ michael@0: uprv_strcpy(fullName, other.fullName); michael@0: michael@0: /* baseName is the cached result of getBaseName. if 'other' has a michael@0: baseName and it fits in baseNameBuffer, then copy it. otherwise set michael@0: it to NULL, and let the user lazy-create it (in getBaseName) if they michael@0: want it. */ michael@0: if(baseName && baseName != baseNameBuffer) { michael@0: uprv_free(baseName); michael@0: } michael@0: baseName = NULL; michael@0: michael@0: if(other.baseName == other.baseNameBuffer) { michael@0: uprv_strcpy(baseNameBuffer, other.baseNameBuffer); michael@0: baseName = baseNameBuffer; michael@0: } michael@0: michael@0: /* Copy the language and country fields */ michael@0: uprv_strcpy(language, other.language); michael@0: uprv_strcpy(script, other.script); michael@0: uprv_strcpy(country, other.country); michael@0: michael@0: /* The variantBegin is an offset, just copy it */ michael@0: variantBegin = other.variantBegin; michael@0: fIsBogus = other.fIsBogus; michael@0: return *this; michael@0: } michael@0: michael@0: Locale * michael@0: Locale::clone() const { michael@0: return new Locale(*this); michael@0: } michael@0: michael@0: UBool michael@0: Locale::operator==( const Locale& other) const michael@0: { michael@0: return (uprv_strcmp(other.fullName, fullName) == 0); michael@0: } michael@0: michael@0: #define ISASCIIALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) michael@0: michael@0: /*This function initializes a Locale from a C locale ID*/ michael@0: Locale& Locale::init(const char* localeID, UBool canonicalize) michael@0: { michael@0: fIsBogus = FALSE; michael@0: /* Free our current storage */ michael@0: if(fullName != fullNameBuffer) { michael@0: uprv_free(fullName); michael@0: fullName = fullNameBuffer; michael@0: } michael@0: michael@0: if(baseName && baseName != baseNameBuffer) { michael@0: uprv_free(baseName); michael@0: baseName = NULL; michael@0: } michael@0: michael@0: // not a loop: michael@0: // just an easy way to have a common error-exit michael@0: // without goto and without another function michael@0: do { michael@0: char *separator; michael@0: char *field[5] = {0}; michael@0: int32_t fieldLen[5] = {0}; michael@0: int32_t fieldIdx; michael@0: int32_t variantField; michael@0: int32_t length; michael@0: UErrorCode err; michael@0: michael@0: if(localeID == NULL) { michael@0: // not an error, just set the default locale michael@0: return *this = getDefault(); michael@0: } michael@0: michael@0: /* preset all fields to empty */ michael@0: language[0] = script[0] = country[0] = 0; michael@0: michael@0: // "canonicalize" the locale ID to ICU/Java format michael@0: err = U_ZERO_ERROR; michael@0: length = canonicalize ? michael@0: uloc_canonicalize(localeID, fullName, sizeof(fullNameBuffer), &err) : michael@0: uloc_getName(localeID, fullName, sizeof(fullNameBuffer), &err); michael@0: michael@0: if(err == U_BUFFER_OVERFLOW_ERROR || length >= (int32_t)sizeof(fullNameBuffer)) { michael@0: /*Go to heap for the fullName if necessary*/ michael@0: fullName = (char *)uprv_malloc(sizeof(char)*(length + 1)); michael@0: if(fullName == 0) { michael@0: fullName = fullNameBuffer; michael@0: break; // error: out of memory michael@0: } michael@0: err = U_ZERO_ERROR; michael@0: length = canonicalize ? michael@0: uloc_canonicalize(localeID, fullName, length+1, &err) : michael@0: uloc_getName(localeID, fullName, length+1, &err); michael@0: } michael@0: if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) { michael@0: /* should never occur */ michael@0: break; michael@0: } michael@0: michael@0: variantBegin = length; michael@0: michael@0: /* after uloc_getName/canonicalize() we know that only '_' are separators */ michael@0: separator = field[0] = fullName; michael@0: fieldIdx = 1; michael@0: while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR)) && fieldIdx < (int32_t)(sizeof(field)/sizeof(field[0]))-1) { michael@0: field[fieldIdx] = separator + 1; michael@0: fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); michael@0: fieldIdx++; michael@0: } michael@0: // variant may contain @foo or .foo POSIX cruft; remove it michael@0: separator = uprv_strchr(field[fieldIdx-1], '@'); michael@0: char* sep2 = uprv_strchr(field[fieldIdx-1], '.'); michael@0: if (separator!=NULL || sep2!=NULL) { michael@0: if (separator==NULL || (sep2!=NULL && separator > sep2)) { michael@0: separator = sep2; michael@0: } michael@0: fieldLen[fieldIdx-1] = (int32_t)(separator - field[fieldIdx-1]); michael@0: } else { michael@0: fieldLen[fieldIdx-1] = length - (int32_t)(field[fieldIdx-1] - fullName); michael@0: } michael@0: michael@0: if (fieldLen[0] >= (int32_t)(sizeof(language))) michael@0: { michael@0: break; // error: the language field is too long michael@0: } michael@0: michael@0: variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */ michael@0: if (fieldLen[0] > 0) { michael@0: /* We have a language */ michael@0: uprv_memcpy(language, fullName, fieldLen[0]); michael@0: language[fieldLen[0]] = 0; michael@0: } michael@0: if (fieldLen[1] == 4 && ISASCIIALPHA(field[1][0]) && michael@0: ISASCIIALPHA(field[1][1]) && ISASCIIALPHA(field[1][2]) && michael@0: ISASCIIALPHA(field[1][3])) { michael@0: /* We have at least a script */ michael@0: uprv_memcpy(script, field[1], fieldLen[1]); michael@0: script[fieldLen[1]] = 0; michael@0: variantField++; michael@0: } michael@0: michael@0: if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) { michael@0: /* We have a country */ michael@0: uprv_memcpy(country, field[variantField], fieldLen[variantField]); michael@0: country[fieldLen[variantField]] = 0; michael@0: variantField++; michael@0: } else if (fieldLen[variantField] == 0) { michael@0: variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */ michael@0: } michael@0: michael@0: if (fieldLen[variantField] > 0) { michael@0: /* We have a variant */ michael@0: variantBegin = (int32_t)(field[variantField] - fullName); michael@0: } michael@0: michael@0: // successful end of init() michael@0: return *this; michael@0: } while(0); /*loop doesn't iterate*/ michael@0: michael@0: // when an error occurs, then set this object to "bogus" (there is no UErrorCode here) michael@0: setToBogus(); michael@0: michael@0: return *this; michael@0: } michael@0: michael@0: int32_t michael@0: Locale::hashCode() const michael@0: { michael@0: return ustr_hashCharsN(fullName, uprv_strlen(fullName)); michael@0: } michael@0: michael@0: void michael@0: Locale::setToBogus() { michael@0: /* Free our current storage */ michael@0: if(fullName != fullNameBuffer) { michael@0: uprv_free(fullName); michael@0: fullName = fullNameBuffer; michael@0: } michael@0: if(baseName && baseName != baseNameBuffer) { michael@0: uprv_free(baseName); michael@0: baseName = NULL; michael@0: } michael@0: *fullNameBuffer = 0; michael@0: *language = 0; michael@0: *script = 0; michael@0: *country = 0; michael@0: fIsBogus = TRUE; michael@0: } michael@0: michael@0: const Locale& U_EXPORT2 michael@0: Locale::getDefault() michael@0: { michael@0: { michael@0: Mutex lock(&gDefaultLocaleMutex); michael@0: if (gDefaultLocale != NULL) { michael@0: return *gDefaultLocale; michael@0: } michael@0: } michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: return *locale_set_default_internal(NULL, status); michael@0: } michael@0: michael@0: michael@0: michael@0: void U_EXPORT2 michael@0: Locale::setDefault( const Locale& newLocale, michael@0: UErrorCode& status) michael@0: { michael@0: if (U_FAILURE(status)) { michael@0: return; michael@0: } michael@0: michael@0: /* Set the default from the full name string of the supplied locale. michael@0: * This is a convenient way to access the default locale caching mechanisms. michael@0: */ michael@0: const char *localeID = newLocale.getName(); michael@0: locale_set_default_internal(localeID, status); michael@0: } michael@0: michael@0: Locale U_EXPORT2 michael@0: Locale::createFromName (const char *name) michael@0: { michael@0: if (name) { michael@0: Locale l(""); michael@0: l.init(name, FALSE); michael@0: return l; michael@0: } michael@0: else { michael@0: return getDefault(); michael@0: } michael@0: } michael@0: michael@0: Locale U_EXPORT2 michael@0: Locale::createCanonical(const char* name) { michael@0: Locale loc(""); michael@0: loc.init(name, TRUE); michael@0: return loc; michael@0: } michael@0: michael@0: const char * michael@0: Locale::getISO3Language() const michael@0: { michael@0: return uloc_getISO3Language(fullName); michael@0: } michael@0: michael@0: michael@0: const char * michael@0: Locale::getISO3Country() const michael@0: { michael@0: return uloc_getISO3Country(fullName); michael@0: } michael@0: michael@0: /** michael@0: * Return the LCID value as specified in the "LocaleID" resource for this michael@0: * locale. The LocaleID must be expressed as a hexadecimal number, from michael@0: * one to four digits. If the LocaleID resource is not present, or is michael@0: * in an incorrect format, 0 is returned. The LocaleID is for use in michael@0: * Windows (it is an LCID), but is available on all platforms. michael@0: */ michael@0: uint32_t michael@0: Locale::getLCID() const michael@0: { michael@0: return uloc_getLCID(fullName); michael@0: } michael@0: michael@0: const char* const* U_EXPORT2 Locale::getISOCountries() michael@0: { michael@0: return uloc_getISOCountries(); michael@0: } michael@0: michael@0: const char* const* U_EXPORT2 Locale::getISOLanguages() michael@0: { michael@0: return uloc_getISOLanguages(); michael@0: } michael@0: michael@0: // Set the locale's data based on a posix id. michael@0: void Locale::setFromPOSIXID(const char *posixID) michael@0: { michael@0: init(posixID, TRUE); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getRoot(void) michael@0: { michael@0: return getLocale(eROOT); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getEnglish(void) michael@0: { michael@0: return getLocale(eENGLISH); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getFrench(void) michael@0: { michael@0: return getLocale(eFRENCH); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getGerman(void) michael@0: { michael@0: return getLocale(eGERMAN); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getItalian(void) michael@0: { michael@0: return getLocale(eITALIAN); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getJapanese(void) michael@0: { michael@0: return getLocale(eJAPANESE); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getKorean(void) michael@0: { michael@0: return getLocale(eKOREAN); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getChinese(void) michael@0: { michael@0: return getLocale(eCHINESE); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getSimplifiedChinese(void) michael@0: { michael@0: return getLocale(eCHINA); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getTraditionalChinese(void) michael@0: { michael@0: return getLocale(eTAIWAN); michael@0: } michael@0: michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getFrance(void) michael@0: { michael@0: return getLocale(eFRANCE); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getGermany(void) michael@0: { michael@0: return getLocale(eGERMANY); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getItaly(void) michael@0: { michael@0: return getLocale(eITALY); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getJapan(void) michael@0: { michael@0: return getLocale(eJAPAN); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getKorea(void) michael@0: { michael@0: return getLocale(eKOREA); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getChina(void) michael@0: { michael@0: return getLocale(eCHINA); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getPRC(void) michael@0: { michael@0: return getLocale(eCHINA); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getTaiwan(void) michael@0: { michael@0: return getLocale(eTAIWAN); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getUK(void) michael@0: { michael@0: return getLocale(eUK); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getUS(void) michael@0: { michael@0: return getLocale(eUS); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getCanada(void) michael@0: { michael@0: return getLocale(eCANADA); michael@0: } michael@0: michael@0: const Locale & U_EXPORT2 michael@0: Locale::getCanadaFrench(void) michael@0: { michael@0: return getLocale(eCANADA_FRENCH); michael@0: } michael@0: michael@0: const Locale & michael@0: Locale::getLocale(int locid) michael@0: { michael@0: Locale *localeCache = getLocaleCache(); michael@0: U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0)); michael@0: if (localeCache == NULL) { michael@0: // Failure allocating the locale cache. michael@0: // The best we can do is return a NULL reference. michael@0: locid = 0; michael@0: } michael@0: return localeCache[locid]; /*operating on NULL*/ michael@0: } michael@0: michael@0: /* michael@0: This function is defined this way in order to get around static michael@0: initialization and static destruction. michael@0: */ michael@0: Locale * michael@0: Locale::getLocaleCache(void) michael@0: { michael@0: umtx_lock(NULL); michael@0: UBool needInit = (gLocaleCache == NULL); michael@0: umtx_unlock(NULL); michael@0: michael@0: if (needInit) { michael@0: Locale *tLocaleCache = new Locale[(int)eMAX_LOCALES]; michael@0: if (tLocaleCache == NULL) { michael@0: return NULL; michael@0: } michael@0: tLocaleCache[eROOT] = Locale(""); michael@0: tLocaleCache[eENGLISH] = Locale("en"); michael@0: tLocaleCache[eFRENCH] = Locale("fr"); michael@0: tLocaleCache[eGERMAN] = Locale("de"); michael@0: tLocaleCache[eITALIAN] = Locale("it"); michael@0: tLocaleCache[eJAPANESE] = Locale("ja"); michael@0: tLocaleCache[eKOREAN] = Locale("ko"); michael@0: tLocaleCache[eCHINESE] = Locale("zh"); michael@0: tLocaleCache[eFRANCE] = Locale("fr", "FR"); michael@0: tLocaleCache[eGERMANY] = Locale("de", "DE"); michael@0: tLocaleCache[eITALY] = Locale("it", "IT"); michael@0: tLocaleCache[eJAPAN] = Locale("ja", "JP"); michael@0: tLocaleCache[eKOREA] = Locale("ko", "KR"); michael@0: tLocaleCache[eCHINA] = Locale("zh", "CN"); michael@0: tLocaleCache[eTAIWAN] = Locale("zh", "TW"); michael@0: tLocaleCache[eUK] = Locale("en", "GB"); michael@0: tLocaleCache[eUS] = Locale("en", "US"); michael@0: tLocaleCache[eCANADA] = Locale("en", "CA"); michael@0: tLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA"); michael@0: michael@0: umtx_lock(NULL); michael@0: if (gLocaleCache == NULL) { michael@0: gLocaleCache = tLocaleCache; michael@0: tLocaleCache = NULL; michael@0: ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup); michael@0: } michael@0: umtx_unlock(NULL); michael@0: if (tLocaleCache) { michael@0: delete [] tLocaleCache; // Fancy array delete will destruct each member. michael@0: } michael@0: } michael@0: return gLocaleCache; michael@0: } michael@0: michael@0: class KeywordEnumeration : public StringEnumeration { michael@0: private: michael@0: char *keywords; michael@0: char *current; michael@0: int32_t length; michael@0: UnicodeString currUSKey; michael@0: static const char fgClassID;/* Warning this is used beyond the typical RTTI usage. */ michael@0: michael@0: public: michael@0: static UClassID U_EXPORT2 getStaticClassID(void) { return (UClassID)&fgClassID; } michael@0: virtual UClassID getDynamicClassID(void) const { return getStaticClassID(); } michael@0: public: michael@0: KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status) michael@0: : keywords((char *)&fgClassID), current((char *)&fgClassID), length(0) { michael@0: if(U_SUCCESS(status) && keywordLen != 0) { michael@0: if(keys == NULL || keywordLen < 0) { michael@0: status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: } else { michael@0: keywords = (char *)uprv_malloc(keywordLen+1); michael@0: if (keywords == NULL) { michael@0: status = U_MEMORY_ALLOCATION_ERROR; michael@0: } michael@0: else { michael@0: uprv_memcpy(keywords, keys, keywordLen); michael@0: keywords[keywordLen] = 0; michael@0: current = keywords + currentIndex; michael@0: length = keywordLen; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: virtual ~KeywordEnumeration(); michael@0: michael@0: virtual StringEnumeration * clone() const michael@0: { michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: return new KeywordEnumeration(keywords, length, (int32_t)(current - keywords), status); michael@0: } michael@0: michael@0: virtual int32_t count(UErrorCode &/*status*/) const { michael@0: char *kw = keywords; michael@0: int32_t result = 0; michael@0: while(*kw) { michael@0: result++; michael@0: kw += uprv_strlen(kw)+1; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: virtual const char* next(int32_t* resultLength, UErrorCode& status) { michael@0: const char* result; michael@0: int32_t len; michael@0: if(U_SUCCESS(status) && *current != 0) { michael@0: result = current; michael@0: len = (int32_t)uprv_strlen(current); michael@0: current += len+1; michael@0: if(resultLength != NULL) { michael@0: *resultLength = len; michael@0: } michael@0: } else { michael@0: if(resultLength != NULL) { michael@0: *resultLength = 0; michael@0: } michael@0: result = NULL; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: virtual const UnicodeString* snext(UErrorCode& status) { michael@0: int32_t resultLength = 0; michael@0: const char *s = next(&resultLength, status); michael@0: return setChars(s, resultLength, status); michael@0: } michael@0: michael@0: virtual void reset(UErrorCode& /*status*/) { michael@0: current = keywords; michael@0: } michael@0: }; michael@0: michael@0: const char KeywordEnumeration::fgClassID = '\0'; michael@0: michael@0: KeywordEnumeration::~KeywordEnumeration() { michael@0: uprv_free(keywords); michael@0: } michael@0: michael@0: StringEnumeration * michael@0: Locale::createKeywords(UErrorCode &status) const michael@0: { michael@0: char keywords[256]; michael@0: int32_t keywordCapacity = 256; michael@0: StringEnumeration *result = NULL; michael@0: michael@0: const char* variantStart = uprv_strchr(fullName, '@'); michael@0: const char* assignment = uprv_strchr(fullName, '='); michael@0: if(variantStart) { michael@0: if(assignment > variantStart) { michael@0: int32_t keyLen = locale_getKeywords(variantStart+1, '@', keywords, keywordCapacity, NULL, 0, NULL, FALSE, &status); michael@0: if(keyLen) { michael@0: result = new KeywordEnumeration(keywords, keyLen, 0, status); michael@0: } michael@0: } else { michael@0: status = U_INVALID_FORMAT_ERROR; michael@0: } michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: int32_t michael@0: Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const michael@0: { michael@0: return uloc_getKeywordValue(fullName, keywordName, buffer, bufLen, &status); michael@0: } michael@0: michael@0: void michael@0: Locale::setKeywordValue(const char* keywordName, const char* keywordValue, UErrorCode &status) michael@0: { michael@0: uloc_setKeywordValue(keywordName, keywordValue, fullName, ULOC_FULLNAME_CAPACITY, &status); michael@0: } michael@0: michael@0: const char * michael@0: Locale::getBaseName() const michael@0: { michael@0: // lazy init michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: // semantically const michael@0: if(baseName == 0) { michael@0: ((Locale *)this)->baseName = ((Locale *)this)->baseNameBuffer; michael@0: int32_t baseNameSize = uloc_getBaseName(fullName, baseName, ULOC_FULLNAME_CAPACITY, &status); michael@0: if(baseNameSize >= ULOC_FULLNAME_CAPACITY) { michael@0: ((Locale *)this)->baseName = (char *)uprv_malloc(sizeof(char) * baseNameSize + 1); michael@0: if (baseName == NULL) { michael@0: return baseName; michael@0: } michael@0: uloc_getBaseName(fullName, baseName, baseNameSize+1, &status); michael@0: } michael@0: baseName[baseNameSize] = 0; michael@0: michael@0: // the computation of variantBegin leaves it equal to the length michael@0: // of fullName if there is no variant. It should instead be michael@0: // the length of the baseName. Patch around this for now. michael@0: if (variantBegin == (int32_t)uprv_strlen(fullName)) { michael@0: ((Locale*)this)->variantBegin = baseNameSize; michael@0: } michael@0: } michael@0: return baseName; michael@0: } michael@0: michael@0: //eof michael@0: U_NAMESPACE_END