blob: 4092570b2f51e93812d6ab1eea3e14bd10621168 [file] [log] [blame]
/***************************************************************************
*
* locale_body.cpp - implementation of the std::locale and __rw_locale class
*
* This is an internal header file used to implement the C++ Standard
* Library. It should never be #included directly by a program.
*
* $Id$
*
***************************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* Copyright 2001-2006 Rogue Wave Software.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#include <rw/_defs.h>
#include <new> // for placement new
#include <locale.h>
#include <stdio.h>
#include <stdlib.h> // for bsearch(), qsort()
#include <string.h> // for memxxx() and strxxx() functions
#include <loc/_locale.h>
#include "access.h"
#include "locale_body.h"
#include "setlocale.h"
#ifdef _RWSTD_NO_LOCALE_NAME_FMAT
// pick an arbitrary order for categories in locale names
# define _RWSTD_CAT_0(pfx) \
{ _RWSTD_LC_CTYPE, "LC_CTYPE", pfx::_C_ctype }
# define _RWSTD_CAT_1(pfx) \
{ _RWSTD_LC_NUMERIC, "LC_NUMERIC", pfx::_C_numeric }
# define _RWSTD_CAT_2(pfx) \
{ _RWSTD_LC_TIME, "LC_TIME", pfx::_C_time }
# define _RWSTD_CAT_3(pfx) \
{ _RWSTD_LC_COLLATE, "LC_COLLATE", pfx::_C_collate }
# define _RWSTD_CAT_4(pfx) \
{ _RWSTD_LC_MONETARY, "LC_MONETARY", pfx::_C_monetary }
# define _RWSTD_CAT_5(pfx) \
{ _RWSTD_LC_MESSAGES, "LC_MESSAGES", pfx::_C_messages }
#endif // _RWSTD_NO_LOCALE_NAME_FMAT
_RWSTD_NAMESPACE (__rw) {
// maps LC_XXX category values to their names
extern const __rw_cats_t
__rw_cats [] = {
// expands to { LC_XXX, "LC_XXX", _RW::__rw_locale::_C_xxx }
// the order of these is determined at config time and corresponds to
// the order mixed/combined locale categories returned by setlocale()
_RWSTD_CAT_0 (_RW::__rw_locale),
_RWSTD_CAT_1 (_RW::__rw_locale),
_RWSTD_CAT_2 (_RW::__rw_locale),
_RWSTD_CAT_3 (_RW::__rw_locale),
_RWSTD_CAT_4 (_RW::__rw_locale),
#ifdef _RWSTD_CAT_5
_RWSTD_CAT_5 (_RW::__rw_locale)
#else // if !defined (_RWSTD_CAT_5)
// for Windoze (no LC_MESSAGES there)
_RWSTD_CAT_0 (_RW::__rw_locale)
#endif // _RWSTD_CAT_5
};
// fills provided buffer (if large anough) with the names of locales
// for each of the standard LC_XXX categories; if all categories come
// from the same locale, copies the name of the common locale into
// the provided buffer
//
// names of categories are in the following order:
// LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES
//
// examples of output (assuming ';' as a separator):
// "C"
// all facets of all categories come from the "C" locale
//
// "C;C;de_DE;C;C;C;"
// all facets come from the "C" locale except those that belong
// to the LC_MONETARY category, which all come from "de_DE"
//
// "C;en_US;*;*;*;*;"
// facets the belong to the LC_COLLATE category come from the
// "C" locale, those belonging to the LC_CTYPE category come
// from "en_US", all others are from mixed locales within
// their respective categories
//
// "*"
// facets belonging to each category come from all different
// locales (e.g., ctype<char> might come from the "C" locale,
// and ctype<wchar_t> from "ja_JP", etc.)
char* __rw_locale::
_C_get_cat_names (char *buf, size_t bufsize) const
{
const size_t savesize = bufsize;
char *savebuf = buf;
if (!buf)
buf = new char [bufsize ? bufsize : (bufsize = 256)];
*buf = '\0';
// will point at the locale name common across all categories
// if one such name exists; otherwise at "*" (mixed locale)
const char *common_name = 0;
// iterate over all categories, searching for all facets that belong
// to that category and comparing their names. if all such facets come
// from the same locale, the name of the category is the name of the
// locale; otherwise "*"
for (size_t i = 0; i != __rw_n_cats; ++i) {
const char *cat_name = 0;
for (size_t j = 0; j != _C_n_std_facets; ++j) {
// verify that all facet pointers are set
_RWSTD_ASSERT (_C_std_facets [j]);
// ignore the non-localized {money,num}_{get,put} facets
// and consider only moneypunct and numpunct for the
// monetary category
switch (_C_std_facets [j]->_C_type) {
case __rw_facet::_C_num_get:
case __rw_facet::_C_wnum_get:
case __rw_facet::_C_num_put:
case __rw_facet::_C_wnum_put:
case __rw_facet::_C_money_get:
case __rw_facet::_C_wmoney_get:
case __rw_facet::_C_money_put:
case __rw_facet::_C_wmoney_put:
continue;
default:
break;
}
_RWSTD_ASSERT (0 != _C_std_facets [j]->_C_pid);
// one category at a time
if ( __rw_cats [i].cat
!= __rw_get_cat (int (*_C_std_facets [j]->_C_pid)))
continue;
// default facet name (if 0) is "C"
const char *facet_name = _C_std_facets [j]->_C_name
? _C_std_facets [j]->_C_name : "C";
if (cat_name && strcmp (cat_name, facet_name))
cat_name = "*";
else
cat_name = facet_name;
if (common_name) {
// if the name of the locale for this category doesn't
// match the common locale name so far, assign "*"
if (strcmp (common_name, cat_name))
common_name = "*";
}
else
common_name = cat_name;
}
// will be 0 iff all facets in a category are 0
// (i.e., an internal logic error)
_RWSTD_ASSERT (cat_name);
if ( strlen (buf) + strlen (cat_name) + 2
>= bufsize) {
// reallocate buffer if necessary
char *tmp = 0;
_TRY {
tmp = new char [bufsize *= 2];
}
_CATCH (...) {
if (buf != savebuf)
delete[] buf;
_RETHROW;
}
strcpy (tmp, buf);
if (buf != savebuf)
delete[] buf;
buf = tmp;
}
strcat (buf, cat_name);
strcat (buf, _RWSTD_CAT_SEP);
}
_RWSTD_ASSERT (common_name);
if (__rw_facet::_C_opts & __rw_facet::_C_condensed_name) {
// if all categories come from the same locale, copy the name
// of that locale into the buffer
if ('*' != common_name [0] || '\0' != common_name [1]) {
if ( savebuf && buf != savebuf
&& savesize >= strlen (common_name)) {
// delete allocated memory and repoint at original buffer
delete[] buf;
buf = savebuf;
}
strcpy (buf, common_name);
}
}
// remove trailing separator
const size_t len = strlen (buf);
if (len && *_RWSTD_CAT_SEP == buf [len - 1])
buf [len - 1] = '\0';
return buf;
}
// returns a pointer to a character array containing locale names separated
// by _RWSTD_CAT_SEP corresponding to the named locale (typically all the
// names will be the same except possibly for the native locale which can
// consist of facets from different named locales specified by the LC_XXX
// environment variables)
static const char*
__rw_expand_name (__rw_chararray &namebuf, const char *name)
{
const char *sep = strchr (name, *_RWSTD_CAT_SEP);
if (sep == name) {
// skip the first separator if it's at the beginning of `name'
// (e.g., SunOS locale names have the format "/en/C/ja/C/it/de")
sep = strchr (++name, *_RWSTD_CAT_SEP);
}
// true if all ctaegories are from the same locale, false otherwise
bool same_cats = true;
if (sep) {
// `name' contains a locale category separator; iterate over
// all locale categories to determine the name of the locale
// for each one of them
for (size_t i = 0; ; sep = strchr (name, *_RWSTD_CAT_SEP)) {
if (!sep)
sep = name + strlen (name);
const size_t catlen = sep - name;
// copy the read-only substring forming a locale name
// into a writeable buffer so that it can be NUL-terminated
const __rw_chararray shortname (name, catlen);
__rw_chararray longname;
// convert the (possibly abbreviated) name or a locale alias
// into the full name uniquely identifying the locale (e.g.,
// all of "de," "de_DE," and "german" can expand into the
// full canonical name "de_DE.ISO-8869-1"; this is necessary
// to guarantee that the internal locale repository doesn't
// contain duplicate entries)
const char* const fullname =
__rw_locale_name (__rw_cats [i].cat, shortname.data (),
longname);
if (!fullname)
return 0;
const size_t namelen = strlen (fullname);
_RWSTD_ASSERT (0 != namelen);
// determine if the new category name is the same as the first
// one (if it is, it is also the same as all subsequent ones,
// otherwise `same_cats' is already set to false)
if (same_cats && i)
same_cats = !memcmp (namebuf.data (), fullname, namelen)
&& *_RWSTD_CAT_SEP == namebuf.data () [namelen];
namebuf.append (fullname, namelen);
if (++i == __rw_n_cats || !*sep)
break;
namebuf.append (_RWSTD_CAT_SEP, 1);
name = sep + 1;
}
}
else if ('\0' == *name) {
// no separator found, set all categories
const __rw_setlocale loc (name, _RWSTD_LC_ALL, 1 /* nothrow */);
if (!loc.name ())
return 0;
// compose a string of category names separated by CAT_SEP
for (size_t i = 0; ; ) {
// retrieve the name of each idividual category
const char* const fullname = setlocale (__rw_cats [i].cat, 0);
// call to setlocale() must succeed if `loc' succeeded
_RWSTD_ASSERT (0 != fullname);
const size_t namelen = strlen (fullname);
if (same_cats && i)
same_cats = !memcmp (namebuf.data (), fullname, namelen)
&& *_RWSTD_CAT_SEP == namebuf.data () [namelen];
namebuf.append (fullname, namelen);
if (++i == __rw_n_cats)
break;
namebuf.append (_RWSTD_CAT_SEP, 1);
}
}
if (*namebuf.data ()) {
if (same_cats && __rw_facet::_C_opts & __rw_facet::_C_condensed_name) {
// all categories were the same, store just the first one
char* const data = namebuf.data ();
const size_t sepinx = strchr (data, *_RWSTD_CAT_SEP) - data;
// NUL-terminate at the first separator
namebuf.acquire (data, sepinx);
namebuf.append ("", 1);
}
}
else {
const char* const fullname =
__rw_locale_name (_RWSTD_LC_ALL, name, namebuf);
if (!fullname)
return 0;
}
return namebuf.data ();
#if 0
// points to the next category name
const char *next = strchr (name, *_RWSTD_CAT_SEP);
if (next && *next) {
// save the end of the first name to detect combined locale
// names composed of categories referring to the same locale
char *first_end = bufbeg;
// name is a string of locale category names; compose
// an equivalent string obtained by concatenating the
// results of calling setlocale() on each
// substring
const char *ps = name;
size_t __i = 0;
do {
if (!next)
next = ps + strlen (ps);
memcpy (pbuf, ps, next - ps);
pbuf [next - ps] = '\0';
if (__i < __rw_n_cats) {
const int cat = __rw_cats [__i++].cat;
// get the actual locale name for each category
// and append it to the end of the name buffer
if (!__rw_locale_name (cat, pbuf, pbuf, pbuf - bufbeg))
return 0; // name not recognized
}
const size_t len = strlen (pbuf);
if (first_end == bufbeg)
first_end = pbuf + len;
else if ( first_end
&& ( size_t (first_end - bufbeg) != len
|| memcmp (bufbeg, pbuf, len))) {
// category name differs from the first (i.e., category
// names will be cobined) no need to check further
first_end = 0;
}
// advance to the next name
pbuf += len;
*pbuf++ = *_RWSTD_CAT_SEP;
// reached the end of the (not `sep'-terminated) list
if ('\0' == *next)
break;
ps = next + 1;
next = strchr (ps, *_RWSTD_CAT_SEP);
} while (*ps);
*pbuf = '\0';
// assume the native locale for all missing categories
while (__i < __rw_n_cats) {
const int cat = __rw_cats [__i++].cat;
const __rw_setlocale loc ("", cat, 1 /* nothrow */);
if (!loc.name ())
return 0;
const char* catname = setlocale (cat, 0);
_RWSTD_ASSERT (0 != catname);
strcat (pbuf, catname);
// compare the category name with the first one and remember
// if they differ (to detect all the same category names)
if (first_end && memcmp (bufbeg, name, first_end - name))
first_end = 0;
pbuf += strlen (pbuf);
*pbuf++ = *_RWSTD_CAT_SEP;
}
// remove the last separator
if (pbuf [-1] == *_RWSTD_CAT_SEP)
pbuf [-1] = '\0';
// if non-0, all categories come from the same locale
// keep the first name and chop off the rest
if ( __rw_facet::_C_opts & __rw_facet::_C_condensed_name
&& first_end)
bufbeg [first_end - bufbeg] = '\0';
}
else {
// all categories have the same name in named locales
name = __rw_locale_name (_RWSTD_LC_ALL, name, bufbeg, bufsize);
if (!name)
return 0;
}
}
return bufbeg;
#endif
}
size_t __rw_locale::
_C_get_facet_inx (size_t id) const
{
// verify that facet's id is initialized
_RWSTD_ASSERT (id);
// standard facet's id are one greater than their index
if (id <= _C_n_std_facets)
return id - 1;
_RWSTD_ASSERT (!_C_n_usr_facets || _C_usr_facets);
// linear search through user-defined facets (if any)
for (size_t i = 0; i != _C_n_usr_facets; ++i) {
_RWSTD_ASSERT (_C_usr_facets [i]->_C_pid);
if (*_C_usr_facets [i]->_C_pid == id)
return i + _C_n_std_facets;
}
return _RWSTD_SIZE_MAX;
}
__rw_locale::
__rw_locale (const char *name)
: _C_usr_facets (0), _C_n_usr_facets (0), _C_ref (1),
_C_std_facet_bits (0), _C_byname_facet_bits (0)
{
_RWSTD_ASSERT (name);
// verify that name doesn't refer to an unnamed (combined) locale
_RWSTD_ASSERT (!strchr (name, '*'));
if ('\0' == *name) {
__rw_chararray fullname;
fullname.acquire (_C_namebuf, 0);
// get the names of locales for all standard categories
// avoiding dynamic memory allocation if possible
_C_name = __rw_expand_name (fullname, name);
fullname.release ();
// caller must guarantee that `name' is valid
_RWSTD_ASSERT (0 != _C_name);
// note that `name' may not be the same as `_C_name' at this point
}
else {
const size_t namelen = strlen (name) + 1;
// assume name is well-formed and valid (checked by caller)
char* const tmp = namelen < sizeof _C_namebuf
? _C_namebuf : new char [namelen];
memcpy (tmp, name, namelen);
_C_name = tmp;
}
// all facets are standard
_C_std_facet_bits = _C_all;
// byname facets determined below
_C_byname_facet_bits = 0;
if (strchr (_C_name, *_RWSTD_CAT_SEP)) {
// name consist of multiple locale names; iterate over them all
// setting byname facet bits for each named category
size_t inx = 0;
for (const char *nm = _C_name; *nm && inx != __rw_n_cats; ++nm) {
if ('C' != nm [0] || *_RWSTD_CAT_SEP != nm [1])
_C_byname_facet_bits |= __rw_cats [inx].facet_bits;
nm = strchr (nm, *_RWSTD_CAT_SEP);
if (!nm)
break;
++inx;
}
}
else {
// all facets (except for num_{get,put}) are byname in non-C locales
if (!__rw_is_C (_C_name))
_C_byname_facet_bits = _C_all;
}
#if defined (__i386__) || !defined (__GNUG__) || __GNUG__ < 3
memset (_C_std_facets, 0, sizeof _C_std_facets);
#else // !i86 || !gcc 3.x
// Working around a gcc 3.1 optimizer bug on SPARC (and possibly
// other RISC architectures) where, when generating PIC, _C_std_facets
// ends up being 4-byte aligned and gcc replaces memset() with a series
// of std %o1, [_C_std_facets + i] instructions with %o1 being 0 and i
// being 0, 8, 16, etc. and std operating on 64-bit double words. The
// first such misaligned std operation causes a SIGBUS
for (size_t i = 0; i != _C_n_std_facets; ++i)
_C_std_facets [i] = 0;
#endif // i86/gcc 3.x
}
// convert a LC_XXX constant to a locale::category value
/* static */ int __rw_locale::
_C_LC2category (int cat)
{
switch (cat) {
case _RWSTD_LC_ALL: cat = __rw_cat_all; break;
case _RWSTD_LC_COLLATE: cat = __rw_cat_collate; break;
case _RWSTD_LC_CTYPE: cat = __rw_cat_ctype; break;
case _RWSTD_LC_MONETARY: cat = __rw_cat_monetary; break;
case _RWSTD_LC_NUMERIC: cat = __rw_cat_numeric; break;
case _RWSTD_LC_TIME: cat = __rw_cat_time; break;
#ifdef _RWSTD_LC_MESSAGES
case _RWSTD_LC_MESSAGES: cat = __rw_cat_messages; break;
#endif
default:
// assume `cat' is already a locale::category value
break;
}
return cat;
}
// convert a LC_XXX constant to an internal bitset of facets
/* static */ int __rw_locale::
_C_LC2facet_bits (int cat)
{
int bits;
switch (cat) {
case _RWSTD_LC_ALL: bits = __rw_locale::_C_all; break;
case _RWSTD_LC_COLLATE: bits = __rw_locale::_C_collate; break;
case _RWSTD_LC_CTYPE: bits = __rw_locale::_C_ctype; break;
case _RWSTD_LC_MONETARY: bits = __rw_locale::_C_monetary; break;
case _RWSTD_LC_NUMERIC: bits = __rw_locale::_C_numeric; break;
case _RWSTD_LC_TIME: bits = __rw_locale::_C_time; break;
#ifdef _RWSTD_LC_MESSAGES
case _RWSTD_LC_MESSAGES: bits = __rw_locale::_C_messages; break;
#endif
default: {
// assume `cat' is a locale::category value
bits = 0;
if (cat & __rw_cat_collate)
bits |= _RW::__rw_locale::_C_collate;
if (cat & __rw_cat_ctype)
bits |= _RW::__rw_locale::_C_ctype;
if (cat & __rw_cat_monetary)
bits |= _RW::__rw_locale::_C_monetary;
if (cat & __rw_cat_numeric)
bits |= _RW::__rw_locale::_C_numeric;
if (cat & __rw_cat_time)
bits |= _RW::__rw_locale::_C_time;
if (cat & __rw_cat_messages)
bits |= _RW::__rw_locale::_C_messages;
break;
}
}
return bits;
}
__rw_locale::
~__rw_locale ()
{
// verify that object isn't being destroyed prematurely
_RWSTD_ASSERT (0 == _C_ref);
if (_C_name != _C_namebuf)
delete[] _RWSTD_CONST_CAST (char*, _C_name);
// decrement the reference count of each installed facet
// and delete each facet whose refcount drops to zero
for (size_t i = 0; i != _C_n_std_facets; ++i) {
if (!_C_std_facets [i])
continue;
_RWSTD_ASSERT (_C_std_facets [i]->_C_type != __rw_facet::_C_invalid);
if (_C_std_facets [i]->_C_type) {
// only standard facets (non-zero type) are managed
// decrement the facet's reference count and remove
// it from the global repository if it is 0
const __rw_facet* const pfacet =
__rw_facet::_C_manage (_C_std_facets [i],
_C_std_facets [i]->_C_type,
_C_std_facets [i]->_C_name,
0);
// verify that facet has been disposed of
_RWSTD_ASSERT (0 == pfacet);
_RWSTD_UNUSED (pfacet);
}
else {
// decrement ref count (must not drop below 0)
const size_t ref = _C_remove_ref (*_C_std_facets [i]);
_RWSTD_ASSERT (ref + 1U != 0);
if (0 == ref)
delete _C_std_facets [i];
}
}
// verify that either both are 0 or neither is 0
_RWSTD_ASSERT (!_C_n_usr_facets == !_C_usr_facets);
// decrement ref counts of user-defined facets and delete if necessary
for (size_t j = 0; j != _C_n_usr_facets; ++j) {
_RWSTD_ASSERT (_C_usr_facets [j]);
_RWSTD_ASSERT (_C_usr_facets [j]->_C_type != __rw_facet::_C_invalid);
// decrement ref count (must not drop below 0)
const size_t ref = _C_remove_ref (*_C_usr_facets [j]);
_RWSTD_ASSERT (ref + 1U != 0);
if (0 == ref)
delete _C_usr_facets [j];
}
delete[] _C_usr_facets;
}
extern "C" {
// compares two locales, returns 0 if equal, -1 if less, +1 otherwise
static int
cmplocales (const void *pv1, const void *pv2)
{
_RWSTD_ASSERT (0 != pv1);
_RWSTD_ASSERT (0 != pv2);
const __rw_locale* const pl1 =
*_RWSTD_STATIC_CAST (const __rw_locale* const*, pv1);
const __rw_locale* const pl2 =
*_RWSTD_STATIC_CAST (const __rw_locale* const*, pv2);
_RWSTD_ASSERT (0 != pl1->_C_get_name ());
_RWSTD_ASSERT (0 != pl1->_C_get_name ());
return strcmp (pl1->_C_get_name (), pl2->_C_get_name ());
}
// compares a key to a locale, returns 0 if equal, -1 if less, +1 otherwise
static int
cmplocale (const void *pv1, const void *pv2)
{
_RWSTD_ASSERT (0 != pv1);
_RWSTD_ASSERT (0 != pv2);
const char* const nm1 = _RWSTD_STATIC_CAST (const char*, pv1);
const __rw_locale* const plocale =
*_RWSTD_STATIC_CAST (const __rw_locale* const*, pv2);
_RWSTD_ASSERT (0 != nm1);
_RWSTD_ASSERT (0 != plocale->_C_get_name ());
return strcmp (nm1, plocale->_C_get_name ());
}
} // extern "C"
// manages a global, per-process repository of locale bodies according
// to the following table:
// plocale locname algorithm
// 0 0 retrieve the global locale
// 0 non-0 find named locale in repository and increment
// its ref count if found, or construct it with
// ref count of 1 and add it otherwise
// non-0 0 make `plocale' the new global locale and
// return the previous global locale
// non-0 non-0 find locale in repository, decrement its ref
// count, and remove it if the ref count is 0
/* static */ __rw_locale*
__rw_locale::
_C_manage (__rw_locale *plocale, const char *locname)
{
// a per-process array of locale body pointers
static __rw_locale* locale_buf [8];
static __rw_locale** locales = locale_buf;
static __rw_locale* classic = 0;
static size_t n_locales = 0;
static size_t locale_bufsize =
sizeof locale_buf / sizeof *locale_buf;
if (!locname) {
// manage the global locale
static __rw_locale* global = 0;
if (!global) {
static volatile long ginit /* = 0 */;
if (!ginit && 1 == _RWSTD_ATOMIC_PREINCREMENT (ginit, false)) {
global = _C_manage (0, "C");
ginit += 1000;
}
else {
while (ginit < 1000);
}
}
// re-entrant
_RWSTD_MT_STATIC_GUARD (_RW::__rw_locale);
__rw_locale* const tmp = global;
_RWSTD_ASSERT (0 != tmp);
if (plocale) {
// replace the global locale with `plocale'
// increment the future global locale's ref count first
_RWSTD_ATOMIC_PREINCREMENT (plocale->_C_ref, false);
// and then set the global locale
global = plocale;
}
else {
// retrieve the global locale
// always increment a ref count, even if `global' is being set
// (and its ref count is > 0), to prevent its ref count from
// dropping to 0
_RWSTD_ATOMIC_PREINCREMENT (tmp->_C_ref, false);
}
return tmp;
}
// re-entrant to protect static local data structures
// (not the locales themselves)
_RWSTD_MT_STATIC_GUARD (_RW::__rw_locale);
// determine whether `locname' refers to the classic C locale
// (e.g., it could be any of "C", "POSIX", "C C C C C C", etc.)
bool is_C_locale = __rw_is_C (locname);
// substitute the fully expanded libc name for the classic C locale,
// i.e., the result of calling std::setlocale ("C")
if (is_C_locale)
locname = __rw_C_locale_name;
__rw_chararray fullname;
if (!plocale && !is_C_locale) {
// to avoid unnecessary calls to setlocale() only get cat names
// when composing a locale name, otherwise the locale name is
// assumed to be correct and complete
locname = __rw_expand_name (fullname, locname);
if (!locname)
return 0; // program error
is_C_locale = __rw_is_C (locname);
if (is_C_locale)
locname = __rw_C_locale_name;
}
if (plocale) {
// remove locale from the locale repository (if it is found) and
// destroy it (the classic C locale is never destroyed)
_RWSTD_ASSERT (0 != n_locales);
const void* const pl =
bsearch (locname, locales, n_locales, sizeof *locales, &cmplocale);
if (pl) {
const size_t inx =
_RWSTD_STATIC_CAST (const __rw_locale* const*, pl)
- _RWSTD_CONST_CAST (const __rw_locale* const*, locales);
plocale = locales [inx];
const size_t ref =
_RWSTD_ATOMIC_PREDECREMENT (plocale->_C_ref, false);
_RWSTD_ASSERT (_RWSTD_SIZE_MAX != ref);
if (0 == ref) {
static const size_t bufsize =
sizeof locale_buf / sizeof *locale_buf;
--n_locales;
if (n_locales < bufsize / 2 && locales != locale_buf) {
_RWSTD_ASSERT (inx < bufsize);
// when the number of locales in the dynamically
// allocated repository drops below half the capacity
// of the statically allocated buffer, copy locales
// from the former into the latter and deallocate the
// former
memcpy (locale_buf, locales, inx * sizeof (*locales));
memcpy (locale_buf + inx, locales + inx + 1,
(n_locales - inx) * sizeof (*locales));
delete[] locales;
// point at the statically allocated buffer and reset
// the current capacity to the original capacity
locales = locale_buf;
locale_bufsize = bufsize;
}
else {
// move facet pointers back
memmove (locales + inx, locales + inx + 1,
(n_locales - inx) * sizeof (*locales));
}
_RWSTD_ASSERT (0 != plocale->_C_name);
// only named locales (i.e., other than the C locale)
// are deleted
if (!__rw_is_C (plocale->_C_name))
delete plocale;
}
}
else {
// object is the classic C locale which is never destroyed
_RWSTD_ASSERT (plocale->_C_name);
_RWSTD_ASSERT (__rw_is_C (plocale->_C_name));
const size_t ref =
_RWSTD_ATOMIC_PREDECREMENT (plocale->_C_ref, false);
_RWSTD_ASSERT (ref + 1U != 0);
_RWSTD_UNUSED (ref);
}
plocale = 0;
}
else {
// find locale in the repository or construct it if not found
const void* const pcl =
bsearch (locname, locales, n_locales, sizeof *locales, &cmplocale);
if (pcl) {
// facet already exists in the repository, bump up its ref count
void* const pl = _RWSTD_CONST_CAST (void*, pcl);
plocale = *_RWSTD_STATIC_CAST (__rw_locale**, pl);
_RWSTD_ASSERT (0 != plocale);
_RWSTD_ATOMIC_PREINCREMENT (plocale->_C_ref, false);
}
else {
if (n_locales == locale_bufsize) {
// reallocate buffer of facet pointers
__rw_locale** const tmp =
new __rw_locale* [n_locales * 2];
memcpy (tmp, locales, n_locales * sizeof *tmp);
if (locales != locale_buf)
delete[] locales;
locales = tmp;
locale_bufsize *= 2;
}
if (__rw_is_C (locname)) {
if (!classic) {
// the classic C locale is statically allocated
// and not destroyed during the lifetime of the process
static union {
void* _C_align;
char _C_buf [sizeof (__rw_locale)];
} classic_body;
// construct a locale body in place
// with the initial reference count of 1
classic = new (&classic_body) __rw_locale (locname);
_RWSTD_ASSERT (1 == classic->_C_ref);
}
else {
_RWSTD_ASSERT (0 != classic);
_RWSTD_ATOMIC_PREINCREMENT (classic->_C_ref, false);
}
plocale = classic;
}
else {
plocale = new __rw_locale (locname);
_RWSTD_ASSERT (1 == plocale->_C_ref);
}
_RWSTD_ASSERT (plocale);
// add locale body pointer to the end of the repository
locales [n_locales++] = plocale;
// sort pointers for fast access
qsort (locales, n_locales, sizeof *locales, &cmplocales);
}
}
return plocale;
}
// returns true iff all the facets in the given locale category
// installed in `*this' are being globally managed by the library
// if (cat == locale::none) holds, returns true iff the entire
// locale body is being managed
bool __rw_locale::
_C_is_managed (int cat) const
{
// `cat' must be a valid category
_RWSTD_ASSERT (_C_check_category (_C_LC2category (cat)));
if (cat == __rw_cat_none) {
// locales containing any user-defined facets are not globally managed
if (_C_n_usr_facets) {
_RWSTD_ASSERT (0 != _C_usr_facets);
return false;
}
// unless all facets in the same category come either from
// the C locale or from some named locale the locale object
// containing the facets is not managed (this test doesn't
// detect categores of all byname facets not all of which
// belong to the same named locale)
if ( (_C_std_facet_bits & _C_all) != _C_all
|| (_C_byname_facet_bits & _C_collate)
&& (_C_byname_facet_bits & _C_collate) != _C_collate
|| (_C_byname_facet_bits & _C_ctype)
&& (_C_byname_facet_bits & _C_ctype) != _C_ctype
|| (_C_byname_facet_bits & _C_monetary)
&& (_C_byname_facet_bits & _C_monetary) != _C_monetary
|| (_C_byname_facet_bits & _C_numeric)
&& (_C_byname_facet_bits & _C_numeric) != _C_numeric
|| (_C_byname_facet_bits & _C_time)
&& (_C_byname_facet_bits & _C_time) != _C_time
|| (_C_byname_facet_bits & _C_messages)
&& (_C_byname_facet_bits & _C_messages) != _C_messages)
return false;
// check whether all _byname facets in each category belong
// to the same named locale; facets witout byname forms are
// excluded from this test
// note that the locale may have a combined name such as
// "/de/en/fr/it/ja/ru"
const char* locname = _C_name ? _C_name : "C";
if (*_RWSTD_CAT_SEP == *locname)
++locname;
// find the first category separator (if any) and compute
// the length of the name of the first category's locale
// (don't bother when combined locales don't exist, i.e.,
// when no locale separator is defined)
const char* next =
*_RWSTD_CAT_SEP ? strchr (locname, *_RWSTD_CAT_SEP) : 0;
size_t loclen = next ? next - locname : strlen (locname);
// iterate over all standard facets, comparing the name of the
// locale each comes from with the name of the category encoded
// in the locale name for equality; a mismatch indicates that
// the locale is not managed
for (size_t i = 0, catinx = 0; i != _C_n_std_facets; ++i) {
if (0 == _C_std_facets [i]) {
// skip facets that are not installed
continue;
}
const __rw_facet::_C_facet_type facet_type =
_C_get_facet_type (*_C_std_facets [i]);
switch (facet_type) {
case __rw_facet::_C_money_get:
case __rw_facet::_C_money_put:
case __rw_facet::_C_num_get:
case __rw_facet::_C_num_put:
case __rw_facet::_C_wmoney_get:
case __rw_facet::_C_wmoney_put:
case __rw_facet::_C_wnum_get:
case __rw_facet::_C_wnum_put:
// skip facets with no _byname forms
continue;
default:
break;
}
// iterate through the names of categories in the combined
// locale name looking for one that the facet belongs to
while (next && !(__rw_cats [catinx].facet_bits & (1 << i))) {
if (*next) {
// advance past the category separator to the name
// of the next category in the (potentially combined)
// locale name
_RWSTD_ASSERT (*next == *_RWSTD_CAT_SEP);
locname = ++next;
++catinx;
}
else {
// reset to the name of the first category
locname = _C_name ? _C_name : "C";
catinx = 0;
}
// skip over the category separator
if (*_RWSTD_CAT_SEP == *locname)
++locname;
next = strchr (locname, *_RWSTD_CAT_SEP);
if (next)
loclen = next - locname;
else {
loclen = strlen (locname);
next = "";
}
}
// get the facet name (or "C" if null)
const char* const facet_name =
_C_std_facets [i]->_C_get_name ();
_RWSTD_ASSERT (0 != facet_name);
_RWSTD_ASSERT (0 != locname);
// mismatch indicates a combined category
if ( strlen (facet_name) != loclen
|| memcmp (locname, facet_name, loclen)) {
// locales with heterogeneous categories are not managed
return false;
}
}
// locales with all heterogeneous categories are managed
return true;
}
// in order for the entire category to be considered globally managed,
// all its facets must come from the same (named or unnamed) locale
// i.e., if the LC_CTYPE category contains codecvt from "de_DE" and
// ctype from "en_US", the category is not considered globally managed
// because it doesn't have a unambiguous name
const int bits = _C_LC2facet_bits (cat);
const int std_bits = bits & int (_C_std_facet_bits);
if (std_bits ? bits == std_bits
: bits == (bits & int (_C_byname_facet_bits)))
return true;
return false;
}
} // namespace __rw
/***************************************************************************/
#include <memory>
#include <loc/_locale.h>
#include <rw/_defs.h>
_RWSTD_NAMESPACE (std) {
const locale&
locale::
operator= (const locale &rhs) _THROWS (())
{
// copy body and bump up a ref count in `rhs'
_RW::__rw_locale* const body = rhs._C_body;
_RWSTD_ATOMIC_PREINCREMENT (rhs._C_body->_C_ref, false);
// _C_body is never 0 except in objects still being constructed by
// the combining ctor locale::locale<Facet>(const locale&, Facet*)
if (_C_body) {
// dispose of old body (dtor decrements ref count and possibly deletes)
locale destroy (*_C_body);
_RWSTD_UNUSED (destroy);
}
// assign new body
_C_body = body;
return *this;
}
} // namespace std