| /************************************************************************ |
| * |
| * 22.locale.numpunct.cpp |
| * |
| * test exercising the std::numpunct facet |
| * |
| * $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. |
| * |
| **************************************************************************/ |
| |
| #include <rw/_defs.h> |
| |
| #if defined (__IBMCPP__) && !defined (_RWSTD_NO_IMPLICIT_INCLUSION) |
| // Disable implicit inclusion to work around |
| // a limitation in IBM's VisualAge 5.0.2.0 (see PR#26959) |
| |
| # define _RWSTD_NO_IMPLICIT_INCLUSION |
| #endif |
| |
| |
| #include <clocale> // for struct lconv, setlocale() |
| #include <cstdio> // for sprintf() |
| #include <cstdlib> // for getenv() |
| #include <cstring> // for strcmp() |
| #include <locale> |
| |
| #include <driver.h> |
| #include <environ.h> |
| #include <rw_locale.h> |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void run_test (charT*); |
| |
| template <class charT> |
| void check_decimal_point (charT, const char*, const std::locale*); |
| |
| template <class charT> |
| void check_thousands_sep (charT, const char*, const std::locale*); |
| |
| template <class charT> |
| void check_grouping (charT, const char*, const std::string&, const std::locale*); |
| |
| template <class charT> |
| void check_falsename (const std::basic_string<charT>&, |
| const char*, const std::locale*); |
| |
| template <class charT> |
| void check_truename (const std::basic_string<charT>&, |
| const char*, const std::locale*); |
| |
| template <class charT> |
| bool check_numpunct (charT, const char*, const char*, const std::locale*); |
| |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void run_test (charT, const char *tname) |
| { |
| rw_info (0, 0, __LINE__, "classic \"C\" locale"); |
| |
| // create a copy of the classic ("C") locale |
| std::locale *loc = new std::locale (std::locale::classic ()); |
| |
| // verify (at compile time) that _byname facets can be used |
| // to specialize use_facet() and has_facet() |
| typedef std::numpunct_byname<charT> NumpunctByname; |
| |
| if (_STD_HAS_FACET (NumpunctByname, *loc)) |
| _STD_USE_FACET (NumpunctByname, *loc); |
| |
| // verify values as per 7.4 of C89, and 7.11, p2 of C99 |
| check_decimal_point (charT ('.'), tname, loc); |
| |
| // verify 22.2.3.1.2, p2 |
| check_thousands_sep (charT (','), tname, loc); |
| check_grouping (charT (), tname, "", loc); |
| |
| const charT fn[] = { 'f', 'a', 'l', 's', 'e', '\0' }; |
| const charT tn[] = { 't', 'r', 'u', 'e', '\0' }; |
| |
| check_falsename (std::basic_string<charT>(fn), tname, loc); |
| check_truename (std::basic_string<charT>(tn), tname, loc); |
| |
| delete loc; |
| loc = 0; |
| |
| ////////////////////////////////////////////////////////////////// |
| // verify that the facet can be safely used on its own, without |
| // first having to be installed in a locale and retrieved using |
| // use_facet |
| |
| check_decimal_point (charT ('.'), tname, loc); |
| check_thousands_sep (charT (','), tname, loc); |
| check_grouping (charT (), tname, "", loc); |
| |
| check_falsename (std::basic_string<charT>(fn), tname, loc); |
| check_truename (std::basic_string<charT>(tn), tname, loc); |
| |
| ////////////////////////////////////////////////////////////////// |
| // exercise the native locale (affected by the environment |
| // variables LANG, LC_ALL, LC_NUMERIC, etc.) |
| check_numpunct (charT (), tname, "", loc); // native locale |
| |
| // the name of the first non-C (and non-POSIX) locale |
| const char *first_non_c = 0; |
| |
| // exercise named locales (including "C" and "POSIX") |
| for (const char* s = rw_locales (); *s; s += std::strlen (s) + 1) { |
| if (check_numpunct (charT (), tname, s, loc)) |
| if ( !first_non_c |
| && std::strcmp ("C", s) |
| && std::strcmp ("POSIX", s)) |
| first_non_c = s; |
| } |
| |
| // verify that numpunct behaves correctly when LC_ALL is set |
| // to the name of the (non-C, non-POSIX) locale |
| char envvar [80]; |
| std::sprintf (envvar, "LC_ALL=%s", first_non_c); |
| rw_putenv (envvar); |
| check_numpunct (charT (), tname, "", loc); |
| } |
| |
| /**************************************************************************/ |
| |
| char widen (char, const char *s) |
| { |
| return *s; |
| } |
| |
| #ifndef _RWSTD_NO_WCHAR_T |
| |
| wchar_t widen (wchar_t, const char *s) |
| { |
| wchar_t wc = 0; |
| |
| # ifndef _RWSTD_NO_MBTOWC |
| |
| const int n = s && *s ? std::mbtowc (&wc, s, std::strlen (s)) : 0; |
| |
| # else // if defined (_RWSTD_NO_MBTOWC) |
| |
| const int n = 1; |
| |
| wc = s ? wchar_t (_RWSTD_STATIC_CAST (unsigned char, *s)) : 0; |
| |
| # endif // _RWSTD_NO_MBTOWC |
| |
| return n > 0 ? wc : n ? -1 : 0; |
| } |
| |
| #endif // _RWSTD_NO_WCHAR_T |
| |
| |
| template <class charT> |
| bool check_numpunct (charT, |
| const char *tname, |
| const char *locname, |
| const std::locale *loc) |
| { |
| // (try to) set the global C locale |
| const char *tmploc = std::setlocale (LC_ALL, locname); |
| if (!tmploc) |
| return false; |
| |
| // future setlocale() calls may change tmploc's value, so make a copy |
| // (fixed-size char buf is clunky but avoids memory-leak potential) |
| char locnamebuf [1024]; |
| RW_ASSERT ((std::strlen (tmploc) + 1) < sizeof locnamebuf); |
| std::strcpy (locnamebuf, tmploc); |
| |
| _TRY { |
| |
| rw_info (0, 0, __LINE__, |
| "locale (%s) -> \"%s\"; LANG=%s, " |
| "LC_ALL=%s, LC_NUMERIC=%s", |
| locname, loc, |
| std::getenv ("LANG"), |
| std::getenv ("LC_ALL"), |
| std::getenv ("LC_NUMERIC")); |
| |
| // get a pointer to lconv |
| const std::lconv *plconv = std::localeconv (); |
| |
| if (!plconv) |
| return false; |
| |
| // copy important data; the contents of *plconv may |
| // be overwritten by the call sto setlocale() below |
| // note that decimal_point at al may be multbyte character |
| // strings that need to be widened according to the rules |
| // of the same locale |
| const charT decimal_point = widen (charT (), plconv->decimal_point); |
| const charT thousands_sep = widen (charT (), plconv->thousands_sep); |
| |
| const std::string grouping = |
| plconv->grouping ? plconv->grouping : ""; |
| |
| // reset to default locale given by LC_LANG |
| std::setlocale (LC_ALL, ""); |
| |
| loc = new std::locale (locname); |
| |
| // check that newly constructed locale matches |
| |
| if ( 'C' == locnamebuf [0] && '\0' == locnamebuf [1] |
| || 'C' == locname [0] && '\0' == locname [1] |
| || *loc == std::locale::classic ()) { |
| // follow requirements in 22.2.3.1.2. p1, 2, and 3 |
| check_decimal_point (charT ('.'), tname, loc); |
| check_thousands_sep (charT (','), tname, loc); |
| |
| check_grouping (charT (), tname, "", loc); |
| } |
| else { |
| // cast to prevent sign extension between (signed) char and wchar_t |
| // tmp guards against possibly passing along a "(nil)" pointer |
| check_decimal_point (decimal_point, tname, loc); |
| check_thousands_sep (thousands_sep, tname, loc); |
| check_grouping (charT (), tname, grouping.c_str (), loc); |
| } |
| |
| // FIXME: exercise falsename() and truename() in named locales |
| |
| delete loc; |
| loc = 0; |
| } |
| _CATCH (...) { |
| return false; |
| } |
| return true; |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void check_decimal_point (charT expect, |
| const char *tname, |
| const std::locale *loc) |
| { |
| typedef std::numpunct<charT> Punct; |
| typedef std::char_traits<charT> Traits; |
| |
| const charT c = loc ? |
| _STD_USE_FACET (Punct, *loc).decimal_point () |
| : Punct ().decimal_point (); |
| |
| |
| rw_assert (Traits::eq (c, expect), 0, __LINE__, |
| "numpunct<%s>::decimal_point() == %{#lc}, got %{#lc}", |
| tname, expect, c); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void check_thousands_sep (charT expect, |
| const char *tname, |
| const std::locale *loc) |
| { |
| typedef std::numpunct<charT> Punct; |
| typedef std::char_traits<charT> Traits; |
| |
| const charT c = loc ? |
| _STD_USE_FACET (Punct, *loc).thousands_sep () |
| : Punct ().thousands_sep (); |
| |
| rw_assert (Traits::eq (c, expect), 0, __LINE__, |
| "numpunct<%s>::thousands_sep() == %{#lc}, got %{#lc}", |
| tname, expect, c); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void check_grouping (charT, |
| const char *tname, |
| const std::string &expect, |
| const std::locale *loc) |
| { |
| typedef std::numpunct<charT> Punct; |
| |
| const std::string s = loc ? |
| _STD_USE_FACET (Punct, *loc).grouping () |
| : Punct ().grouping (); |
| |
| if ( s != expect |
| && s.size () != expect.size () && s.size () && expect.size ()) { |
| |
| // if the grouping is not exactly the same as the expected result, |
| // verify that the actual grouping is equivalent to the expected |
| // one, e.g., that "\003\003" is equivalent to "\003" |
| |
| const std::string *lng = s.size () > expect.size () ? &expect : &s; |
| const std::string *shrt = s.size () < expect.size () ? &expect : &s; |
| |
| std::size_t i = shrt->size () - 1; |
| |
| for ( ; i != lng->size (); ++i) |
| if ((*shrt) [shrt->size () - 1] != (*lng)[i]) |
| break; |
| |
| rw_assert (i == lng->size (), 0, __LINE__, |
| "numpunct<%s>::grouping() equivalent to %{#S}, got %{#S}", |
| tname, &expect, &s); |
| } |
| else |
| rw_assert (s == expect, 0, __LINE__, |
| "numpunct<%s>::grouping() == %{#S}, got %{#S}", |
| tname, &expect, &s); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void check_falsename (const std::basic_string<charT> &expect, |
| const char *tname, |
| const std::locale *loc) |
| { |
| typedef std::numpunct<charT> Punct; |
| |
| const std::basic_string<charT> s = loc ? |
| _STD_USE_FACET (Punct, *loc).falsename () |
| : Punct ().falsename (); |
| |
| const int char_size = int (sizeof (charT)); |
| |
| rw_assert (s == expect, 0, __LINE__, |
| "numpunct<%s>::falsename() == %{#*S}, got %{#*S}", |
| tname, char_size, &expect, char_size, &s); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void check_truename (const std::basic_string<charT> &expect, |
| const char *tname, |
| const std::locale *loc) |
| { |
| typedef std::numpunct<charT> Punct; |
| |
| const std::basic_string<charT> s = loc ? |
| _STD_USE_FACET (Punct, *loc).truename () |
| : Punct ().truename (); |
| |
| const int char_size = int (sizeof (charT)); |
| |
| rw_assert (s == expect, 0, __LINE__, |
| "numpunct<%s>::truename() == %{#*S}, got %{#*S}", |
| tname, char_size, &expect, char_size, &s); |
| } |
| |
| /**************************************************************************/ |
| |
| static int |
| run_test (int, char**) |
| { |
| run_test (char (), "char"); |
| |
| #ifndef _RWSTD_WCHAR_T |
| |
| run_test (wchar_t (), "wchar_t"); |
| |
| #endif // _RWSTD_WCHAR_T |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ |
| |
| int main (int argc, char *argv[]) |
| { |
| return rw_test (argc, argv, __FILE__, |
| "lib.locale.numpunct", |
| "", run_test, |
| "", |
| (void*)0); |
| } |