text: /************************************************************************
   *
   * 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 <rw_driver.h>
  #include <rw_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);
  }
  