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