First public contribution.
1 /************************************************************************
3 * localedef.cpp - definitions of locale helpers
5 * $Id: localedef.cpp 290022 2005-09-18 23:59:35Z sebor $
7 ************************************************************************
9 * Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave
10 * Software division. Licensed under the Apache License, Version 2.0 (the
11 * "License"); you may not use this file except in compliance with the
12 * License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0. Unless required by
14 * applicable law or agreed to in writing, software distributed under
15 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
16 * CONDITIONS OF ANY KIND, either express or implied. See the License
17 * for the specific language governing permissions and limitations under
20 **************************************************************************/
22 // expand _TEST_EXPORT macros
23 #define _RWSTD_TEST_SRC
25 #include <localedef.h>
27 #include <environ.h> // for rw_putenv()
28 #include <file.h> // for SHELL_RM_RF, rw_tmpnam
29 #include <system.h> // for rw_system()
33 // on Linux define _XOPEN_SOURCE to get CODESET defined in <langinfo.h>
34 # define _XOPEN_SOURCE 500 /* Single Unix conformance */
35 // bring __int32_t into scope (otherwise <wctype.h> fails to compile)
36 # include <sys/types.h>
40 #include <sys/stat.h> // for stat
42 #if (!defined (_WIN32) && !defined (_WIN64)) || defined (__SYMBIAN32__)
44 # include <sys/wait.h> // for WIFEXITED(), WIFSIGNALED(), WTERMSIG()
47 # include <crtdbg.h> // for _malloc_dbg()
50 #include <ios> // for ios::*
51 #include <limits> // for numeric_limits
52 #include <locale> // for money_base::pattern
54 #include <assert.h> // for assert
55 #include <errno.h> // for EBADF
56 #include <float.h> // for {FLT,DBL,LDBL}_DIG
57 #include <limits.h> // for CHAR_BIT, PATH_MAX
58 #include <locale.h> // for LC_XXX macros, setlocale
59 #include <stdarg.h> // for va_copy, va_list, ...
60 #include <stdio.h> // for fgets, remove, sprintf, ...
61 #include <stdlib.h> // for getenv, free, malloc, realloc
62 #include <string.h> // for strcat, strcpy, strlen, ...
64 #include <wchar.h> // for wcslen, ...
67 # define PATH_MAX 1024
73 # define LC_MESSAGES _RWSTD_LC_MESSAGES
74 # endif // LC_MESSAGES
75 # include <langinfo.h>
76 # define EXE_SUFFIX ""
78 # define EXE_SUFFIX ".exe"
82 #define TOPDIR "TOPDIR" /* the TOPDIR environment variable */
83 #define BINDIR "BINDIR" /* the BINDIR environment variable */
86 #if _RWSTD_PATH_SEP == '/'
88 # define IS_ABSOLUTE_PATHNAME(path) (_RWSTD_PATH_SEP == *(path))
91 # define IS_ABSOLUTE_PATHNAME(path) \
92 ( ( 'A' <= *(path) && 'Z' >= *(path) \
93 || 'a' <= *(path) && 'z' >= *(path)) \
95 && _RWSTD_PATH_SEP == (path)[2])
98 // relative paths to the etc/nls directory and its subdirectories
99 #if defined (_RWSTD_USE_CONFIG)
100 # define RELPATH "etc" SLASH "nls"
101 # define TESTS_ETC_PATH "tests" SLASH "etc"
103 # define RELPATH "etc" SLASH "stdlib" SLASH "nls"
104 # define TESTS_ETC_PATH "tests" SLASH "stdlib" SLASH "etc"
105 #endif // _RWSTD_USE_CONFIG
107 /**************************************************************************/
110 rw_locale (const char *args, const char *fname)
112 // use BINDIR to determine the location of the locale command
113 const char* bindir = getenv ("BINDIR");
115 bindir = ".." SLASH "bin";
120 ret = rw_system ("%s%slocale%s %s",
121 bindir, SLASH, EXE_SUFFIX, args);
123 ret = rw_system ("%s%slocale%s %s >%s",
124 bindir, SLASH, EXE_SUFFIX, args, fname);
129 /**************************************************************************/
131 _TEST_EXPORT const char*
132 rw_localedef (const char *args,
133 const char* src, const char *charmap, const char *locname)
135 assert (src && charmap);
137 // create a fully qualified pathname of the locale database
138 // when (locname == 0), the pathname is computed by appending
139 // the name of the character map file `charmap' to the name
140 // of the locale definition file `src'
141 // otherwise, when `locname' is not a pathname, the pathname
142 // of the locale database is formed by appending `locname'
143 // to the name of the locale root directory
144 static char locale_path [PATH_MAX];
146 const char* locale_root = getenv (LOCALE_ROOT_ENVAR);
150 assert ( strlen (locale_root)
153 + 2 < sizeof locale_path);
155 strcpy (locale_path, locale_root);
158 if (strchr (locname, _RWSTD_PATH_SEP))
159 strcpy (locale_path, locname);
161 strcat (locale_path, SLASH);
162 strcat (locale_path, locname);
166 // compute the locale pathname from `src', `charmap',
168 strcpy (locale_path, locale_root);
169 strcat (locale_path, SLASH);
171 const char *slash = strrchr (src, _RWSTD_PATH_SEP);
172 slash = slash ? slash + 1 : src;
174 strcat (locale_path, src);
175 strcat (locale_path, ".");
177 slash = strrchr (charmap, _RWSTD_PATH_SEP);
178 slash = slash ? slash + 1 : charmap;
180 strcat (locale_path, slash);
183 // check to see if the locale database already exists and
184 // if so, return immediately the locale filename to the caller
185 #if !defined (_MSC_VER)
187 if (!stat (locale_path, &sb)) {
190 if (!_stat (locale_path, &sb)) {
192 return strrchr (locale_path, _RWSTD_PATH_SEP) + 1;
195 // otherwise, try to create the locale database
197 // use TOPDIR to determine the root of the source tree
198 const char* const topdir = getenv (TOPDIR);
199 if (!topdir || !*topdir) {
200 fprintf (stderr, "%s:%d: the environment variable %s is %s\n",
201 __FILE__, __LINE__, TOPDIR, topdir ? "empty" : "undefined");
205 // use BINDIR to determine the location of the localedef command
206 const char* bindir = getenv ("BINDIR");
208 bindir = ".." SLASH "bin";
210 // if `src' is relative pathname (or a filename) construct the fully
211 // qualified absolute pathname to the locale definition file from it
212 char src_path [PATH_MAX];
214 if (!IS_ABSOLUTE_PATHNAME (src)) {
215 strcpy (src_path, topdir);
216 strcat (src_path, SLASH RELPATH SLASH "src" SLASH);
217 strcat (src_path, src);
219 // if the file doesn't exist, see if there is a file
220 // with that name in the locale root directory (e.g.,
222 FILE* const file_exists = fopen (src_path, "r");
224 fclose (file_exists);
226 strcpy (src_path, locale_root);
227 strcat (src_path, SLASH);
228 strcat (src_path, src);
234 char charmap_path [PATH_MAX];
235 if (!IS_ABSOLUTE_PATHNAME (charmap)) {
236 strcpy (charmap_path, topdir);
237 strcat (charmap_path, SLASH RELPATH SLASH "charmaps" SLASH);
238 strcat (charmap_path, charmap);
240 // if the file doesn't exist, see if there is a file
241 // with that name in the locale root directory (e.g.,
243 FILE* const file_exists = fopen (charmap_path, "r");
245 fclose (file_exists);
247 strcpy (charmap_path, locale_root);
248 strcat (charmap_path, SLASH);
249 strcat (charmap_path, charmap);
252 charmap = charmap_path;
258 const int ret = rw_system ("%s%slocaledef%s %s -c -f %s -i %s %s",
259 bindir, SLASH, EXE_SUFFIX, args,
260 charmap, src, locale_path);
262 // return the unqualified locale file name on success or 0 on failure
263 return ret ? (char*)0 : strrchr (locale_path, _RWSTD_PATH_SEP) + 1;
266 /**************************************************************************/
270 static char rw_locale_root [256];
272 static void atexit_rm_locale_root ()
274 // remove temporary locale databases created by the test
275 rw_system (SHELL_RM_RF "%s", rw_locale_root);
280 _TEST_EXPORT const char*
281 rw_set_locale_root ()
283 // set any additional environment variables defined in
284 // the RW_PUTENV environment variable (if it exists)
287 // create a temporary directory for files created by the test
288 const char* const locale_root = rw_tmpnam (rw_locale_root);
292 char envvar [sizeof LOCALE_ROOT_ENVAR + sizeof rw_locale_root] =
293 LOCALE_ROOT_ENVAR "=";
294 #ifndef __SYMBIAN32__
295 std::strcat (envvar, locale_root);
297 strcat (envvar, locale_root);
299 // remove temporary file if mkstemp() rw_tmpnam() called mkstemp()
300 if (rw_system (SHELL_RM_RF " %s", locale_root)) {
302 #if defined (_WIN32) || defined (_WIN64)
303 // ignore errors on WIN32 where the stupid DEL command
304 // fails even with /Q /S when the files don't exist
306 // assume a sane implementation of SHELL_RM_RF
308 #endif // _WIN{32,64}
311 if (rw_system ("mkdir %s", locale_root))
314 // set the "RWSTD_LOCALE_ROOT" environment variable
315 // where std::locale looks for locale database files
318 if (atexit (atexit_rm_locale_root))
319 perror ("atexit(atexit_rm_locale_root) failed");
324 /**************************************************************************/
327 rw_locales (int loc_cat, const char* grep_exp)
329 static char* slocname = 0;
331 static int size = 0; // the number of elements in the array
332 static int total_size = 5120; // the size of the array
333 static int last_cat = loc_cat; // last category
335 // allocate first time through
339 slocname = _RWSTD_STATIC_CAST (char*, malloc (5120));
341 // prevent this leaked allocation from causing failures
342 // in tests that keep track of storage allocated in
344 slocname = _RWSTD_STATIC_CAST (char*,
345 _malloc_dbg (5120, _CLIENT_BLOCK, 0, 0));
350 // return immediately if buffer is already initialized
351 if (*slocname && loc_cat == last_cat)
354 // remmeber the category we were last called with
357 char* locname = slocname;
359 char* save_localename = 0;
362 if (loc_cat != _UNUSED_CAT) {
363 // copy the locale name, the original may be overwitten by libc
364 save_localename = strcpy (namebuf, setlocale (loc_cat, 0));
367 const char* const fname = rw_tmpnam (0);
373 // make sure that grep_exp is <= 80
374 if (grep_exp && 80 < strlen (grep_exp)) {
378 // execute a shell command and redirect its output into the file
379 const int exit_status =
380 grep_exp && *grep_exp
381 ? rw_system ("locale -a | grep \"%s\" > %s", grep_exp, fname)
382 : rw_system ("locale -a > %s", fname);
388 // open file containing the list of installed locales
389 FILE *file = fopen (fname, "r");
395 // even simple locale names can be very long (e.g., on HP-UX,
396 // where a locale name always consists of the names of all
397 // categories, such as "C C C C C C")
398 char last_name [256];
401 // if successful, construct a char array with the locales
402 while (fgets (linebuf, sizeof linebuf, file)) {
404 linebuf [strlen (linebuf) - 1] = '\0';
406 #ifdef _RWSTD_OS_SUNOS
408 // avoid the bad locale named iso_8859_1 on SunOS
409 if (!strcmp ("iso_8859_1", linebuf))
412 #endif // _RWSTD_OS_SUNOS
414 // if our buffer is full then dynamically allocate a new one
415 if ((size += (strlen (linebuf) + 1)) > total_size) {
419 _RWSTD_STATIC_CAST (char*, malloc (total_size));
421 memcpy (tmp, slocname, total_size - 5120);
425 locname = slocname + size - strlen (linebuf) - 1;
430 // prevent a hang (OS/libc bug?)
431 strcpy (locname, linebuf);
432 locname += strlen (linebuf) + 1;
434 #else // if !defined (_WIN64)
435 if (loc_cat != _UNUSED_CAT) {
437 // set the C locale to verify that the name is valid
438 const char *name = setlocale (loc_cat, linebuf);
440 // if it is and if the actual locale name different
441 // from the last one, append it to the list
442 if (name && strcmp (last_name, name)) {
443 strcpy (locname, linebuf);
444 locname += strlen (linebuf) + 1;
446 // save the last locale name
447 assert (strlen (name) < sizeof last_name);
448 strcpy (last_name, name);
452 strcpy (locname, linebuf);
453 locname += strlen (linebuf) + 1;
462 if (loc_cat != _UNUSED_CAT)
463 setlocale (loc_cat, save_localename);
465 // close before removing