blob: d52a6a9453a3f81beee419e9edab66c8fb53adea [file] [log] [blame]
/***************************************************************************
*
* rw_setlocale.cpp - implementation of the __rw_setlocale 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>
#if defined (__linux__) && !defined (_XOPEN_SOURCE)
// need S_IFDIR on Linux
# define _XOPEN_SOURCE
#endif // __linux__ && !_XOPEN_SOURCE
#include <locale.h> // for setlocale()
#include <stdlib.h> // for getenv()
#include <string.h> // for memcpy(), strcmp()
#include <time.h> // for struct tm
#include <rw/_error.h>
#include <rw/_mutex.h>
#include "podarray.h" // for __rw_charray
#include "setlocale.h"
#ifndef _MSC_VER
# include <limits.h> // for PATH_MAX
# if defined (__INTERIX) && !defined (NAME_MAX)
# define NAME_MAX 255
# endif // __INTERIX && !NAME_MAX
#else // if defined (_MSC_VER)
# include <windows.h>
#endif // _MSC_VER
#include <sys/stat.h>
#ifndef PATH_MAX
# ifdef MAX_PATH
# define PATH_MAX MAX_PATH
# else // if defined (MAX_PATH)
# define PATH_MAX 1024
# endif // MAX_PATH
#endif // PATH_MAX
_RWSTD_NAMESPACE (__rw) {
// "setlocale" synchronization (internal, not exported)
__rw_mutex __rw_setlocale_mutex;
// ctor changes the C library locale, saving the old locale to be
// restored by the dtor
__rw_setlocale::__rw_setlocale (const char *locname, int cat, int nothrow)
: _C_name (0), _C_guard (1), _C_cat (cat)
{
// acquire global per-process lock
__rw_setlocale_mutex._C_acquire ();
// retrieve previous locale name and check if it is already set
const char* const curname = ::setlocale (cat, 0);
if (curname) {
// try to optimize away the following potential
// memory allocation if the locale is already set
if (locname && !nothrow && !::strcmp (locname, curname))
return;
// save the name of the current locale before making the call below
// even though it may fail, since a successful call may change the
// string pointed to by curname
const _RWSTD_SIZE_T size = ::strlen (curname) + 1;
_C_name = size <= sizeof _C_namebuf ? _C_namebuf : new char [size];
::memcpy (_C_name, curname, size);
if (::setlocale (cat, locname)) {
// a NULL name is only used when we want to use the object
// as a retriever for the name of the current locale;
// no need for a lock then
if (!locname) {
_C_guard = 0;
__rw_setlocale_mutex._C_release ();
}
return;
}
if (_C_name != _C_namebuf)
delete[] _C_name;
_C_name = 0;
}
// bad locales OR bad locale names OR bad locale categories
_C_guard = 0;
__rw_setlocale_mutex._C_release ();
// failure in setting the locale
_RWSTD_REQUIRES (nothrow, (_RWSTD_ERROR_LOCALE_BAD_NAME,
_RWSTD_FUNC ("std::setlocale(const char*)"),
locname));
}
// dtor restores the C library locale that
// was in effect when the object was constructed
__rw_setlocale::~__rw_setlocale ()
{
// if guard is set, constructor changed the locale
if (_C_guard) {
// restore the locale
::setlocale (_C_cat, _C_name);
// release the lock
__rw_setlocale_mutex._C_release ();
}
if (_C_name != _C_namebuf)
delete [] _C_name;
}
char* __rw_locale_name (int cat, const char *locname, __rw_chararray &namebuf)
{
static char locale_root [PATH_MAX];
_RWSTD_ASSERT (0 != locname);
if (!*locale_root) {
#ifndef _RWSTD_LOCALE_ROOT_ENVVAR
# define _RWSTD_LOCALE_ROOT_ENVVAR "RWSTD_LOCALE_ROOT"
#endif
const char* const envvar = getenv (_RWSTD_LOCALE_ROOT_ENVVAR);
if (envvar && strlen (envvar) < sizeof locale_root)
strcpy (locale_root, envvar);
}
// construct the pathname of the locale database
char locpath [PATH_MAX];
*locpath = '\0';
_RWSTD_SIZE_T namelen = strlen (locname);
_RWSTD_SIZE_T pathlen = 0;
if (*locale_root) {
if (namelen + strlen (locale_root) + 1 < sizeof locpath) {
strcpy (locpath, locale_root);
pathlen = strlen (locpath);
locpath [pathlen++] = _RWSTD_PATH_SEP;
locpath [pathlen] = '\0';
}
}
if (strlen (locpath) + namelen < sizeof locpath) {
strcpy (locpath + pathlen, locname);
// see if the directory containing the locale database exists
struct stat sb;
if (0 == stat (locpath, &sb) && sb.st_mode & S_IFDIR) {
namebuf.assign (locname);
return namebuf.data ();
}
}
// if the named locale is not found in the locale directory
// try to see if the locale refers to a libc locale
const __rw_setlocale tmp (locname, cat, 1 /* nothrow */);
if (!tmp.name ())
return 0;
// get the libc locale name in case locname is an abbreviation
const char* const fullname = setlocale (cat, locname);
_RWSTD_ASSERT (0 != fullname);
namebuf.assign (fullname);
return namebuf.data ();
}
} // namespace __rw