| /*************************************************************************** |
| * |
| * 22.locale.moneypunct.cpp - tests for the moneypunct 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. |
| * |
| * Copyright 1998-2006 Rogue Wave Software. |
| * |
| **************************************************************************/ |
| |
| #include <locale> |
| |
| #include <cassert> |
| #include <clocale> // for LC_ALL, setlocale() |
| #include <cstdio> // for fprintf(), stderr |
| #include <cstdlib> // for getenv() |
| #include <cstring> // for memcmp(), strerror() |
| |
| #include <driver.h> // for rw_test() |
| #include <environ.h> // for rw_putenv() |
| #include <rw_locale.h> // for rw_locales() |
| #include <rw_printf.h> // for rw_fprintf() |
| |
| /**************************************************************************/ |
| |
| typedef unsigned char UChar; |
| |
| /**************************************************************************/ |
| |
| // does a deep copy of struct lconv |
| std::lconv* lconvdup (const std::lconv *plconv) |
| { |
| if (!plconv) |
| return 0; |
| |
| const std::size_t |
| decimal_point_sz = std::strlen (plconv->decimal_point) + 1, |
| thousands_sep_sz = std::strlen (plconv->thousands_sep) + 1, |
| grouping_sz = std::strlen (plconv->grouping) + 1, |
| int_curr_symbol_sz = std::strlen (plconv->int_curr_symbol) + 1, |
| currency_symbol_sz = std::strlen (plconv->currency_symbol) + 1, |
| mon_decimal_point_sz = std::strlen (plconv->mon_decimal_point) + 1, |
| mon_thousands_sep_sz = std::strlen (plconv->mon_thousands_sep) + 1, |
| mon_grouping_sz = std::strlen (plconv->mon_grouping) + 1, |
| positive_sign_sz = std::strlen (plconv->positive_sign) + 1, |
| negative_sign_sz = std::strlen (plconv->negative_sign) + 1; |
| |
| const std::size_t total_sz = |
| decimal_point_sz |
| + thousands_sep_sz |
| + grouping_sz |
| + int_curr_symbol_sz |
| + currency_symbol_sz |
| + mon_decimal_point_sz |
| + mon_thousands_sep_sz |
| + mon_grouping_sz |
| + positive_sign_sz |
| + negative_sign_sz; |
| |
| char *pbuf = new char [sizeof (std::lconv) + total_sz]; |
| |
| std::lconv *plconv2 = _RWSTD_REINTERPRET_CAST (std::lconv*, pbuf); |
| |
| plconv2->decimal_point = (pbuf += sizeof (std::lconv)); |
| std::memcpy (pbuf, plconv->decimal_point, decimal_point_sz); |
| |
| plconv2->thousands_sep = (pbuf += decimal_point_sz); |
| std::memcpy (pbuf, plconv->thousands_sep, thousands_sep_sz); |
| |
| plconv2->grouping = (pbuf += thousands_sep_sz); |
| std::memcpy (pbuf, plconv->grouping, grouping_sz); |
| |
| plconv2->int_curr_symbol = (pbuf += grouping_sz); |
| std::memcpy (pbuf, plconv->int_curr_symbol, int_curr_symbol_sz); |
| |
| plconv2->currency_symbol = (pbuf += int_curr_symbol_sz); |
| std::memcpy (pbuf, plconv->currency_symbol, currency_symbol_sz); |
| |
| plconv2->mon_decimal_point = (pbuf += currency_symbol_sz); |
| std::memcpy (pbuf, plconv->mon_decimal_point, mon_decimal_point_sz); |
| |
| plconv2->mon_thousands_sep = (pbuf += mon_decimal_point_sz); |
| std::memcpy (pbuf, plconv->mon_thousands_sep, mon_thousands_sep_sz); |
| |
| plconv2->mon_grouping = (pbuf += mon_thousands_sep_sz); |
| std::memcpy (pbuf, plconv->mon_grouping, mon_grouping_sz); |
| |
| plconv2->positive_sign = (pbuf += mon_grouping_sz); |
| std::memcpy (pbuf, plconv->positive_sign, positive_sign_sz); |
| |
| plconv2->negative_sign = (pbuf += positive_sign_sz); |
| std::memcpy (pbuf, plconv->negative_sign, negative_sign_sz); |
| |
| plconv2->int_frac_digits = plconv->int_frac_digits; |
| plconv2->frac_digits = plconv->frac_digits; |
| plconv2->p_cs_precedes = plconv->p_cs_precedes; |
| plconv2->p_sep_by_space = plconv->p_sep_by_space; |
| plconv2->n_cs_precedes = plconv->n_cs_precedes; |
| plconv2->n_sep_by_space = plconv->n_sep_by_space; |
| plconv2->p_sign_posn = plconv->p_sign_posn; |
| plconv2->n_sign_posn = plconv->n_sign_posn; |
| |
| #ifndef _RWSTD_NO_LCONV_INT_FMAT |
| |
| plconv2->int_p_cs_precedes = plconv->int_p_cs_precedes; |
| plconv2->int_p_sep_by_space = plconv->int_p_sep_by_space; |
| plconv2->int_n_cs_precedes = plconv->int_n_cs_precedes; |
| plconv2->int_n_sep_by_space = plconv->int_n_sep_by_space; |
| plconv2->int_p_sign_posn = plconv->int_p_sign_posn; |
| plconv2->int_n_sign_posn = plconv->int_n_sign_posn; |
| |
| #endif // _RWSTD_NO_LCONV_INT_FMAT |
| |
| return plconv2; |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| class Test |
| { |
| std::locale loc_; |
| bool intl_; // international? |
| const char *char_name_; // charT name ("char" or "wchar_t") |
| const char *locname_; |
| const char *lang_; |
| const char *lc_all_; |
| const char *lc_monetary_; |
| |
| public: |
| |
| typedef std::moneypunct<charT, false> Punct; |
| typedef std::moneypunct<charT, true> IntlPunct; |
| |
| Test (const char *cname, bool intl) |
| : loc_ (), intl_ (intl), |
| char_name_ (cname), |
| locname_ (0), lang_ (0), lc_all_ (0), lc_monetary_ (0) { } |
| |
| void runTest (); |
| |
| void check_decimal_point (charT); |
| void check_thousands_sep (charT); |
| void check_frac_digits (int); |
| void check_grouping (const std::string&); |
| |
| typedef std::char_traits<charT> Traits; |
| typedef std::allocator<charT> Allocator; |
| typedef std::basic_string<charT, Traits, Allocator> String; |
| |
| void check_curr_symbol (const String&); |
| void check_positive_sign (const String&); |
| void check_negative_sign (const String&); |
| |
| void check_format (const std::lconv&); |
| void check_format (bool, std::money_base::pattern, const UChar [3]); |
| |
| bool check_moneypunct (const char*); |
| }; |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| runTest() |
| { |
| // create a copy of the classic C locale |
| loc_ = std::locale::classic (); |
| |
| // verify (at compile time) that _bynam facets can be used |
| // to specialize use_facet() and has_facet() |
| typedef std::moneypunct_byname<charT, false> MoneyPunctByname; |
| typedef std::moneypunct_byname<charT, true> MoneyPunctIntlByname; |
| |
| if (1) { |
| if (_STD_HAS_FACET (MoneyPunctByname, loc_)) |
| _V3_USE_FACET (MoneyPunctByname, loc_); |
| |
| if (_STD_HAS_FACET (MoneyPunctIntlByname, loc_)) |
| _V3_USE_FACET (MoneyPunctIntlByname, loc_); |
| } |
| |
| // note that values in 7.4 of C89, and 7.11, p2 of C99, respectively, |
| // or '\0' and '\0' for decimal_point and thousands_sep, are specified |
| // in 22.2.3.1.2, p1 and p2 of C++98 to be '.', and ',' in the C locale |
| // |
| check_decimal_point (charT ('.')); |
| check_thousands_sep (charT (',')); |
| check_frac_digits (0 /* i.e., not available, or same as CHAR_MAX in C */); |
| check_grouping (""); |
| |
| const charT empty[] = { '\0' }; |
| check_curr_symbol (empty); |
| check_positive_sign (empty); |
| check_negative_sign (empty); |
| |
| // verify 22.2.6.3.2, p7: |
| // moneypunct<char>, moneypunct<wchar_t>, moneypunct<char,true>, and |
| // moneypunct<wchar_t,true>, return an object of type pattern initialized |
| // to { symbol, sign, none, value } |
| std::money_base::pattern fmat = { |
| // initializer must be properly bracketed to prevent g++ warnings |
| { |
| std::money_base::symbol, |
| std::money_base::sign, |
| std::money_base::none, |
| std::money_base::value |
| } |
| }; |
| |
| const UChar cpat [4] = { '?', '?', '?', 0 }; |
| check_format (false /* negative */, fmat, cpat); |
| check_format (true /* positive */, fmat, cpat); |
| |
| // exercise the native locale (affected by the environment |
| // variables LANG, LC_ALL, LC_MONETARY, etc.) |
| check_moneypunct (""); // 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; s += std::strlen (s) + 1) { |
| if (check_moneypunct (s)) |
| if ( !first_non_c |
| && std::strcmp ("C", s) |
| && std::strcmp ("POSIX", s)) { |
| first_non_c = s; |
| } |
| } |
| |
| if (!first_non_c) |
| return; |
| |
| // verify that moneypunct behaves correctly when LC_ALL is set |
| // to the name of the (non-C, non-POSIX) locale |
| char envvar [80]; |
| |
| // make sure buffer doesn't overflow |
| assert (std::strlen(first_non_c) + sizeof "LC_ALL=" <= sizeof envvar); |
| |
| std::sprintf (envvar, "LC_ALL=%s", first_non_c); |
| rw_putenv (envvar); |
| check_moneypunct (""); |
| |
| // remove LC_ALL from the environment |
| rw_putenv ("LC_ALL="); |
| } |
| |
| /**************************************************************************/ |
| |
| std::string convert (const char*, const char *s, const char*) |
| { |
| return std::string (s); |
| } |
| |
| |
| // convert a multibyte character string in an external representation |
| // to wstring object in an internal representation |
| std::wstring |
| convert (const char *locname, const char *s, const wchar_t*) |
| { |
| // save the name of the original locale |
| const char *savename = std::setlocale (LC_ALL, 0); |
| |
| // switch to (named) locale |
| const char *loc = std::setlocale (LC_ALL, locname); |
| if (!loc) { |
| rw_fprintf (rw_stderr, |
| "%s:%d: setlocale (LC_ALL, %s) = 0: %m\n", |
| locname); |
| return std::wstring (); |
| } |
| |
| // use an extension: allocate but do not initialize |
| std::wstring res ((wchar_t*)0, 64); |
| |
| for ( ; ; ) { |
| // try to convert, resizing buffer if necessary |
| std::size_t n = |
| std::mbstowcs (&res [0], s, res.capacity ()); |
| |
| if (res.capacity () == n) |
| // increase capacity |
| res.reserve (res.capacity () * 2); |
| else if (std::size_t (-1) == n) { |
| |
| // restore the original locale before printing out |
| // the error message (we don't want it localized) |
| std::setlocale (LC_ALL, savename); |
| |
| rw_fprintf (rw_stderr, |
| "%s:%d: mbstowcs(..., %#s, %zu) " |
| "= -1: %m\n", __FILE__, __LINE__, |
| s, res.capacity ()); |
| res = std::wstring (); // mbstowcs() error |
| break; |
| } |
| else { |
| // shrink if necessary |
| res.resize (n); |
| break; |
| } |
| } |
| |
| // restore original locale |
| std::setlocale (LC_ALL, savename); |
| |
| return res; |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| bool Test<charT>:: |
| check_moneypunct (const char *locname) |
| { |
| // (try to) set the global C locale |
| char locnamebuf [256]; |
| const char *loc = std::setlocale (LC_MONETARY, locname); |
| if (!loc) |
| return false; |
| |
| loc = std::strcpy (locnamebuf, loc); |
| |
| locname_ = loc; |
| |
| // the values of the environment variables LANG, LC_ALL, |
| // and LC_MONETARY (and others) affect the native locale |
| lang_ = std::getenv ("LANG"); |
| lc_all_ = std::getenv ("LC_ALL"); |
| lc_monetary_ = std::getenv ("LC_MONETARY"); |
| |
| if (!lang_) |
| lang_ = "(null)"; |
| if (!lc_all_) |
| lc_all_ = "(null)"; |
| if (!lc_monetary_) |
| lc_monetary_ = "(null)"; |
| |
| _TRY { |
| |
| // get a pointer to lconv and copy data to a temporray buffer |
| const std::lconv* const plconv = lconvdup (std::localeconv ()); |
| |
| if (!plconv) |
| return false; |
| |
| // reset to default locale given by LC_LANG |
| std::setlocale (LC_MONETARY, ""); |
| |
| // create a new C++ locale object |
| loc_ = std::locale (locname); |
| |
| // check that newly constructed locale matches |
| |
| // the MBCS mon_decimal_point and mon_thousands_sep must |
| // be properly converted (i.e., *mon_decimal_point may not |
| // the same as s [0] after the conversion) |
| String s; |
| |
| // `locname' may be the empty string, in which case `loc' |
| // will be set to the actual name of the locale |
| if ( std::strcmp ("C", locname) |
| && std::strcmp ("C", loc)) { |
| |
| // named locale other than "C" or "POISX" |
| s = ::convert (locname, plconv->mon_decimal_point, (charT*)0); |
| check_decimal_point (s.size () ? s [0] : charT ()); |
| |
| s = ::convert (locname, plconv->mon_thousands_sep, (charT*)0); |
| check_thousands_sep (s.size () ? s [0] : charT ()); |
| |
| check_frac_digits (plconv->int_frac_digits); |
| } |
| else { |
| // note that values in 7.4 of C89, and 7.11, p2 of C99, |
| // respectively, or '\0' and '\0' for decimal_point and |
| // thousands_sep, are specified in 22.2.3.1.2, p1 and p2 |
| // of C++98 to be '.', and ',' in the C locale |
| check_decimal_point (charT ('.')); |
| check_thousands_sep (charT (',')); |
| |
| // frac_digits is specified as CHAR_MAX (i.e., not available) by C99 |
| // verify that the C++ value is NOT CHAR_MAX, but rather 0 (it could |
| // be negative with the same result) |
| check_frac_digits (0); |
| } |
| |
| check_grouping (plconv->mon_grouping); |
| |
| // convert a (possibly) multibyte string in external |
| // representation to one in internal representation |
| s = intl_ ? ::convert (locname, plconv->int_curr_symbol, (charT*)0) |
| : ::convert (locname, plconv->currency_symbol, (charT*)0); |
| |
| check_curr_symbol (s); |
| |
| s = ::convert (locname, plconv->positive_sign, (charT*)0); |
| |
| check_positive_sign (s); |
| |
| s = ::convert (locname, plconv->negative_sign, (charT*)0); |
| |
| check_negative_sign (s); |
| |
| check_format (*plconv); |
| |
| // cast away constness to work around compiler bugs (e.g., MSVC 6) |
| delete _RWSTD_CONST_CAST (std::lconv*, plconv); |
| } |
| _CATCH (...) { |
| return false; |
| } |
| return true; |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_decimal_point (charT result) |
| { |
| const charT c = intl_ ? _V3_USE_FACET (IntlPunct, loc_).decimal_point () |
| : _V3_USE_FACET (Punct, loc_).decimal_point (); |
| |
| rw_assert (std::char_traits<charT>::eq (c, result), 0, __LINE__, |
| "moneypunct<%s, %b>::decimal_point() == %#lc, got %#lc " |
| "in locale (\"%s\") with LANG=%s, LC_ALL=%s, LC_MONETARY=%s", |
| char_name_, intl_, result, c, |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_thousands_sep (charT result) |
| { |
| const charT c = intl_ ? _V3_USE_FACET (IntlPunct, loc_).thousands_sep () |
| : _V3_USE_FACET (Punct, loc_).thousands_sep (); |
| |
| rw_assert (std::char_traits<charT>::eq (c, result), 0, __LINE__, |
| "moneypunct<%s, %b>::thousands_sep() == %#lc, got %#lc " |
| "in locale (\"%s\") with LANG=%s, LC_ALL=%s, LC_MONETARY=%s", |
| char_name_, intl_, result, c, |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_frac_digits (int result) |
| { |
| const int i = intl_ ? _V3_USE_FACET (IntlPunct, loc_).frac_digits () |
| : _V3_USE_FACET (Punct, loc_).frac_digits (); |
| |
| rw_assert (i == result, 0, __LINE__, |
| "moneypunct<%s, %b>::frac_digits() == %d, got %d " |
| "in locale (\"%s\") with LANG=%s, LC_ALL=%s, LC_MONETARY=%s", |
| char_name_, intl_, result, i, |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_grouping (const std::string &result) |
| { |
| const std::string s = intl_ ? _V3_USE_FACET (IntlPunct, loc_).grouping () |
| : _V3_USE_FACET (Punct, loc_).grouping (); |
| |
| if ( s != result |
| && s.size () != result.size () && s.size () && result.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 () > result.size () ? &result : &s; |
| const std::string *shrt = s.size () < result.size () ? &result : &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, %b>::grouping() equivalent to %#s, got %#s " |
| "in locale (%#s) with LANG=%s, LC_ALL=%s, " |
| "LC_MONETARY=%s", |
| char_name_, intl_, result.c_str (), s.c_str (), |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| else |
| rw_assert (s == result, 0, __LINE__, |
| "moneypunct<%s, %b>::grouping() == %#s, got %#s " |
| "in locale (%#s) with LANG=%s, LC_ALL=%s, " |
| "LC_MONETARY=%s", |
| char_name_, intl_, result.c_str (), s.c_str (), |
| locname_, lang_, lc_all_, lc_monetary_); |
| |
| #if 0 |
| |
| // Test disabled: what's meant here is that the pattern, NOT the value, |
| // is defined identically as that of numpunct<charT>::do_grouping(). |
| // The values are often going to be different since one uses |
| // lconv::grouping, and the other lconv::mon_grouping. |
| |
| // 22.2.6.3.2, p3: do_grouping() returns a pattern defined identically |
| // as the result of numpunct<charT>::do_grouping(). |
| |
| const std::string grp = |
| _V3_USE_FACET (std::numpunct<charT>, loc_).grouping (); |
| |
| rw_assert (s == grp, 0, __LINE__, |
| "moneypunct<%s, %b>::grouping() == numpunct<>::grouping() == " |
| "%{*Ac}, got %{*Ac} in locale (\"%s\") with LANG=%s, LC_ALL=%s, " |
| "LC_MONETARY=%s", |
| char_name_, intl_, |
| int (sizeof (charT)), result.c_str (), |
| int (sizeof (charT)), s.c_str (), |
| locname_, lang_, lc_all_, lc_monetary_); |
| |
| #endif // 0 |
| |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_curr_symbol (const String &result) |
| { |
| const String s = intl_ ? _V3_USE_FACET (IntlPunct, loc_).curr_symbol () |
| : _V3_USE_FACET (Punct, loc_).curr_symbol (); |
| |
| rw_assert (s == result, 0, __LINE__, |
| "moneypunct<%s, %b>::curr_symbol() == %{*Ac}, got %{*Ac} " |
| "in locale (\"%s\") with LANG=%s, LC_ALL=%s, LC_MONETARY=%s", |
| char_name_, intl_, |
| int (sizeof (charT)), result.c_str (), |
| int (sizeof (charT)), s.c_str (), |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_positive_sign (const String &result) |
| { |
| const String s = intl_ ? _V3_USE_FACET (IntlPunct, loc_).positive_sign () |
| : _V3_USE_FACET (Punct, loc_).positive_sign (); |
| |
| rw_assert (s == result, 0, __LINE__, |
| "moneypunct<%s, %b>::positive_sign() == %{*Ac}, got %{*Ac} " |
| "in locale (\"%s\") with LANG=%s, LC_ALL=%s, LC_MONETARY=%s", |
| char_name_, intl_, |
| int (sizeof (charT)), result.c_str (), |
| int (sizeof (charT)), s.c_str (), |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_negative_sign (const String &result) |
| { |
| const String s = intl_ ? _V3_USE_FACET (IntlPunct, loc_).negative_sign () |
| : _V3_USE_FACET (Punct, loc_).negative_sign (); |
| |
| rw_assert (s == result, 0, __LINE__, |
| "moneypunct<%s, %b>::negative_sign() == %{*Ac}, got %{*Ac} " |
| "in locale (\"%s\") with LANG=%s, LC_ALL=%s, LC_MONETARY=%s", |
| char_name_, intl_, |
| int (sizeof (charT)), result.c_str (), |
| int (sizeof (charT)), s.c_str (), |
| locname_, lang_, lc_all_, lc_monetary_); |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_format (const std::lconv &lc) |
| { |
| const UChar cpat [2][2][3] = { |
| // ^ ^ ^ |
| // | | | |
| // | | +--- cs_precedes, sep_by_space, sign_posn |
| // | +------ positive, negative |
| // +--------- local, international |
| |
| { |
| // C90 positive local format |
| { lc.p_cs_precedes, lc.p_sep_by_space, lc.p_sign_posn }, |
| |
| // C90 negative local format |
| { lc.n_cs_precedes, lc.n_sep_by_space, lc.n_sign_posn } |
| }, |
| |
| #ifndef _RWSTD_NO_LCONV_INT_FMAT |
| |
| { // C99 positive international format |
| { lc.int_p_cs_precedes, lc.int_p_sep_by_space, lc.int_p_sign_posn}, |
| |
| // C99 negative international format |
| { lc.int_n_cs_precedes, lc.int_n_sep_by_space, lc.int_n_sign_posn } |
| } |
| |
| #else // if defined (_RWSTD_NO_LCONV_INT_FMAT) |
| |
| { |
| { lc.p_cs_precedes, lc.p_sep_by_space, lc.p_sign_posn }, |
| { lc.n_cs_precedes, lc.n_sep_by_space, lc.n_sign_posn } |
| } |
| |
| #endif // _RWSTD_NO_LCONV_INT_FMAT |
| }; |
| |
| // cs_precedes [0..1]: |
| // |
| // An integer set to 1 if the currency_symbol precedes the value |
| // for a monetary value, and set to 0 if the symbol succeeds |
| // the value. |
| |
| // sep_by_space [0..2]: |
| // |
| // 0 No space separates the currency_symbol from the value for |
| // a monetary value. |
| // 1 If the currency symbol and sign string are adjacent, a space |
| // separates them from the value; otherwise, a space separates |
| // the currency symbol from the value. |
| // 2 If the currency symbol and sign string are adjacent, a space |
| // separates them; otherwise, a space separates the sign string |
| // from the value. |
| |
| // sign_posn [0..4]: |
| // |
| // An integer set to a value indicating the positioning of the |
| // positive_sign for a monetary value. The following integer |
| // values shall be recognized: |
| // |
| // 0 Parentheses enclose the value and the currency_symbol. |
| // 1 The sign string precedes the value and the currency_symbol. |
| // 2 The sign string succeeds the value and the currency_symbol. |
| // 3 The sign string immediately precedes the currency_symbol. |
| // 4 The sign string immediately succeeds the currency_symbol. |
| |
| enum { |
| none = std::money_base::none, |
| space = std::money_base::space, |
| symbol = std::money_base::symbol, |
| sign = std::money_base::sign, |
| value = std::money_base::value |
| }; |
| |
| static const std::money_base::pattern patterns[] = { |
| |
| /* 000: -1$. */ { { sign, value, symbol, none } }, // "\3\4\2\0" |
| /* 001: -1$. */ { { sign, value, symbol, none } }, // "\3\4\2\0" |
| /* 002: 1$-. */ { { value, symbol, sign, none } }, // "\4\2\3\0" |
| /* 003: 1-$. */ { { value, sign, symbol, none } }, // "\4\3\2\0" |
| /* 004: 1$-. */ { { value, symbol, sign, none } }, // "\4\2\3\0" |
| |
| /* 010: -1 $ */ { { sign, value, space, symbol } }, // "\3\4\1\2" |
| /* 011: -1 $ */ { { sign, value, space, symbol } }, // "\3\4\1\2" |
| /* 012: 1 $- */ { { value, space, symbol, sign } }, // "\4\1\2\3" |
| /* 013: 1 -$ */ { { value, space, sign, symbol } }, // "\4\3\3\2" |
| /* 014: 1 $- */ { { value, space, symbol, sign } }, // "\4\1\2\3" |
| |
| /* 020: - 1$ */ { { sign, space, value, symbol } }, // "\3\1\4\2" |
| /* 021: - 1$ */ { { sign, space, value, symbol } }, // "\3\\14\2" |
| /* 022: 1$ - */ { { value, symbol, space, sign } }, // "\4\2\1\3" |
| /* 023: 1- $ */ { { value, sign, space, symbol } }, // "\4\3\1\2" |
| /* 024: 1$ - */ { { value, symbol, space, sign } }, // "\4\2\1\3" |
| |
| /* 100: -$1. */ { { sign, symbol, value, none } }, // "\3\2\4\0" |
| /* 101: -$1. */ { { sign, symbol, value, none } }, // "\3\2\4\0" |
| /* 102: $1-. */ { { symbol, value, sign, none } }, // "\2\4\3\0" |
| /* 103: -$1. */ { { sign, symbol, value, none } }, // "\3\2\4\0" |
| /* 104: $-1. */ { { symbol, sign, value, none } }, // "\2\3\4\0" |
| |
| /* 110: -$ 1 */ { { sign, symbol, space, value } }, // "\3\2\1\4" |
| /* 111: -$ 1 */ { { sign, symbol, space, value } }, // "\3\2\1\4" |
| /* 112: $ 1- */ { { symbol, space, value, sign } }, // "\2\1\4\3" |
| /* 113: -$ 1 */ { { sign, symbol, space, value } }, // "\3\2\1\4" |
| /* 114: $- 1 */ { { symbol, sign, space, value } }, // "\2\3\1\4" |
| |
| /* 120: - $1 */ { { sign, space, symbol, value } }, // "\3\1\2\4" |
| /* 121: - $1 */ { { sign, space, symbol, value } }, // "\3\1\2\4" |
| /* 122: $1 - */ { { symbol, value, space, sign } }, // "\2\4\1\3" |
| /* 123: - $1 */ { { sign, space, symbol, value } }, // "\3\1\2\4" |
| /* 124: $ -1 */ { { symbol, space, sign, value } } // "\2\1\3\4" |
| }; |
| |
| for (int neg = 0; neg != 2; ++neg) { |
| |
| enum { cs_precedes, sep_by_space, sign_posn }; |
| |
| // ignore unspecified formats (-1) |
| if ( cpat [intl_][neg][cs_precedes] > 1 |
| || cpat [intl_][neg][sep_by_space] > 2 |
| || cpat [intl_][neg][sign_posn] > 4) |
| continue; |
| |
| const int inx = |
| cpat [intl_][neg][cs_precedes] * 3 * 5 |
| + cpat [intl_][neg][sep_by_space] * 5 |
| + cpat [intl_][neg][sign_posn]; |
| |
| check_format (!neg, patterns [inx], cpat [intl_][neg]); |
| } |
| } |
| |
| /**************************************************************************/ |
| |
| template <class charT> |
| void Test<charT>:: |
| check_format (bool pos, std::money_base::pattern result, const UChar cpat [3]) |
| { |
| const std::money_base::pattern pat = |
| intl_ ? pos ? _V3_USE_FACET (IntlPunct, loc_).pos_format () |
| : _V3_USE_FACET (IntlPunct, loc_).neg_format () |
| : pos ? _V3_USE_FACET (Punct, loc_).pos_format () |
| : _V3_USE_FACET (Punct, loc_).neg_format (); |
| |
| // number of times each symbol appears in pattern |
| int counts [5] = { 0 }; |
| |
| for (unsigned i = 0; i != sizeof pat.field / sizeof *pat.field; i++) { |
| |
| if (UChar (pat.field [i] < char (sizeof counts / sizeof *counts))) |
| ++counts [UChar (pat.field [i])]; |
| |
| // verify 22.2.6.3, p1 |
| if (std::money_base::none == pat.field [i] && !i) |
| rw_assert (false, 0, __LINE__, |
| "moneypunct<%s, %b>::%s_format() == %{LM}, " |
| "none must not appear first", |
| char_name_, intl_, pos ? "pos" : "neg", |
| pat.field); |
| |
| if ( std::money_base::space == pat.field [i] |
| && !i && i != sizeof pat.field / sizeof *pat.field - 1) |
| rw_assert (false, 0, __LINE__, |
| "moneypunct<%s, %b>::%s_format() == %{LM}, " |
| "space must not appear first or last", |
| char_name_, intl_, pos ? "pos" : "neg", |
| pat.field); |
| } |
| |
| // verify that the actual pattern matches the expected one |
| rw_assert (0 == std::memcmp (&pat, &result, sizeof pat), 0, __LINE__, |
| "moneypunct<%s, %b>::%s_format() == %{LM}, got %{LM}; " |
| "(cs_precedes = '\\%o', sep_by_space = '\\%o', " |
| "sign_posn = '\\%o')", |
| char_name_, intl_, pos ? "pos" : "neg", |
| pat.field, result.field, cpat [0], cpat [1], cpat [2]); |
| |
| // verify 22.2.6.3, p1 |
| rw_assert (1 == counts [std::money_base::symbol], 0, __LINE__, |
| "money_base::symbol must apear exactly once, did %d times", |
| counts [std::money_base::symbol]); |
| |
| rw_assert (1 == counts [std::money_base::sign], 0, __LINE__, |
| "money_base::sign must apear exactly once, did %d times", |
| counts [std::money_base::sign]); |
| |
| rw_assert (1 == counts [std::money_base::value], 0, __LINE__, |
| "money_base::value must apear exactly once, did %d times", |
| counts [std::money_base::value]); |
| |
| rw_assert (1 == counts [std::money_base::space] |
| + counts [std::money_base::none], 0, __LINE__, |
| "money_base::space or money_base::none must appear " |
| "exactly once, did %d times", |
| counts [std::money_base::space] |
| + counts [std::money_base::none]); |
| } |
| |
| /****************************************************************************/ |
| |
| static int |
| run_test (int, char**) |
| { |
| { |
| Test<char> t ("char", false); |
| t.runTest (); |
| } |
| |
| { |
| Test<char> t ("char", true); |
| t.runTest (); |
| } |
| |
| #ifndef _RWSTD_NO_WCHAR_T |
| |
| { |
| Test<wchar_t> t ("wchar_t", false); |
| t.runTest (); |
| } |
| |
| { |
| Test<wchar_t> t ("wchar_t", true); |
| t.runTest (); |
| } |
| |
| #endif // _RWSTD_NO_WCHAR_T |
| |
| return 0; |
| } |
| |
| |
| /****************************************************************************/ |
| |
| int main (int argc, char *argv[]) |
| { |
| return rw_test (argc, argv, __FILE__, |
| "lib.locale.moneypunct", |
| 0 /* no comment */, |
| run_test, |
| "", |
| (void*)0 /* sentinel */); |
| } |