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