| // 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; |
| } |