blob: 558356a63b5a71ad754970aa207a0b8f6f36de8d [file] [log] [blame]
// determining combined locale name format
/***************************************************************************
*
* 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 1999-2007 Rogue Wave Software, Inc.
*
**************************************************************************/
// If strict_ansi_errors is present, the definition of __PURE_CNAME
// dictates inclusion of locale_cname_impl & locale_ansi_impl which
// do not define LC_MESSAGES locale category
#if defined (__PURE_CNAME)
# undef __PURE_CNAME
#endif // __PURE_CNAME
#include <locale.h>
#include <stdio.h>
#include <string.h>
// prevent IBM xlC 5.0 errors caused by using /usr/include/string.h
// which #defines these (and other) names to undeclared symbols
#undef strcpy
#undef strcmp
#undef strlen
#undef strcat
#include "locale_names.h" // for test_locale_names
// the longest combined locale name handled by the test (GNU glibc
// can generate some awfully long names since in addition to the
// name of the locale name for each category it includes the name
// of the category itself, e.g., "LC_CTYPE=en;LC_NUMERIC=es;...")
#define MAX_LOCALE_NAME_LEN 1024
#if 0 // disabled
// enable for debugging to emulate a system with no locales installed
extern "C" char* setlocale (int, const char *name)
{
static char cname[] = "C";
if (0 == name)
return cname;
if ('C' != name [0] || '\0' != name [1])
return 0;
return cname;
}
#endif // 0/1
// define own version of the libc functions to prevent problems
// caused by them being not declared (sometimes due to the fact
// that they must be overloaded in C++ which may not be done in
// headers that are #included in the test)
char* rw_strchr (const char *s, char c)
{
for (; *s && *s != c; ++s);
return *s == c ? (char*)s : 0;
}
char* rw_strpbrk (const char *s, const char *seps)
{
for (; *seps; ++seps) {
char *where = rw_strchr (s, *seps);
if (where)
return where;
}
return 0;
}
char* rw_strstr (const char *str, const char *s)
{
for (; *str; ++str) {
str = rw_strchr (str, *s);
if (!str)
return 0;
const char *s1 = str;
const char *s2 = s;
for (; *s1 && *s2 && *s1 == *s2; ++s1, ++s2);
if (!*s2)
return (char*)str;
}
return 0;
}
extern "C" int putenv (char*);
extern const char* const test_locale_names[];
extern const unsigned nlocales;
int print_lc_constants ();
int print_categories (const char*, int, int&, int&, int, char&, char&);
int print_locale_name_format (int, int, int, int, int, char, char);
#if !defined (_WIN32) || defined (__CYGWIN__)
char cat_seps[] = " \n\t/\\:;#%";
#else
char cat_seps[] = "\n\t/\\:;#%";
#endif
int main ()
{
// compute and print the values of the LC_XXX constants
// and their relationship to the std::locale::category
// constants
if (print_lc_constants ())
return 0;
// try to determine the format of combined locale names
int setlocale_environ = 0;
int loc_name_use_cat = 0;
int loc_name_prepend_sep = 0;
int loc_name_condense = 0;
char loc_name_cat_sep = '\0';
char loc_name_cat_eq = '\0';
char namebuf [MAX_LOCALE_NAME_LEN];
namebuf [0] = '\0';
// determine whether setlocale(LC_ALL, name) returns
// a string containing the equivalent of `name' for
// each LC_XXX category or whether it collapses all
// equivalent names into just a single locale name
const char *locname = setlocale (LC_ALL, "C");
char *sep = locname ? rw_strpbrk (locname, cat_seps) : 0;
if (sep) {
if ('C' == sep [-1] && 1 == sep - locname)
loc_name_cat_sep = *sep;
else {
loc_name_condense = 1;
*rw_strchr (cat_seps, *sep) = '\n';
}
}
else {
loc_name_condense = 1;
}
// find the first valid locale name other than "C" or
// the default locale returned by locale (LC_ALL, "")
if ((locname = setlocale (LC_ALL, "")))
strcpy (namebuf, locname);
unsigned i;
for (i = 0; i != nlocales; ++i) {
locname = test_locale_names [i];
#if defined (_MSC_VER) && _MSC_VER <= 1200
// work around an MSVC libc bug (PR #27990)
if (rw_strpbrk (locname, ".-"))
continue;
#endif // MSVC <= 6.0
locname = setlocale (LC_ALL, locname);
if (locname && strcmp (namebuf, locname))
break;
}
if (locname) {
locname = strcpy (namebuf, locname);
sep = rw_strpbrk (locname, cat_seps);
if (sep)
*sep = '\0';
// determine if the environment has any effect on setlocale()
{
char def_locale [MAX_LOCALE_NAME_LEN];
def_locale [0] = '\0';
const char *tmpname = setlocale (LC_ALL, "");
if (tmpname)
strcpy (def_locale, tmpname);
char buf [MAX_LOCALE_NAME_LEN];
strcpy (buf, "LC_COLLATE=");
strcat (buf, locname);
putenv (buf);
tmpname = setlocale (LC_ALL, "");
setlocale_environ = tmpname ? strcmp (def_locale, tmpname) : 1;
}
}
print_categories (locname,
setlocale_environ,
loc_name_use_cat,
loc_name_prepend_sep,
loc_name_condense,
loc_name_cat_sep,
loc_name_cat_eq);
return print_locale_name_format (0 == locname,
setlocale_environ,
loc_name_use_cat,
loc_name_prepend_sep,
loc_name_condense,
loc_name_cat_sep,
loc_name_cat_eq);
}
/*********************************************************************/
static struct LC_vars
{
int ord;
int cat;
char name [64];
const char *lower;
} lc_vars [] = {
{ -1, LC_COLLATE, "LC_COLLATE=C", "collate" },
{ -1, LC_CTYPE, "LC_CTYPE=C", "ctype" },
{ -1, LC_MONETARY, "LC_MONETARY=C", "monetary" },
{ -1, LC_NUMERIC, "LC_NUMERIC=C", "numeric" },
{ -1, LC_TIME, "LC_TIME=C", "time" },
#if defined (LC_MESSAGES)
{ -1, LC_MESSAGES, "LC_MESSAGES=C", "messages" },
#endif
#if defined (LC_NAME)
{ -1, LC_NAME, "LC_NAME=C", "name" },
#endif
#if defined (LC_PAPER)
{ -1, LC_PAPER, "LC_PAPER=C", "paper" },
#endif
#if defined (LC_IDENTIFICATION)
{ -1, LC_IDENTIFICATION, "LC_IDENTIFICATION=C", "ident" },
#endif
#if defined (LC_ADDRESS)
{ -1, LC_ADDRESS, "LC_ADDRESS=C", "address" },
#endif
#if defined (LC_TELEPHONE)
{ -1, LC_TELEPHONE, "LC_TELEPHONE=C", "telephone" },
#endif
#if defined (LC_MEASUREMENT)
{ -1, LC_MEASUREMENT, "LC_MEASUREMENT=C", "measurement" },
#endif
{ -1, LC_ALL, "LC_ALL", 0 },
{ -1, 0, "", 0 }
};
// the known order of categories in combined locale names
const int lc_cat_order[] = {
#if defined (_AIX)
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES,
-1, -1, -1, -1, -1, -1
#elif defined (__FreeBSD__) || defined (__NetBSD__)
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES,
-1, -1, -1, -1, -1, -1
#elif defined (__hpux)
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES,
-1, -1, -1, -1, -1, -1
#elif defined (__GLIBC__)
LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES,
# if defined (LC_PAPER)
LC_PAPER, LC_NAME, LC_ADDRESS, LC_TELEPHONE, LC_MEASUREMENT,
LC_IDENTIFICATION
# else
-1, -1, -1, -1, -1, -1
# endif
#elif defined (__osf__)
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES,
-1, -1, -1, -1, -1, -1
#elif defined (__sgi)
LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES,
-1, -1, -1, -1, -1, -1
#elif (defined (__sun__) || defined (__sun) || defined (sun)) \
&& defined (__svr4__)
LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_MESSAGES,
-1, -1, -1, -1, -1, -1
#elif defined (_WIN32)
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, -1,
-1, -1, -1, -1, -1, -1,
#elif defined (__CYGWIN__)
// this is just a wild guess since localization support
// on CygWin seems to be very limited
LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, -1,
-1, -1, -1, -1, -1, -1,
#else
-1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1
#endif
};
/*********************************************************************/
int print_lc_constants ()
{
// determine the values of LC_XXX constants
unsigned lc_max_inx = 0;
unsigned lc_min_inx = 0;
char *eq;
for (unsigned i = 0; *lc_vars [i].name; ++i) {
eq = rw_strchr (lc_vars [i].name, '=');
if (eq)
*eq = '\0';
printf ("#define _RWSTD_%-20s %2d\n",
lc_vars [i].name, lc_vars [i].cat);
if (lc_vars [i].cat > lc_vars [lc_max_inx].cat)
lc_max_inx = i;
if (lc_vars [i].cat < lc_vars [lc_min_inx].cat)
lc_min_inx = i;
if (eq)
*eq = '=';
}
eq = rw_strchr (lc_vars [lc_max_inx].name, '=');
if (eq)
*eq = '\0';
printf ("#define %-27s _RWSTD_%s\n",
"_RWSTD_LC_MAX", lc_vars [lc_max_inx].name);
if (eq)
*eq = '=';
eq = rw_strchr (lc_vars [lc_min_inx].name, '=');
if (eq)
*eq = '\0';
printf ("#define %-27s _RWSTD_%s\n",
"_RWSTD_LC_MIN", lc_vars [lc_min_inx].name);
if (eq)
*eq = '=';
return 0;
}
/*********************************************************************/
int print_categories (const char *locname,
int setlocale_environ,
int &loc_name_use_cat,
int &loc_name_prepend_sep,
int loc_name_condense,
char &loc_name_cat_sep,
char &loc_name_cat_eq)
{
// set or overwrite LC_ALL to prevent it from
// overriding the settings below, and also reset
// the global C locale to "C"
char lc_all[] = "LC_ALL=";
putenv (lc_all);
setlocale (LC_ALL, "C");
unsigned i;
// set up the default environment (i.e., LC_COLLATE=C; LC_CTYPE=C; etc.)
for (i = 0; lc_vars [i].cat != LC_ALL; ++i)
putenv (lc_vars [i].name);
for (i = 0; locname && lc_vars [i].cat != LC_ALL; ++i) {
if (i) {
if (setlocale_environ) {
// replace previous LC_XXX environment variable
strcpy (rw_strchr (lc_vars [i - 1].name, '=') + 1, "C");
putenv (lc_vars [i - 1].name);
}
else {
setlocale (lc_vars [i - 1].cat, "C");
}
}
strcpy (rw_strchr (lc_vars [i].name, '=') + 1, locname);
// add/modify variable to/in the environemnt (may or may not
// be necessary depending on whether putenv() adds the actual
// string or a copy of it
putenv (lc_vars [i].name);
// set the combined locale
char *combined;
if (setlocale_environ) {
// create combined name from the environment
combined = setlocale (LC_ALL, "");
}
else {
// create combined name programmatically
// by setting just one category
setlocale (lc_vars [i].cat, locname);
combined = setlocale (LC_ALL, 0);
}
// construct LC_XXX name
char *eq = rw_strchr (lc_vars [i].name, '=');
*eq = '\0';
// look for LC_XXX string in locale name
const char *where =
combined ? rw_strstr (combined, lc_vars [i].name) : (char*)0;
if (where) {
// found the name of the category constant
// in the name of the combined locale
loc_name_use_cat = 1;
// look for a separator between LC_XXX
// and the locale name (typically '=')
if (!loc_name_cat_eq)
loc_name_cat_eq = where [strlen (lc_vars [i].name)];
int j = -1;
char* sep = rw_strpbrk (combined, cat_seps);
char* first = rw_strstr (combined, locname);
for (const char *s = combined; *s && s != first; ++j)
s = rw_strchr (s, loc_name_cat_eq) + 1;
lc_vars [i].ord = j;
// look for a separator between LC_XXX=name pairs
// (typically ';')
if (where != combined && !loc_name_cat_sep) {
loc_name_cat_sep = where [-1];
}
}
else {
// look for a separator between locale categories
char* sep = combined ? rw_strpbrk (combined, cat_seps) : (char*)0;
if (sep == combined)
loc_name_prepend_sep = 1;
if (sep)
loc_name_cat_sep = *sep;
int j = -loc_name_prepend_sep;
if (sep) {
char *first = rw_strstr (combined, locname);
for (const char *s = combined; *s && s != first; ++j) {
s = rw_strchr (s, *sep);
if (s)
s += 1;
else
break;
}
lc_vars [i].ord = j;
}
else {
// combined locale name is the same as the name
// of one of the combining locales (e.g., Windows)
lc_vars [i].ord = i;
}
}
// replace the original '=' in the LC_XXX=name environment variable
*eq = '=';
if (combined && *combined == loc_name_cat_sep)
loc_name_prepend_sep = 1;
}
for (i = 0; lc_vars [i].cat != LC_ALL; ++i) {
char* eq = rw_strchr (lc_vars [i].name, '=');
if (eq)
*eq = '\0';
const char *comment = 0;
int inx = -1;
for (int j = 0; *lc_vars [j].name; ++j) {
if (lc_vars [i].ord == j) {
comment = 0;
inx = j;
break;
}
if (lc_vars [i].cat == lc_cat_order [j]) {
comment = " // assumed";
inx = j;
}
}
if (-1 < inx)
printf ("#define _RWSTD_CAT_%d(pfx) "
"{ %d, \"%s\", pfx::_C_%s }%s\n",
inx, lc_vars [i].cat, lc_vars [i].name,
lc_vars [i].lower, comment ? comment : "");
}
return 0;
}
/*********************************************************************/
int print_locale_name_format (int guess,
int setlocale_environ,
int loc_name_use_cat,
int loc_name_prepend_sep,
int loc_name_condense,
char loc_name_cat_sep,
char loc_name_cat_eq)
{
const char* os_name = 0;
if (guess) {
#if defined (_AIX)
setlocale_environ = 1;
loc_name_use_cat = 0;
loc_name_prepend_sep = 0;
loc_name_condense = 0;
loc_name_cat_sep = ' ';
loc_name_cat_eq = '\0';
os_name = "AIX";
#elif defined (__FreeBSD__)
setlocale_environ = 1;
loc_name_use_cat = 0;
loc_name_prepend_sep = 0;
loc_name_condense = 1;
loc_name_cat_sep = '/';
loc_name_cat_eq = '\0';
os_name = "FreeBSD";
#elif defined (__hpux)
setlocale_environ = 1;
loc_name_use_cat = 0;
loc_name_prepend_sep = 0;
loc_name_condense = 0;
loc_name_cat_sep = ' ';
loc_name_cat_eq = '\0';
os_name = "HP-UX";
#elif defined (__GLIBC__)
setlocale_environ = 1;
loc_name_use_cat = 1;
loc_name_prepend_sep = 0;
loc_name_condense = 0;
loc_name_cat_sep = ';';
loc_name_cat_eq = '=';
os_name = "GNU libc";
#elif defined (__NetBSD__)
setlocale_environ = 0;
loc_name_use_cat = 0;
loc_name_prepend_sep = 0;
loc_name_condense = 1;
loc_name_cat_sep = '\0';
loc_name_cat_eq = '\0';
os_name = "NetBSD";
#elif defined (__osf__)
setlocale_environ = 1;
loc_name_use_cat = 0;
loc_name_prepend_sep = 0;
loc_name_condense = 0;
loc_name_cat_sep = ' ';
loc_name_cat_eq = '\0';
os_name = "Tru64 UNIX";
#elif defined (__sgi)
setlocale_environ = 1;
loc_name_use_cat = 0;
loc_name_prepend_sep = 1;
loc_name_condense = 1;
loc_name_cat_sep = '/';
loc_name_cat_eq = '\0';
os_name = "SGI IRIX";
#elif (defined (__sun__) || defined (__sun) || defined (sun)) \
&& defined (__svr4__)
setlocale_environ = 1;
loc_name_use_cat = 0;
loc_name_prepend_sep = 1;
loc_name_condense = 1;
loc_name_cat_sep = '/';
loc_name_cat_eq = '\0';
os_name = "SunOS";
#elif defined (_WIN32)
setlocale_environ = 0;
loc_name_use_cat = 1;
loc_name_prepend_sep = 0;
loc_name_condense = 1;
loc_name_cat_sep = ';';
loc_name_cat_eq = '=';
os_name = "Windows";
#elif defined (__CYGWIN__)
// guessing this might be the same as Windows
setlocale_environ = 0;
loc_name_use_cat = 1;
loc_name_prepend_sep = 0;
loc_name_condense = 1;
loc_name_cat_sep = ';';
loc_name_cat_eq = '=';
// change the OS name to CygWin as soon as CygWin
// has implemented locale support
os_name = "Windows";
#else
printf ("// no locales found, unknown system\n");
return -1; // unknown OS, fail
#endif
}
if (os_name)
printf ("// no locales found, using %s format\n", os_name);
if (setlocale_environ)
printf ("// #define _RWSTD_NO_SETLOCALE_ENVIRONMENT\n");
else
printf ("#define _RWSTD_NO_SETLOCALE_ENVIRONMENT\n");
if (loc_name_use_cat)
printf ("// #define _RWSTD_NO_CAT_NAMES\n");
else
printf ("#define _RWSTD_NO_CAT_NAMES\n");
if (loc_name_cat_sep)
printf ("#define _RWSTD_CAT_SEP \"%c\"\n", loc_name_cat_sep);
else
printf ("#define _RWSTD_NO_CAT_SEP\n");
if (loc_name_cat_eq)
printf ("#define _RWSTD_CAT_EQ \"%c\"\n", loc_name_cat_eq);
else
printf ("#define _RWSTD_NO_CAT_EQ\n");
if (loc_name_prepend_sep)
printf ("// #define _RWSTD_NO_INITIAL_CAT_SEP\n");
else
printf ("#define _RWSTD_NO_INITIAL_CAT_SEP\n");
if (loc_name_condense)
printf ("// #define _RWSTD_NO_CONDENSED_NAME\n");
else
printf ("#define _RWSTD_NO_CONDENSED_NAME\n");
return 0;
}