sl@0: /************************************************************************ sl@0: * sl@0: * localedef.cpp - definitions of locale helpers sl@0: * sl@0: * $Id: localedef.cpp 290022 2005-09-18 23:59:35Z sebor $ sl@0: * sl@0: ************************************************************************ sl@0: * sl@0: * Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave sl@0: * Software division. Licensed under the Apache License, Version 2.0 (the sl@0: * "License"); you may not use this file except in compliance with the sl@0: * License. You may obtain a copy of the License at sl@0: * http://www.apache.org/licenses/LICENSE-2.0. Unless required by sl@0: * applicable law or agreed to in writing, software distributed under sl@0: * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR sl@0: * CONDITIONS OF ANY KIND, either express or implied. See the License sl@0: * for the specific language governing permissions and limitations under sl@0: * the License. sl@0: * sl@0: **************************************************************************/ sl@0: sl@0: // expand _TEST_EXPORT macros sl@0: #define _RWSTD_TEST_SRC sl@0: sl@0: #include sl@0: sl@0: #include // for rw_putenv() sl@0: #include // for SHELL_RM_RF, rw_tmpnam sl@0: #include // for rw_system() sl@0: sl@0: sl@0: #if defined __linux__ sl@0: // on Linux define _XOPEN_SOURCE to get CODESET defined in sl@0: # define _XOPEN_SOURCE 500 /* Single Unix conformance */ sl@0: // bring __int32_t into scope (otherwise fails to compile) sl@0: # include sl@0: #endif // __linux__ sl@0: sl@0: #include sl@0: #include // for stat sl@0: sl@0: #if (!defined (_WIN32) && !defined (_WIN64)) || defined (__SYMBIAN32__) sl@0: # include sl@0: # include // for WIFEXITED(), WIFSIGNALED(), WTERMSIG() sl@0: #else sl@0: # include sl@0: # include // for _malloc_dbg() sl@0: #endif sl@0: sl@0: #include // for ios::* sl@0: #include // for numeric_limits sl@0: #include // for money_base::pattern sl@0: sl@0: #include // for assert sl@0: #include // for EBADF sl@0: #include // for {FLT,DBL,LDBL}_DIG sl@0: #include // for CHAR_BIT, PATH_MAX sl@0: #include // for LC_XXX macros, setlocale sl@0: #include // for va_copy, va_list, ... sl@0: #include // for fgets, remove, sprintf, ... sl@0: #include // for getenv, free, malloc, realloc sl@0: #include // for strcat, strcpy, strlen, ... sl@0: #include sl@0: #include // for wcslen, ... sl@0: sl@0: #ifndef PATH_MAX sl@0: # define PATH_MAX 1024 sl@0: #endif sl@0: sl@0: #ifndef _MSC_VER sl@0: # include sl@0: # ifndef LC_MESSAGES sl@0: # define LC_MESSAGES _RWSTD_LC_MESSAGES sl@0: # endif // LC_MESSAGES sl@0: # include sl@0: # define EXE_SUFFIX "" sl@0: #else // if MSVC sl@0: # define EXE_SUFFIX ".exe" sl@0: #endif // _MSC_VER sl@0: sl@0: sl@0: #define TOPDIR "TOPDIR" /* the TOPDIR environment variable */ sl@0: #define BINDIR "BINDIR" /* the BINDIR environment variable */ sl@0: sl@0: sl@0: #if _RWSTD_PATH_SEP == '/' sl@0: # define SLASH "/" sl@0: # define IS_ABSOLUTE_PATHNAME(path) (_RWSTD_PATH_SEP == *(path)) sl@0: #else sl@0: # define SLASH "\\" sl@0: # define IS_ABSOLUTE_PATHNAME(path) \ sl@0: ( ( 'A' <= *(path) && 'Z' >= *(path) \ sl@0: || 'a' <= *(path) && 'z' >= *(path)) \ sl@0: && ':' == (path)[1] \ sl@0: && _RWSTD_PATH_SEP == (path)[2]) sl@0: #endif sl@0: sl@0: // relative paths to the etc/nls directory and its subdirectories sl@0: #if defined (_RWSTD_USE_CONFIG) sl@0: # define RELPATH "etc" SLASH "nls" sl@0: # define TESTS_ETC_PATH "tests" SLASH "etc" sl@0: #else sl@0: # define RELPATH "etc" SLASH "stdlib" SLASH "nls" sl@0: # define TESTS_ETC_PATH "tests" SLASH "stdlib" SLASH "etc" sl@0: #endif // _RWSTD_USE_CONFIG sl@0: sl@0: /**************************************************************************/ sl@0: sl@0: _TEST_EXPORT int sl@0: rw_locale (const char *args, const char *fname) sl@0: { sl@0: // use BINDIR to determine the location of the locale command sl@0: const char* bindir = getenv ("BINDIR"); sl@0: if (!bindir) sl@0: bindir = ".." SLASH "bin"; sl@0: sl@0: int ret; sl@0: sl@0: if (fname) sl@0: ret = rw_system ("%s%slocale%s %s", sl@0: bindir, SLASH, EXE_SUFFIX, args); sl@0: else sl@0: ret = rw_system ("%s%slocale%s %s >%s", sl@0: bindir, SLASH, EXE_SUFFIX, args, fname); sl@0: sl@0: return ret; sl@0: } sl@0: sl@0: /**************************************************************************/ sl@0: sl@0: _TEST_EXPORT const char* sl@0: rw_localedef (const char *args, sl@0: const char* src, const char *charmap, const char *locname) sl@0: { sl@0: assert (src && charmap); sl@0: sl@0: // create a fully qualified pathname of the locale database sl@0: // when (locname == 0), the pathname is computed by appending sl@0: // the name of the character map file `charmap' to the name sl@0: // of the locale definition file `src' sl@0: // otherwise, when `locname' is not a pathname, the pathname sl@0: // of the locale database is formed by appending `locname' sl@0: // to the name of the locale root directory sl@0: static char locale_path [PATH_MAX]; sl@0: sl@0: const char* locale_root = getenv (LOCALE_ROOT_ENVAR); sl@0: if (!locale_root) sl@0: locale_root = "."; sl@0: sl@0: assert ( strlen (locale_root) sl@0: + strlen (src) sl@0: + strlen (charmap) sl@0: + 2 < sizeof locale_path); sl@0: sl@0: strcpy (locale_path, locale_root); sl@0: sl@0: if (locname) { sl@0: if (strchr (locname, _RWSTD_PATH_SEP)) sl@0: strcpy (locale_path, locname); sl@0: else { sl@0: strcat (locale_path, SLASH); sl@0: strcat (locale_path, locname); sl@0: } sl@0: } sl@0: else { sl@0: // compute the locale pathname from `src', `charmap', sl@0: // and `locale_root' sl@0: strcpy (locale_path, locale_root); sl@0: strcat (locale_path, SLASH); sl@0: sl@0: const char *slash = strrchr (src, _RWSTD_PATH_SEP); sl@0: slash = slash ? slash + 1 : src; sl@0: sl@0: strcat (locale_path, src); sl@0: strcat (locale_path, "."); sl@0: sl@0: slash = strrchr (charmap, _RWSTD_PATH_SEP); sl@0: slash = slash ? slash + 1 : charmap; sl@0: sl@0: strcat (locale_path, slash); sl@0: } sl@0: sl@0: // check to see if the locale database already exists and sl@0: // if so, return immediately the locale filename to the caller sl@0: #if !defined (_MSC_VER) sl@0: struct stat sb; sl@0: if (!stat (locale_path, &sb)) { sl@0: #else sl@0: struct _stat sb; sl@0: if (!_stat (locale_path, &sb)) { sl@0: #endif sl@0: return strrchr (locale_path, _RWSTD_PATH_SEP) + 1; sl@0: } sl@0: sl@0: // otherwise, try to create the locale database sl@0: sl@0: // use TOPDIR to determine the root of the source tree sl@0: const char* const topdir = getenv (TOPDIR); sl@0: if (!topdir || !*topdir) { sl@0: fprintf (stderr, "%s:%d: the environment variable %s is %s\n", sl@0: __FILE__, __LINE__, TOPDIR, topdir ? "empty" : "undefined"); sl@0: return 0; sl@0: } sl@0: sl@0: // use BINDIR to determine the location of the localedef command sl@0: const char* bindir = getenv ("BINDIR"); sl@0: if (!bindir) sl@0: bindir = ".." SLASH "bin"; sl@0: sl@0: // if `src' is relative pathname (or a filename) construct the fully sl@0: // qualified absolute pathname to the locale definition file from it sl@0: char src_path [PATH_MAX]; sl@0: sl@0: if (!IS_ABSOLUTE_PATHNAME (src)) { sl@0: strcpy (src_path, topdir); sl@0: strcat (src_path, SLASH RELPATH SLASH "src" SLASH); sl@0: strcat (src_path, src); sl@0: sl@0: // if the file doesn't exist, see if there is a file sl@0: // with that name in the locale root directory (e.g., sl@0: // a temporary file) sl@0: FILE* const file_exists = fopen (src_path, "r"); sl@0: if (file_exists) sl@0: fclose (file_exists); sl@0: else { sl@0: strcpy (src_path, locale_root); sl@0: strcat (src_path, SLASH); sl@0: strcat (src_path, src); sl@0: } sl@0: sl@0: src = src_path; sl@0: } sl@0: sl@0: char charmap_path [PATH_MAX]; sl@0: if (!IS_ABSOLUTE_PATHNAME (charmap)) { sl@0: strcpy (charmap_path, topdir); sl@0: strcat (charmap_path, SLASH RELPATH SLASH "charmaps" SLASH); sl@0: strcat (charmap_path, charmap); sl@0: sl@0: // if the file doesn't exist, see if there is a file sl@0: // with that name in the locale root directory (e.g., sl@0: // a temporary file) sl@0: FILE* const file_exists = fopen (charmap_path, "r"); sl@0: if (file_exists) sl@0: fclose (file_exists); sl@0: else { sl@0: strcpy (charmap_path, locale_root); sl@0: strcat (charmap_path, SLASH); sl@0: strcat (charmap_path, charmap); sl@0: } sl@0: sl@0: charmap = charmap_path; sl@0: } sl@0: sl@0: if (!args) sl@0: args = ""; sl@0: sl@0: const int ret = rw_system ("%s%slocaledef%s %s -c -f %s -i %s %s", sl@0: bindir, SLASH, EXE_SUFFIX, args, sl@0: charmap, src, locale_path); sl@0: sl@0: // return the unqualified locale file name on success or 0 on failure sl@0: return ret ? (char*)0 : strrchr (locale_path, _RWSTD_PATH_SEP) + 1; sl@0: } sl@0: sl@0: /**************************************************************************/ sl@0: sl@0: extern "C" { sl@0: sl@0: static char rw_locale_root [256]; sl@0: sl@0: static void atexit_rm_locale_root () sl@0: { sl@0: // remove temporary locale databases created by the test sl@0: rw_system (SHELL_RM_RF "%s", rw_locale_root); sl@0: } sl@0: sl@0: } sl@0: sl@0: _TEST_EXPORT const char* sl@0: rw_set_locale_root () sl@0: { sl@0: // set any additional environment variables defined in sl@0: // the RW_PUTENV environment variable (if it exists) sl@0: rw_putenv (0); sl@0: sl@0: // create a temporary directory for files created by the test sl@0: const char* const locale_root = rw_tmpnam (rw_locale_root); sl@0: if (!locale_root) sl@0: return 0; sl@0: sl@0: char envvar [sizeof LOCALE_ROOT_ENVAR + sizeof rw_locale_root] = sl@0: LOCALE_ROOT_ENVAR "="; sl@0: #ifndef __SYMBIAN32__ sl@0: std::strcat (envvar, locale_root); sl@0: #else sl@0: strcat (envvar, locale_root); sl@0: #endif sl@0: // remove temporary file if mkstemp() rw_tmpnam() called mkstemp() sl@0: if (rw_system (SHELL_RM_RF " %s", locale_root)) { sl@0: sl@0: #if defined (_WIN32) || defined (_WIN64) sl@0: // ignore errors on WIN32 where the stupid DEL command sl@0: // fails even with /Q /S when the files don't exist sl@0: #else sl@0: // assume a sane implementation of SHELL_RM_RF sl@0: return 0; sl@0: #endif // _WIN{32,64} sl@0: } sl@0: sl@0: if (rw_system ("mkdir %s", locale_root)) sl@0: return 0; sl@0: sl@0: // set the "RWSTD_LOCALE_ROOT" environment variable sl@0: // where std::locale looks for locale database files sl@0: rw_putenv (envvar); sl@0: sl@0: if (atexit (atexit_rm_locale_root)) sl@0: perror ("atexit(atexit_rm_locale_root) failed"); sl@0: sl@0: return locale_root; sl@0: } sl@0: sl@0: /**************************************************************************/ sl@0: sl@0: _TEST_EXPORT char* sl@0: rw_locales (int loc_cat, const char* grep_exp) sl@0: { sl@0: static char* slocname = 0; sl@0: sl@0: static int size = 0; // the number of elements in the array sl@0: static int total_size = 5120; // the size of the array sl@0: static int last_cat = loc_cat; // last category sl@0: sl@0: // allocate first time through sl@0: if (!slocname) { sl@0: sl@0: #ifndef _MSC_VER sl@0: slocname = _RWSTD_STATIC_CAST (char*, malloc (5120)); sl@0: #else sl@0: // prevent this leaked allocation from causing failures sl@0: // in tests that keep track of storage allocated in sl@0: // _NORMAL_BLOCKS sl@0: slocname = _RWSTD_STATIC_CAST (char*, sl@0: _malloc_dbg (5120, _CLIENT_BLOCK, 0, 0)); sl@0: #endif sl@0: *slocname = '\0'; sl@0: } sl@0: sl@0: // return immediately if buffer is already initialized sl@0: if (*slocname && loc_cat == last_cat) sl@0: return slocname; sl@0: sl@0: // remmeber the category we were last called with sl@0: last_cat = loc_cat; sl@0: sl@0: char* locname = slocname; sl@0: sl@0: char* save_localename = 0; sl@0: char namebuf [256]; sl@0: sl@0: if (loc_cat != _UNUSED_CAT) { sl@0: // copy the locale name, the original may be overwitten by libc sl@0: save_localename = strcpy (namebuf, setlocale (loc_cat, 0)); sl@0: } sl@0: sl@0: const char* const fname = rw_tmpnam (0); sl@0: sl@0: if (!fname) { sl@0: return 0; // error sl@0: } sl@0: sl@0: // make sure that grep_exp is <= 80 sl@0: if (grep_exp && 80 < strlen (grep_exp)) { sl@0: abort (); sl@0: } sl@0: sl@0: // execute a shell command and redirect its output into the file sl@0: const int exit_status = sl@0: grep_exp && *grep_exp sl@0: ? rw_system ("locale -a | grep \"%s\" > %s", grep_exp, fname) sl@0: : rw_system ("locale -a > %s", fname); sl@0: sl@0: if (exit_status) { sl@0: return 0; // error sl@0: } sl@0: sl@0: // open file containing the list of installed locales sl@0: FILE *file = fopen (fname, "r"); sl@0: sl@0: if (file) { sl@0: sl@0: char linebuf [256]; sl@0: sl@0: // even simple locale names can be very long (e.g., on HP-UX, sl@0: // where a locale name always consists of the names of all sl@0: // categories, such as "C C C C C C") sl@0: char last_name [256]; sl@0: *last_name = '\0'; sl@0: sl@0: // if successful, construct a char array with the locales sl@0: while (fgets (linebuf, sizeof linebuf, file)) { sl@0: sl@0: linebuf [strlen (linebuf) - 1] = '\0'; sl@0: sl@0: #ifdef _RWSTD_OS_SUNOS sl@0: sl@0: // avoid the bad locale named iso_8859_1 on SunOS sl@0: if (!strcmp ("iso_8859_1", linebuf)) sl@0: continue; sl@0: sl@0: #endif // _RWSTD_OS_SUNOS sl@0: sl@0: // if our buffer is full then dynamically allocate a new one sl@0: if ((size += (strlen (linebuf) + 1)) > total_size) { sl@0: total_size += 5120; sl@0: sl@0: char* tmp = sl@0: _RWSTD_STATIC_CAST (char*, malloc (total_size)); sl@0: sl@0: memcpy (tmp, slocname, total_size - 5120); sl@0: free (slocname); sl@0: sl@0: slocname = tmp; sl@0: locname = slocname + size - strlen (linebuf) - 1; sl@0: } sl@0: sl@0: #ifdef _WIN64 sl@0: sl@0: // prevent a hang (OS/libc bug?) sl@0: strcpy (locname, linebuf); sl@0: locname += strlen (linebuf) + 1; sl@0: sl@0: #else // if !defined (_WIN64) sl@0: if (loc_cat != _UNUSED_CAT) { sl@0: sl@0: // set the C locale to verify that the name is valid sl@0: const char *name = setlocale (loc_cat, linebuf); sl@0: sl@0: // if it is and if the actual locale name different sl@0: // from the last one, append it to the list sl@0: if (name && strcmp (last_name, name)) { sl@0: strcpy (locname, linebuf); sl@0: locname += strlen (linebuf) + 1; sl@0: sl@0: // save the last locale name sl@0: assert (strlen (name) < sizeof last_name); sl@0: strcpy (last_name, name); sl@0: } sl@0: } sl@0: else { sl@0: strcpy (locname, linebuf); sl@0: locname += strlen (linebuf) + 1; sl@0: } sl@0: sl@0: #endif // _WIN64 sl@0: sl@0: } sl@0: *locname = '\0'; sl@0: } sl@0: sl@0: if (loc_cat != _UNUSED_CAT) sl@0: setlocale (loc_cat, save_localename); sl@0: sl@0: // close before removing sl@0: fclose (file); sl@0: sl@0: remove (fname); sl@0: sl@0: return slocname; sl@0: }