blob: 8d81a3563021c0ebfd714d0d556c0596bf5c3ef3 [file] [log] [blame]
/***************************************************************************
*
* locale_combine.cpp - definition of std::locale::combine() and related
* member functions
*
* $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 1994-2006 Rogue Wave Software.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#include <rw/_defs.h>
#include <locale.h>
#include <stdio.h>
#include <string.h> // for memxxx() and strxxx() functions
#include <loc/_locale.h>
#include "access.h"
#include "locale_body.h"
#include "setlocale.h"
_RWSTD_NAMESPACE (__rw) {
// constructs a new locale name from two locale names, combining
// all but the category given by `cat' from `name_1' and the rest
// from `name_2'
static void
__rw_combine_names (__rw_chararray &locname,
const char *name_1, const char *name_2, int cat)
{
_RWSTD_ASSERT (0 != name_1);
_RWSTD_ASSERT (0 != name_2);
// `cat' must be a locale::category (and not a LC_XXX value)
_RWSTD_ASSERT (__rw_locale::_C_check_category (cat));
for (_RWSTD_SIZE_T i = 0; i != __rw_n_cats; ++i) {
const char *nxt1 = strchr (name_1, *_RWSTD_CAT_SEP);
const char *nxt2 = strchr (name_2, *_RWSTD_CAT_SEP);
if (!nxt1 || !*nxt1)
nxt1 = name_1 + strlen (name_1);
if (!nxt2 || !*nxt2)
nxt2 = name_2 + strlen (name_2);
// convert a LC_XXX constant to a locale::category value
const int c = __rw_locale::_C_LC2category (__rw_cats [i].cat);
const char *from; // copy from where
_RWSTD_SIZE_T len; // this many chars
if (cat & c) { // category comes from `other'
from = name_2;
len = nxt2 - name_2;
}
else { // category comes from `one'
from = name_1;
len = nxt1 - name_1;
}
if (locname.size ())
locname.append (_RWSTD_CAT_SEP, 1);
locname.append (from, len);
// advance to the next category name in each name
if (*nxt1)
name_1 = nxt1 + 1;
if (*nxt2)
name_2 = nxt2 + 1;
}
}
void __rw_locale::
_C_construct (const __rw_locale &rhs, const __rw_facet *pfacet)
{
// called from ctor only, members are not initialized
_C_ref = 1;
// copy standard facets from other locale
#if defined (__i386__) || !defined (__GNUG__) || __GNUG__ < 3
memcpy (_C_std_facets, rhs._C_std_facets,
_C_n_std_facets * sizeof *_C_std_facets);
#else // !i86 || !gcc 3.x
// Working around a bug (most probably) identical to the one described
// and fixed in __rw_locale::__rw_locale, but related to the use of
// memcpy.
for(_RWSTD_SIZE_T i = 0; i != _C_n_std_facets; i++)
_C_std_facets [i] = rhs._C_std_facets [i];
#endif // i86/gcc 3.x
_RWSTD_ASSERT (!pfacet || pfacet->_C_pid);
// get facet's index into one of the facet arrays
_RWSTD_SIZE_T inx = pfacet
? rhs._C_get_facet_inx (*pfacet->_C_pid) : (_RWSTD_SIZE_T)(-1);
// extend size if facet isn't installed in `rhs'
_C_n_usr_facets = rhs._C_n_usr_facets
+ (inx > rhs._C_n_usr_facets + _C_n_std_facets);
// allocate a buffer for user facets if necesary
_C_usr_facets = _C_n_usr_facets ? new __rw_facet* [_C_n_usr_facets] : 0;
// copy facets from other locale
memcpy (_C_usr_facets, rhs._C_usr_facets,
rhs._C_n_usr_facets * sizeof *_C_usr_facets);
_RWSTD_ASSERT (rhs._C_name);
_C_name = 0;
// copy facet bits from `rhs' and adjust below as necessary
_C_std_facet_bits = rhs._C_std_facet_bits;
_C_byname_facet_bits = rhs._C_byname_facet_bits;
if (pfacet) {
__rw_facet* const pf = _RWSTD_CONST_CAST (__rw_facet*, pfacet);
// overwrite an existing pointer or add a new one
if (inx < _C_n_std_facets) {
_C_std_facets [inx] = pf;
const _RWSTD_SIZE_T facet_bit = 1UL << inx;
// set or clear a corresponding bit based on whether
// the facet is one of the standard facets or not
// (user-defined objects even if they are of standard
// facet types, are not considered standard facets)
if (pf->_C_type > 0)
_C_std_facet_bits |= facet_bit;
else
_C_std_facet_bits &= ~facet_bit;
// ditto for named facet bitmap
if (pf->_C_type & 1) // even types are _byname
_C_byname_facet_bits &= ~facet_bit;
else
_C_byname_facet_bits |= facet_bit;
// construct a new name
// _C_name =
// _C_make_name (_C_namebuf, sizeof _C_namebuf, rhs, *pfacet);
}
else if ((_RWSTD_SIZE_T)(-1) == inx)
_C_usr_facets [_C_n_usr_facets - 1] = pf;
else
_C_usr_facets [inx - _C_n_std_facets] = pf;
}
if (!_C_name) {
// simply copy name from `rhs'
const _RWSTD_SIZE_T size = strlen (rhs._C_name) + 1;
char* const name = size <= sizeof _C_namebuf
? _C_namebuf : new char [size];
memcpy (name, rhs._C_name, size);
_C_name = name;
}
// increment reference counts for facets (including the newly added one)
for (inx = 0; inx != _C_n_std_facets; ++inx) {
if (_C_std_facets [inx])
_RWSTD_ATOMIC_PREINCREMENT (_C_std_facets [inx]->_C_ref,
_C_std_facets [inx]->_C_mutex);
}
for (inx = 0; inx != _C_n_usr_facets; ++inx) {
_RWSTD_ATOMIC_PREINCREMENT (_C_usr_facets [inx]->_C_ref,
_C_usr_facets [inx]->_C_mutex);
}
}
void __rw_locale::
_C_construct (const __rw_locale &one, const __rw_locale &other, int cat)
{
// category must be valid at this point (checked by caller)
_RWSTD_ASSERT (_C_check_category (cat));
// called from ctor only, members are not initialized
_C_ref = 1;
// size is the same as that of `one'
_C_n_usr_facets = one._C_n_usr_facets;
// allocate only if internal buffer is insufficient
_C_usr_facets = _C_n_usr_facets ? new __rw_facet* [_C_n_usr_facets] : 0;
// copy standard facets to `one' from `other'
memcpy (_C_std_facets, one._C_std_facets,
_C_n_std_facets * sizeof *_C_std_facets);
// copy user-defined facets (if any)
memcpy (_C_usr_facets, one._C_usr_facets,
_C_n_usr_facets * sizeof *_C_usr_facets);
// compute and assign facet bitmaps
const int bits = _C_LC2facet_bits (cat);
_C_std_facet_bits = one._C_std_facet_bits & ~bits
| other._C_std_facet_bits & bits;
_C_byname_facet_bits = one._C_byname_facet_bits & ~bits
| other._C_byname_facet_bits & bits;
for (_RWSTD_SIZE_T i = 0; i != _C_n_std_facets; ++i) {
// each facet is stored at an index equal to its (id - 1)
// convert an LC_XXX constant to a locale::category value
const int c = _C_LC2category (__rw_get_cat (int (i) + 1));
if (cat & c) {
// assign/overwrite corresponding facets
_C_std_facets [i] = other._C_std_facets [i];
}
if (_C_std_facets [i]) {
// bump up the facet's reference count of *this' facet
_RWSTD_ATOMIC_PREINCREMENT (_C_std_facets [i]->_C_ref,
_C_std_facets [i]->_C_mutex);
}
}
// increment the ref count of user-defined facets (if any)
for (_RWSTD_SIZE_T j = 0; j != _C_n_usr_facets; ++j) {
_RWSTD_ASSERT (_C_usr_facets [j]);
_RWSTD_ATOMIC_PREINCREMENT (_C_usr_facets [j]->_C_ref,
_C_usr_facets [j]->_C_mutex);
}
__rw_chararray locname;
__rw_combine_names (locname, one._C_name, other._C_name, cat);
if (locname.size () >= one._C_namebuf_size)
_C_name = locname.release ();
else {
_C_name = _C_namebuf;
memcpy (_C_namebuf, locname.data (), locname.size () + 1);
}
}
_RW::__rw_locale* __rw_locale::
_C_combine (const __rw_facet *pfacet) const
{
_RWSTD_ASSERT (pfacet);
// template ctor: locale::locale (const locale&, Facet*)
// with `facet' being installed (e.g., adding or replacing
// an existing one)
// verify that Facet::id has been initialized
_RWSTD_ASSERT (pfacet->_C_pid);
_RWSTD_ASSERT (*pfacet->_C_pid);
// get the locale name ("C" if it's NUL)
const char* const one_locname = _C_name ? _C_name : "C";
// get the name of the locale `*pfacet' belongs to
const char* const facet_locname =
pfacet->_C_name ? pfacet->_C_name : "C";
// get the locale::category that `*pfacet' belongs to
const int facet_cat =
_C_LC2category (__rw_get_cat (int (*pfacet->_C_pid)));
bool managed = false;
if (pfacet->_C_type && !_C_n_usr_facets) {
// facet is one of the (perhaps byname) standard facets
// compute the number of the bit in _C_std_facet_bits or
//_C_byname_facet_bits corresponding to the facet being
// used (i.e., `*pfacet')
const _RWSTD_SIZE_T facet_bit =
(_RWSTD_SIZE_T)1U << (*pfacet->_C_pid - 1U);
// a locale object is globally managed if all its facets are objects
// of standard facet types (ordinary or byname) that were constructed
// by the library and within each locale category all facets belong
// to the same facet
// managed locales have names that completely describe their facets
// and from which they can be unambiguously and fully constructed
//
// note that in the code snippet below, x0 is not a managed locale
// (its name is "*"), but y0 is a managed locale (on SunOS, its name
// is "/de_DE/de_DE/de_DE/de_DE/de_DE/en_US"); x1 is the same as x0
// and y1 is the same as de:
//
// locale de ("de_DE");
// locale en ("en_US");
// locale x0 = de.combine<messages<char> >(en);
// locale y0 = x0.combine<messages<wchar_t> >(en);
// locale x1 = y0.combine<messages<wchar_t> >(de);
// locale y1 = x1.combine<messages<char> >(de);
// assert (de == y1);
// assert (x0 == x1);
if (__rw_locale::_C_all == (_C_std_facet_bits | facet_bit)) {
// compute the bitmask corresponding to all the facets in
// the same category as the one to which `*pfacet' belongs
const int cat_bits = _C_LC2facet_bits (facet_cat);
// get the index into the _C_std_facets array for `pfacet'
const _RWSTD_SIZE_T facet_inx =
_C_get_facet_inx (*pfacet->_C_pid);
managed = true;
// iterate over all facets that belong the same category
// as `*pfacet' and compare the name of each with the name
// of `*pfacet' to determine if they all have the same name
// _byname facets that are not installed yet (i.e., whose
// pointers are 0 come from a locale whose name is given
// by the name of the whole locale
for (_RWSTD_SIZE_T i = 0; i != _C_n_std_facets; ++i) {
// skip facets from other categories
if (!((1 << i) & cat_bits))
continue;
// skip the facet being replaced
if (facet_inx == i)
continue;
const char* const ones_facet_locname =
_C_std_facets [i] ? _C_std_facets [i]->_C_name
? _C_std_facets [i]->_C_name : "C" : one_locname;
if (strcmp (facet_locname, ones_facet_locname)) {
managed = false;
break;
}
}
}
}
__rw_locale *plocale;
if (managed) {
__rw_chararray locname;
__rw_combine_names (locname, one_locname, facet_locname, facet_cat);
plocale = __rw_locale::_C_manage (0, locname.data ());
_RWSTD_ASSERT (0 != plocale);
_RWSTD_ASSERT (plocale->_C_is_managed (_STD::locale::none));
}
else {
plocale = new __rw_locale (*this, pfacet);
_RWSTD_ASSERT (0 != plocale);
_RWSTD_ASSERT (!plocale->_C_is_managed (_STD::locale::none));
}
return plocale;
}
__rw_locale* __rw_locale::
_C_combine (const __rw_locale &other, int cat)
{
// convert a possible LC_XXX constant to a locale::category value
cat = _C_LC2category (cat);
// verify that the category argument is valid
if (!_C_check_category (cat))
return 0;
// combining ctor: locale (const locale&, const locale&, cat)
if (this == &other || _STD::locale::none == cat) {
// same as copy ctor (copying all facets from `*this')
// body may or may not be globally managed
_RWSTD_ATOMIC_PREINCREMENT (_C_ref, false);
return this;
}
#if 0 // disabled
// 22.1.1.2, p14: only facets that implement categories in `cat' are
// to be copied from `other', i.e., not necessarily user-defined facets
if (_RWSTD_LC_ALL == cat || _STD::locale::all == cat) {
// same as copy ctor (copying all facets from `other')
// body may or may not be globally managed
_RWSTD_ATOMIC_PREINCREMENT (other._C_ref, false);
return &other;
}
#endif // 0/1
if ( _C_is_managed (_STD::locale::all & ~cat)
&& other._C_is_managed (cat)) {
// determine new locale name from `*this' and `other's, taking all of
// this's category names except for those given by `cat', which come
// from `other'
const char *name_1 = _C_name;
const char *name_2 = other._C_name;
if (0 == strcmp (name_1, name_2)) {
// if locale names are the same, return a globally managed body
__rw_locale* const plocale = __rw_locale::_C_manage (0, name_1);
_RWSTD_ASSERT (0 != plocale);
_RWSTD_ASSERT (plocale->_C_is_managed (_STD::locale::none));
return plocale;
}
__rw_chararray locname;
// compose the new name
__rw_combine_names (locname, name_1, name_2, cat);
// return a globally managed body
__rw_locale* const plocale =
__rw_locale::_C_manage (0, locname.data ());
_RWSTD_ASSERT (0 != plocale);
_RWSTD_ASSERT (plocale->_C_is_managed (_STD::locale::none));
return plocale;
}
// one or both locales are not globally managed (one or more of
// their facets are of use-defined types, possibly derived from
// the standard facets), and thus the combined locale cannot be
// globally managed either
__rw_locale *plocale = new __rw_locale (*this, other, cat);
_RWSTD_ASSERT (!plocale->_C_is_managed (_STD::locale::none));
return plocale;
}
/* static */ __rw_locale* __rw_locale::
_C_get_body (__rw_locale *one,
__rw_locale *other,
const char *locname,
int cat,
const __rw_facet *pfacet)
{
__rw_chararray realname;
// see if `locname' contains embedded "LC_XXX=..." sequences
if (locname && !strncmp (locname, "LC_", 3)) {
// convert a combined locale name (consisting of a sequence
// of `sep'-separated LC_XXX=<name> substrings) to a normalized
// form in the expected (internal) format and order
const char *pcatnames [__rw_n_cats] = { 0 };
// try the libc "native" separator first, semicolon next
const char *sep = strchr (locname, *_RWSTD_CAT_SEP);
if (!sep)
sep = ";";
for (const char *s = locname; *s; ) {
const char *next = strchr (s, *sep);
if (!next)
next = s + strlen (s);
const char* const endcat = strchr (s, '=');
if (!endcat)
return 0; // error: missing `=' after LC_XXX
// go through the LC_XXX category names, trying to find
// a match; if found, remember its position (duplicates
// are not allowed)
for (_RWSTD_SIZE_T i = 0; i != __rw_n_cats; ++i) {
if (!strncmp (__rw_cats [i].name, s, endcat - s)) {
if (pcatnames [i])
return 0; // error: duplicate LC_XXX
pcatnames [i] = endcat + 1;
break;
}
}
s = *next ? next + 1 : next;
}
// compose a normalized locale name out of category names
_RWSTD_SIZE_T size = 0;
for (_RWSTD_SIZE_T i = 0; i != __rw_n_cats; ++i) {
if (!pcatnames [i]) {
// use "C" for missing LC_XXX values
pcatnames [i] = "C";
}
const char *endcat = strchr (pcatnames [i], *sep);
if (!endcat)
endcat = pcatnames [i] + strlen (pcatnames [i]);
const _RWSTD_SIZE_T catsize = endcat - pcatnames [i];
// append name followed by the libc "native" separator
realname.append (pcatnames [i], endcat - pcatnames [i]);
realname.append (_RWSTD_CAT_SEP, 1);
size += catsize;
}
locname = realname.data ();
}
else if (locname && *locname == *_RWSTD_CAT_SEP) {
// advance past the leading category separator if present
++locname;
}
if (!one && !other && !locname && _STD::locale::none == cat && !pfacet) {
// implements default ctor: locale::locale () throw ()
__rw_locale* const global = __rw_locale::_C_manage (0, 0);
_RWSTD_ASSERT (0 != global);
return global;
}
if (one && !other && !locname && _STD::locale::none == cat && !pfacet) {
// copy ctor: locale::locale (const locale&)
// or combining ctor: locale::locale (const locale&, Facet*)
// (in the second case with Facet* being 0)
// body may or may not be globally managed
// or locale::operator=(const locale&)
_RWSTD_ATOMIC_PREINCREMENT (one->_C_ref, false);
return one;
}
if (!one && !other && locname && _STD::locale::none == cat && !pfacet) {
// ctor: locale::locale (const char*)
// if successful, body is globally managed
__rw_locale* const plocale = __rw_locale::_C_manage (0, locname);
_RWSTD_ASSERT (!plocale || plocale->_C_is_managed (_STD::locale::none));
return plocale;
}
if (one && !other && !locname && _STD::locale::none == cat && pfacet) {
// template ctor: locale::locale (const locale&, Facet*)
// with `pfacet' being installed (e.g., adding or replacing
// an existing one)
// template <class Facet> locale::combine(const locale&) is
// trivially implemented inline in terms of the template ctor
__rw_locale* const plocale = one->_C_combine (pfacet);
_RWSTD_ASSERT (0 != plocale);
return plocale;
}
if (one && !other && locname && !pfacet) {
// combining ctor: locale (const locale&, const char*, category)
// construct a temporary locale from locale name `locname'
const _STD::locale tmp (locname);
// construct a locale combined from `one' and the temporary
__rw_locale* const plocale = one->_C_combine (*tmp._C_body, cat);
return plocale;
}
if (one && other && !locname && !pfacet) {
// combining ctor: locale (const locale&, const locale&, category)
return one->_C_combine (*other, cat);
}
_RWSTD_ASSERT (!"logic error: bad combination of arguments");
return 0;
}
} // namespace __rw
_RWSTD_NAMESPACE (std) {
// outlined to hide implementation details
locale::
locale (const char *locname)
{
// verify 22.1.1.2, p7: `locname' must not be null
_RWSTD_REQUIRES (locname,
(_RWSTD_ERROR_LOCALE_BAD_NAME,
_RWSTD_FUNC ("locale::locale (const char*)"), "(null)"));
_C_body = _RW::__rw_locale::_C_get_body (0, 0, locname, none, 0);
// verify 22.1.1.2, p7: `locname' must be valid
_RWSTD_REQUIRES (_C_body,
(_RWSTD_ERROR_LOCALE_BAD_NAME,
_RWSTD_FUNC ("locale::locale (const char*)"), locname));
}
// outlined to hide implementation details
locale::
locale (const locale &rhs, const char *locname, category cat)
{
// verify 22.1.1.2, p10: `locname' must not ne null
_RWSTD_REQUIRES (locname,
(_RWSTD_ERROR_LOCALE_BAD_NAME,
_RWSTD_FUNC ("locale::locale (const locale&, "
"const char*, category)"), "(null"));
_C_body = _RW::__rw_locale::_C_get_body (rhs._C_body, 0, locname, cat, 0);
// verify 22.1.1.2, p10: `locname' must be valid
_RWSTD_REQUIRES (_C_body,
(_RWSTD_ERROR_LOCALE_BAD_NAME,
_RWSTD_FUNC ("locale::locale (const locale&, "
"const char*, category)"), locname));
}
// outlined to hide implementation details
locale::
locale (const locale &one, const locale &other, category cat)
: _C_body (_RW::__rw_locale::_C_get_body (one._C_body, other._C_body,
0, cat, 0))
{
// verify 22.1.1.2, p14 (missing requirement): category must be valid
_RWSTD_REQUIRES (_C_body,
(_RWSTD_ERROR_LOCALE_BAD_CAT,
_RWSTD_FUNC ("locale::locale (const locale&, "
"const locale&, category)"), cat));
}
// outlined to hide implementation details
/* private */ locale::
locale (_RW::__rw_locale &rhs, const facet *pfacet)
: _C_body (_RW::__rw_locale::_C_get_body (&rhs, 0, 0, none, pfacet))
{
// empty
}
const locale::facet* locale::
_C_get_facet (const id &fid) const
{
// initialize id if not yet initialized
if (!fid._C_id)
fid._C_init ();
// properly initialized facet id's are non-zero
_RWSTD_ASSERT (0 != fid._C_id);
_RWSTD_ASSERT (0 != _C_body);
// find the index at which the facet is stored in one of the facet arrays
const _RWSTD_SIZE_T inx = _C_body->_C_get_facet_inx (fid._C_id);
if ((_RWSTD_SIZE_T)(-1) == inx)
return 0;
const locale::facet* const pfacet = inx < _RW::__rw_locale::_C_n_std_facets
? _C_body->_C_std_facets [inx]
: _C_body->_C_usr_facets [inx - _RW::__rw_locale::_C_n_std_facets];
if (pfacet) {
// facet id must have been initialized to point
// to the numeric member of the static Facet::id
_RWSTD_ASSERT (pfacet->_C_pid);
// make sure the static Facet::id matches its (non-static) id member
_RWSTD_ASSERT (*pfacet->_C_pid == fid._C_id);
}
return pfacet;
}
} // namespace std