blob: bfe77850df6a3f99e897627de108f56fa43cf35f [file] [log] [blame]
/***************************************************************************
*
* messages.cpp
*
* $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 2000-2006 Rogue Wave Software.
*
**************************************************************************/
#define _RWSTD_LIB_SRC
#include <rw/_defs.h>
#include <new> // for placement new
#include <string.h> // for memcpy, size_t
#include <loc/_locale.h>
#include <loc/_messages.h>
#include <rw/_mutex.h>
#include "podarray.h"
#if !defined (_RWSTD_NO_NL_TYPES_H) && !defined (_RWSTD_NO_CATOPEN_CATGETS)
# include <nl_types.h>
#else // _RWSTD_NO_NL_TYPES_H || _RWSTD_NO_CATOPEN_CATGETS
# include "catalog.h"
#endif // !_RWSTD_NO_NL_TYPES_H && !_RWSTD_NO_CATOPEN_CATGETS
#define _RWSTD_BAD_CATD ((nl_catd)-1)
_RWSTD_NAMESPACE (__rw) {
// Implementation structure private to this module -- __rw_open_cat_data.
// Keeps track of the association between an open message catalog (as
// identified by the nl_catd handle returned by the C library catopen
// function) and the locale specified by the user for character translation
// in the call to messages<_CharT>::open.
//
// Also used as a parameter to rw_get_static_mutex to ensure uniqueness
// of the mutex object used in sync'ing the threads using __rw_cat...
// (see RWSTD_MT_STATIC_GUARD below)
struct __rw_open_cat_data
{
nl_catd catd;
__rw_aligned_buffer<_STD::locale> loc;
};
struct __rw_open_cat_page
{
static const size_t _C_size = 8;
__rw_open_cat_page* _C_next; // next page
__rw_open_cat_data _C_data [_C_size];
};
// manages a global, per-process repository of open catalogs according
// to the following table:
// cat _rw_open_cat_data algorithm
// -1 0 invalid
// -1 non-0 append the struct into the repository and
// return a pointer to a struct containing
// the new catalog and locale
// > -1 0 return the a pointer to a struct
// containing the locale associated with
// this catalog
// > -1 non-0 find the struct containg the locale and
// catalog in the repository and invalidate
// it.
// Return a pointer to the struct
static __rw_open_cat_data*
__rw_manage_cat_data (int &cat, __rw_open_cat_data *pcat_data)
{
// a per-process array of pointers to catalog data structs
static __rw_open_cat_data* catalog_buf [__rw_open_cat_page::_C_size];
static __rw_open_cat_data** catalogs = 0;
// first page of a per-process list of pages of catalog data structs
static __rw_open_cat_page catalog_page;
static size_t n_catalogs = 0;
static size_t catalog_bufsize = sizeof catalog_buf / sizeof *catalog_buf;
static size_t largest_cat = 0;
if (!catalogs) {
for (size_t i = 0; i < catalog_bufsize; ++i) {
catalog_page._C_data [i].catd = _RWSTD_BAD_CATD;
catalog_buf [i] = &catalog_page._C_data [i];
}
catalogs = catalog_buf;
}
if (-1 == cat) {
_RWSTD_ASSERT (0 != pcat_data);
// append the locale and return a struct with the associated catalog
if (pcat_data) {
if (n_catalogs == catalog_bufsize) {
// allocate new page of catalog data
__rw_open_cat_page* const page =
new __rw_open_cat_page;
// insert new page into singly-linked page list
page->_C_next = catalog_page._C_next;
catalog_page._C_next = page;
// initialize new page
for (size_t i = 0; i < __rw_open_cat_page::_C_size; ++i) {
page->_C_data [i].catd = _RWSTD_BAD_CATD;
}
// rwallocate buffer of catalog data pointers
__rw_open_cat_data** const data =
new __rw_open_cat_data* [n_catalogs + __rw_open_cat_page::_C_size];
memcpy (data, catalogs, n_catalogs * sizeof *data);
if (catalogs != catalog_buf)
delete[] catalogs;
catalogs = data;
catalog_bufsize += __rw_open_cat_page::_C_size;
for (size_t i = 0; i < __rw_open_cat_page::_C_size; ++i) {
catalogs [n_catalogs + i] = &page->_C_data [i];
}
cat = int (n_catalogs);
catalogs [cat]->catd = pcat_data->catd;
memcpy (catalogs [cat]->loc._C_store (),
pcat_data->loc._C_store (),
sizeof (_STD::locale));
if (size_t (cat) > largest_cat)
largest_cat = size_t (cat);
++n_catalogs;
}
else {
// find the first open slot and use it.
cat = 0;
while (catalogs [cat]->catd != _RWSTD_BAD_CATD) {
++cat;
}
catalogs [cat]->catd = pcat_data->catd;
memcpy (catalogs [cat]->loc._C_store (),
pcat_data->loc._C_store (),
sizeof (_STD::locale));
if (size_t (cat) > largest_cat)
largest_cat = size_t (cat);
++n_catalogs;
}
}
}
else {
if (0 == pcat_data) {
// find struct and return it
if (size_t (cat) < catalog_bufsize)
return catalogs [cat];
return 0;
}
// initialize the struct to an invalid state
--n_catalogs;
catalogs [cat]->catd = _RWSTD_BAD_CATD;
if (size_t (cat) == largest_cat) {
// find the next smallest valid slot
largest_cat = 0;
for (int i = cat; i >= 0; --i) {
if (catalogs [i]->catd != _RWSTD_BAD_CATD) {
largest_cat = size_t (i);
break;
}
}
static const size_t bufsize =
sizeof catalog_buf / sizeof *catalog_buf;
if ((largest_cat < bufsize / 2) && (catalogs != catalog_buf)) {
// when there are no more open catalogs indexed beyond
// second half of the static pointer repository, copy
// the open catalog pointers back into the repository.
catalog_bufsize = bufsize;
memcpy (catalog_buf, catalogs,
catalog_bufsize * sizeof (*catalogs));
delete[] catalogs;
catalogs = catalog_buf;
// remove all pages, they're not in use
while (catalog_page._C_next)
{
// remove next page from page list
__rw_open_cat_page* page = catalog_page._C_next;
catalog_page._C_next = page->_C_next;
// deallocate that page
delete page;
}
}
}
}
return pcat_data;
}
// Open a message catalog and assign and return a handle for it.
int __rw_cat_open (const _STD::string &cat_name, const _STD::locale &loc)
{
_RWSTD_MT_CLASS_GUARD (__rw_open_cat_data);
const nl_catd catd = catopen (cat_name.c_str (), NL_CAT_LOCALE);
if (_RWSTD_BAD_CATD == catd)
return -1;
__rw_open_cat_data cat_data;
cat_data.catd = catd;
new (cat_data.loc._C_store ()) _STD::locale (loc);
int cat = -1;
__rw_manage_cat_data (cat, &cat_data);
_RWSTD_ASSERT (0 <= cat);
return cat;
}
// Get message text from catalog.
const char* __rw_get_message (int cat, int set_num, int msg_num)
{
if (cat < 0)
return 0;
_RWSTD_MT_CLASS_GUARD (__rw_open_cat_data);
__rw_open_cat_data *const pcat_data = __rw_manage_cat_data (cat, 0);
if (pcat_data && _RWSTD_BAD_CATD != pcat_data->catd) {
// 22.2.7.1.2, p3: `catalog' must be valid
const char dflt[] = "";
const nl_catd catd = pcat_data->catd;
const char* const text = catgets (catd, set_num, msg_num, dflt);
if (text != dflt)
return text;
}
return 0;
}
// Get locale to be used for character translation for this message catalog.
const _STD::locale& __rw_get_locale (int cat)
{
_RWSTD_MT_CLASS_GUARD (__rw_open_cat_data);
_RWSTD_ASSERT (0 <= cat);
__rw_open_cat_data* const pcat_data = __rw_manage_cat_data (cat, 0);
_RWSTD_ASSERT (0 != pcat_data);
return *pcat_data->loc._C_data ();
}
// Close a catalog and release its handle.
void __rw_cat_close (int cat)
{
bool cat_closed = false;
{
// guard is local to this scope to avoid reacquiring the mutex
// if have to generate an exception message string below.
_RWSTD_MT_CLASS_GUARD (__rw_open_cat_data);
__rw_open_cat_data* const pcat_data =
cat < 0 ? 0 : __rw_manage_cat_data (cat, 0);
// 22.2.7.1.2, p5: `catalog' must be valid
if (pcat_data && pcat_data->catd != _RWSTD_BAD_CATD) {
catclose (pcat_data->catd);
_STD::locale* const ploc = pcat_data->loc._C_data ();
ploc->~locale ();
__rw_manage_cat_data (cat, pcat_data);
cat_closed = true;
}
else {
cat_closed = false;
}
}
_RWSTD_REQUIRES (cat_closed, (_RWSTD_ERROR_INVALID_ARGUMENT,
_RWSTD_FUNC ("__rw_cat_close (int cat)")));
}
} // namespace __rw