blob: 316792d113d2ce197596226cb3c43f3f6c56f08a [file] [log] [blame]
/***************************************************************************
*
* 22.locale.ctype.cpp - Tests exercising the ctype 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 2001-2006 Rogue Wave Software.
*
**************************************************************************/
// DESCRIPTION: test iterates over the locales installed on a machine,
// calling the C character classification functions and
// their C++ counterpart(s), comparing the results of
// the calls against one another.
#include <rw/_defs.h>
#if defined __linux__
// on Linux define _XOPEN_SOURCE to get CODESET defined in <langinfo.h>
# define _XOPEN_SOURCE 500 /* Single Unix conformance */
// bring __int32_t into scope (otherwise <wctype.h> fails to compile)
# include <sys/types.h>
#endif // __linux__
// see Onyx PR #28150
#if defined (__SUNPRO_CC) && __SUNPRO_CC <= 0x540
# include <wchar.h>
#endif // defined (__SUNPRO_CC) && __SUNPRO_CC <= 0x540
#include <locale>
#include <climits>
#include <clocale>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cwchar> // for WEOF, btowc(), wctob()
#include <cwctype> // for iswxxx()
#if !defined (_MSC_VER)
# if !defined (LC_MESSAGES)
# define LC_MESSAGES _RWSTD_LC_MESSAGES
# endif // LC_MESSAGES
# include <langinfo.h>
#endif // _MSC_VER
#include <driver.h>
#include <file.h> // for SLASH
#include <rw_locale.h> // for rw_locales()
/**************************************************************************/
// the root of the locale directory (RWSTD_LOCALE_ROOT)
// not set here to avoid Solaris 7 putenv() bug (PR #30017)
const char* locale_root;
#define NLOOPS 25
#define MAX_STR_SIZE 16
#define BEGIN_LOCALE_LOOP(num, locname, loop_cntrl) \
for (const char* locname = rw_locales (LC_CTYPE, 0); \
locname && *locname; locname += std::strlen (locname) + 1) { \
_TRY { \
const std::locale loc (locname); \
const std::ctype<char> &ctc = \
_STD_USE_FACET (std::ctype<char>, loc); \
_RWSTD_UNUSED (ctc); \
const std::ctype<charT> &ctp = \
_STD_USE_FACET (std::ctype<charT>, loc); \
for (int loop_cntrl = 0; loop_cntrl < int (num); loop_cntrl++)
#define END_LOCALE_LOOP(locname) \
} \
_CATCH (...) { \
rw_assert (0, 0, __LINE__, \
"locale (\"%s\") threw an exception", locname); \
} \
}
// for notational convenience
typedef unsigned char UChar;
#define ALPHA std::ctype_base::alpha
#define UPPER std::ctype_base::upper
#define LOWER std::ctype_base::lower
#define DIGIT std::ctype_base::digit
#define SPACE std::ctype_base::space
#define CNTRL std::ctype_base::cntrl
#define PUNCT std::ctype_base::punct
#define XDIGIT std::ctype_base::xdigit
#define GRAPH std::ctype_base::graph
#define PRINT std::ctype_base::print
// wrapper functions for the c library char and wchar_t functions
int libc_isalpha (char ch)
{
return (std::isalpha)(UChar (ch));
}
int libc_isspace (char ch)
{
return (std::isspace)(UChar (ch));
}
int libc_isprint (char ch)
{
return (std::isprint)(UChar (ch));
}
int libc_iscntrl (char ch)
{
return (std::iscntrl)(UChar (ch));
}
int libc_isupper (char ch)
{
return (std::isupper)(UChar (ch));
}
int libc_islower (char ch)
{
return (std::islower)(UChar (ch));
}
int libc_isdigit (char ch)
{
return (std::isdigit)(UChar (ch));
}
int libc_ispunct (char ch)
{
return (std::ispunct)(UChar (ch));
}
int libc_isxdigit (char ch)
{
return (std::isxdigit)(UChar (ch));
}
int libc_isalnum (char ch)
{
return (std::isalnum)(UChar (ch));
}
int libc_isgraph (char ch)
{
return (std::isgraph)(UChar (ch));
}
char libc_tolower (char ch)
{
return std::tolower (UChar (ch));
}
char libc_toupper (char ch)
{
return (std::toupper)(UChar (ch));
}
std::ctype_base::mask libc_mask (int mask, char ch, const char *locname)
{
char curlocname [256];
if (locname) {
std::strcpy (curlocname, std::setlocale (LC_CTYPE, 0));
if (0 == std::setlocale (LC_CTYPE, locname))
return std::ctype_base::mask ();
}
const int c = UChar (ch);
int result = 0;
if (mask & ALPHA && (std::isalpha)(c))
result |= ALPHA;
if (mask & CNTRL && (std::iscntrl)(c))
result |= CNTRL;
if (mask & DIGIT && (std::isdigit)(c))
result |= DIGIT;
if (mask & GRAPH && (std::isgraph)(c))
result |= GRAPH;
if (mask & LOWER && (std::islower)(c))
result |= LOWER;
if (mask & PRINT && (std::isprint)(c))
result |= PRINT;
if (mask & PUNCT && (std::ispunct)(c))
result |= PUNCT;
if (mask & SPACE && (std::isspace)(c))
result |= SPACE;
if (mask & UPPER && (std::isupper)(c))
result |= UPPER;
if (mask & XDIGIT && (std::isxdigit)(c))
result |= XDIGIT;
if (locname)
std::setlocale (LC_CTYPE, curlocname);
return std::ctype_base::mask (result);
}
inline bool libc_is (std::ctype_base::mask mask, char ch, const char *locname)
{
const std::ctype_base::mask m = libc_mask (mask, ch, locname);
return 0 != (m & mask);
}
std::size_t c_strlen (const char *s1)
{
return std::strlen (s1);
}
int c_strcmp (const char *s1, const char *s2)
{
int ret = std::strcoll (s1, s2);
return ret ? ret > 0 ? 1 : -1 : 0;
}
const char* narrow (char *dst, const char *src)
{
if (src == dst || !src || !dst)
return src;
std::memcpy (dst, src, std::strlen (src) + 1);
return dst;
}
const char* widen (char *dst, const char *src)
{
if (src == dst || !src || !dst)
return src;
std::memcpy (dst, src, std::strlen (src) + 1);
return dst;
}
char widen (char, char ch, const char*)
{
return ch;
}
char narrow (char ch, const char*)
{
return ch;
}
// cond1() verifies condition [1] in Test::test_narrow_widen()
// below using libc functions
bool cond1 (std::ctype_base::mask mask, char ch, const char *locname)
{
char curlocname [256];
std::strcpy (curlocname, std::setlocale (LC_CTYPE, 0));
if (0 == std::setlocale (LC_CTYPE, locname))
return false;
#ifdef __SUNPRO_CC
// working around a SunPro bug (PR #28150)
using std::wint_t;
#endif // __SUNPRO_CC
#ifndef _RWSTD_NO_BTOWC
const std::wint_t wc = std::btowc (UChar (ch));
#elif !defined (_RWSTD_NO_MBSTOWCS)
wchar_t tmp;
const std::wint_t wc = 1 == std::mbstowcs (&tmp, &ch, 1) ? tmp : WEOF;
#else
const std::wint_t wc = WEOF;
#endif // _RWSTD_NO_BTOWC, _RWSTD_NO_MBSTOWCS
const bool result =
WEOF == wc || libc_is (mask, ch, 0) || !libc_is (mask, wchar_t (wc), 0);
std::setlocale (LC_CTYPE, curlocname);
return result;
}
// cond3() overloads verify condition [3] in Test::test_narrow_widen()
// below using libc functions
bool cond3 (std::ctype_base::mask, char, const char*)
{
return true;
}
#ifndef _RWSTD_NO_WCHAR_T
int libc_isalpha (wchar_t ch)
{
return (std::iswalpha)(ch);
}
int libc_isspace (wchar_t ch)
{
return (std::iswspace)(ch);
}
int libc_isprint (wchar_t ch)
{
return (std::iswprint)(ch);
}
int libc_iscntrl (wchar_t ch)
{
return (std::iswcntrl)(ch);
}
int libc_isupper (wchar_t ch)
{
return (std::iswupper)(ch);
}
int libc_islower (wchar_t ch)
{
return (std::iswlower)(ch);
}
int libc_isdigit (wchar_t ch)
{
return (std::iswdigit)(ch);
}
int libc_ispunct (wchar_t ch)
{
return (std::iswpunct)(ch);
}
int libc_isxdigit (wchar_t ch)
{
return (std::iswxdigit)(ch);
}
int libc_isalnum (wchar_t ch)
{
return (std::iswalnum)(ch);
}
int libc_isgraph (wchar_t ch)
{
return (std::iswgraph)(ch);
}
wchar_t libc_tolower (wchar_t ch)
{
return (std::towlower)(ch);
}
wchar_t libc_toupper (wchar_t ch)
{
return (std::towupper)(ch);
}
std::size_t c_strlen (const wchar_t *s1)
{
return std::wcslen (s1);
}
int c_strcmp (const wchar_t *s1, const wchar_t *s2)
{
int ret = std::wcscmp (s1, s2);
return ret ? ret > 0 ? 1 : -1 : 0;
}
const char* narrow (char *dst, const wchar_t *src)
{
static char buf [4096];
if (!src)
return 0;
if (!dst)
dst = buf;
std::size_t len = std::wcslen (src);
_RWSTD_ASSERT (len < sizeof buf);
len = std::wcstombs (dst, src, sizeof buf / sizeof *buf);
if (std::size_t (-1) == len)
*dst = 0;
return dst;
}
const wchar_t* widen (wchar_t *dst, const char *src)
{
static wchar_t buf [4096];
if (!src)
return 0;
if (!dst)
dst = buf;
std::size_t len = std::strlen (src);
_RWSTD_ASSERT (len < sizeof buf /sizeof *buf);
len = std::mbstowcs (dst, src, sizeof buf / sizeof *buf);
if (std::size_t (-1) == len)
*dst = 0;
return dst;
}
std::ctype_base::mask libc_mask (int mask, wchar_t ch, const char *locname)
{
char curlocname [256];
if (locname) {
std::strcpy (curlocname, std::setlocale (LC_CTYPE, 0));
if (0 == std::setlocale (LC_CTYPE, locname))
return std::ctype_base::mask ();
}
int result = 0;
if (mask & ALPHA && (std::iswalpha)(ch))
result |= ALPHA;
if (mask & CNTRL && (std::iswcntrl)(ch))
result |= CNTRL;
if (mask & DIGIT && (std::iswdigit)(ch))
result |= DIGIT;
if (mask & GRAPH && (std::iswgraph)(ch))
result |= GRAPH;
if (mask & LOWER && (std::iswlower)(ch))
result |= LOWER;
if (mask & PRINT && (std::iswprint)(ch))
result |= PRINT;
if (mask & PUNCT && (std::iswpunct)(ch))
result |= PUNCT;
if (mask & SPACE && (std::iswspace)(ch))
result |= SPACE;
if (mask & UPPER && (std::iswupper)(ch))
result |= UPPER;
if (mask & XDIGIT && (std::iswxdigit)(ch))
result |= XDIGIT;
if (locname)
std::setlocale (LC_CTYPE, curlocname);
return std::ctype_base::mask (result);
}
bool libc_is (std::ctype_base::mask mask, wchar_t ch, const char *locname)
{
const std::ctype_base::mask m = libc_mask (mask, ch, locname);
return 0 != (m & mask);
}
wchar_t widen (wchar_t, char ch, const char *locname)
{
char curlocname [256];
std::strcpy (curlocname, std::setlocale (LC_CTYPE, 0));
if (0 == std::setlocale (LC_CTYPE, locname))
return UChar (ch);
wchar_t result;
#ifndef _RWSTD_NO_BTOWC
result = std::btowc (UChar (ch));
#elif !defined (_RWSTD_NO_MBTOWC)
if (1 != std::mbtowc (&result, &ch, 1))
result = wchar_t (WEOF);
#else
result = UChar (ch);
#endif // _RWSTD_NO_BTOWC, _RWSTD_NO_MBTOWC
if (locname)
std::setlocale (LC_CTYPE, curlocname);
return result;
}
char narrow (wchar_t ch, const char *locname)
{
char curlocname [256];
std::strcpy (curlocname, std::setlocale (LC_CTYPE, 0));
if (0 == std::setlocale (LC_CTYPE, locname))
return UChar (ch);
char result [MB_LEN_MAX];
#ifndef _RWSTD_NO_WCTOB
result [0] = std::wctob (ch);
#elif !defined (_RWSTD_NO_WCTOMB)
if (1 != std::wctomb (result, ch))
result [0] = '\377';
#else
result [0] = char (ch);
#endif // _RWSTD_NO_WCTOB, _RWSTD_NO_WCTOMB
if (locname)
std::setlocale (LC_CTYPE, curlocname);
return result [0];
}
bool cond3 (std::ctype_base::mask mask, wchar_t ch, const char *locname)
{
char curlocname [256];
std::strcpy (curlocname, std::setlocale (LC_CTYPE, 0));
if (0 == std::setlocale (LC_CTYPE, locname))
return false;
#ifndef _RWSTD_NO_WCTOB
const int byte = std::wctob (ch);
#elif !defined (_RWSTD_NO_WCTOMB)
char buf [MB_LEN_MAX];
const int byte = 1 == std::wctomb (buf, ch) ? buf [0] : EOF;
#else
const int byte = EOF;
#endif // _RWSTD_NO_WCTOB, _RWSTD_NO_WCTOMB
const bool result =
EOF == byte || !libc_is (mask, char (byte), 0) || libc_is (mask, ch, 0);
std::setlocale (LC_CTYPE, curlocname);
return result;
}
#endif // _RWSTD_NO_WCHAR_T
template <class charT>
void gen_str (charT *str, std::size_t size)
{
// generate a random string with the given size
// we do not attempt to check that the size is within the
// valid range for the string.
for (std::size_t i = 0; i < size; i++){
str [i] = UChar (std::rand () % UCHAR_MAX);
// only increment if we are not going to roll over
if (str [i] != charT (UCHAR_MAX - 1U))
++str [i];
}
str [size] = charT ();
_RWSTD_ASSERT (c_strlen (str) == size);
}
// The character map, as well as the source file generated below use
// Unicode literal strings for character symbolic names; the algorithm in
// localedef utility will generate correct databases even if the platform
// does not have a C library locale encoded with the character set we are
// creating.
void write_string (std::FILE *fp, const char *str)
{
// ASCII (ISO-646) character map definition
static const char* const base_chset [] = {
"<U0000>", "<U0001>", "<U0002>", "<U0003>", "<U0004>", "<U0005>",
"<U0006>", "<U0007>", "<U0008>", "<U0009>", "<U000a>", "<U000b>",
"<U000c>", "<U000d>", "<U000e>", "<U000f>", "<U0010>", "<U0011>",
"<U0012>", "<U0013>", "<U0014>", "<U0015>", "<U0016>", "<U0017>",
"<U0018>", "<U0019>", "<U001a>", "<U001b>", "<U001c>", "<U001d>",
"<U001e>", "<U001f>", "<U0020>", "<U0021>", "<U0022>", "<U0023>",
"<U0024>", "<U0025>", "<U0026>", "<U0027>", "<U0028>", "<U0029>",
"<U002a>", "<U002b>", "<U002c>", "<U002d>", "<U002e>", "<U002f>",
"<U0030>", "<U0031>", "<U0032>", "<U0033>", "<U0034>", "<U0035>",
"<U0036>", "<U0037>", "<U0038>", "<U0039>", "<U003a>", "<U003b>",
"<U003c>", "<U003d>", "<U003e>", "<U003f>", "<U0040>", "<U0041>",
"<U0042>", "<U0043>", "<U0044>", "<U0045>", "<U0046>", "<U0047>",
"<U0048>", "<U0049>", "<U004a>", "<U004b>", "<U004c>", "<U004d>",
"<U004e>", "<U004f>", "<U0050>", "<U0051>", "<U0052>", "<U0053>",
"<U0054>", "<U0055>", "<U0056>", "<U0057>", "<U0058>", "<U0059>",
"<U005a>", "<U005b>", "<U005c>", "<U005d>", "<U005e>", "<U005f>",
"<U0060>", "<U0061>", "<U0062>", "<U0063>", "<U0064>", "<U0065>",
"<U0066>", "<U0067>", "<U0068>", "<U0069>", "<U006a>", "<U006b>",
"<U006c>", "<U006d>", "<U006e>", "<U006f>", "<U0070>", "<U0071>",
"<U0072>", "<U0073>", "<U0074>", "<U0075>", "<U0076>", "<U0077>",
"<U0078>", "<U0079>", "<U007a>", "<U007b>", "<U007c>", "<U007d>",
"<U007e>", "<U007f>"
};
static const char* const supp_chset [] = {
// And a set of characters above 128, which have wide equivalents
// with values above 256
/* <U1000> */ "<U1000> \\xa0",
/* <U1001> */ "<U1001> \\xa1",
/* <U1002> */ "<U1002> \\xa2"
};
if (str) {
// write out `str' using the charmap above
for (; *str; ++str)
std::fprintf (fp, "%s", base_chset [UChar (*str)]);
}
else {
#ifndef _RWSTD_NO_NL_LANGINFO
const char* const codeset = nl_langinfo (CODESET);
std::fprintf (fp, "<code_set_name> \"%s\"\n", codeset);
std::fprintf (fp, "<mb_cur_max> 1\n");
std::fprintf (fp, "<mb_cur_min> 1\n");
std::fprintf (fp, "CHARMAP\n");
// write out the basic character set
for (unsigned i = 0; i < sizeof base_chset / sizeof *base_chset; ++i)
std::fprintf (fp, "%s \\x%02x\n", base_chset [i], i);
// write out the additional characters
for (unsigned j = 0; j < sizeof supp_chset / sizeof *supp_chset; ++j)
std::fprintf (fp, "%s\n", supp_chset [j]);
std::fprintf (fp, "END CHARMAP\n");
#endif // _RWSTD_NO_NL_LANGINFO
}
}
// invoke localedef to build a simple locale for testing
const char* create_locale ()
{
// only one locale is enough (avoid invoking localedef more than once)
static const char* locname;
if (locname)
return locname;
// set up RWSTD_LOCALE_ROOT and other environment variables
locale_root = rw_set_locale_root ();
if (0 == locale_root)
return 0;
// create a temporary locale definition file that exercises as
// many different parts of the collate standard as possible
char srcfname [256];
std::sprintf (srcfname, "%s%slocale.src", locale_root, SLASH);
std::FILE *fout = std::fopen (srcfname, "w");
if (!fout) {
std::fprintf (stderr, "%s:%d: fopen(\"%s\", \"w\") failed\n",
__FILE__, __LINE__, srcfname);
return 0;
}
static const char text[] = {
"escape_char /\n"
"LC_CTYPE\n"
"# <A> <B> <C> MYANMAR LETTER KHA\n"
"upper <U0041>;<U0042>;<U0043>;<U1001>\n"
" <a> <b> <c> MYANMAR LETTER KA\n"
"lower <U0061>;<U0062>;<U0063>;<U1000>\n"
"alpha <U0061>;<U0062>;<U0063>;<U0041>;"
"<U0042>;<U0043>;<U1000>;<U1001>\n"
"digit <U0031>;<U0032>;<U0033>;<U1002>\n"
"space <U0020>\n"
"cntrl <U0000>\n"
" <!> <\">\n"
"punct <U0021>; <U0022>\n"
"graph <U0041>;<U0042>;<U0043>;<U0061>;<U0062>;<U0063>;"
"<U1000>;<U1001>;<U1002>;<U1003>;<U1004>;<U1005>;"
"<U0031>;<U0032>;<U0033>;<U0020>;<U0021>;<U0022>\n"
"print <U0041>;<U0042>;<U0043>;"
"<U0061>;<U0062>;<U0063>;"
"<U1000>;<U1001>;<U1002>;<U1003>;<U1004>;<U1005>;"
"<U0031>;<U0032>;<U0033>;<U0020>;<U0021>;<U0022>\n"
"xdigit <U0041>;<U0042>;<U0043>;<U0061>;<U0062>;"
"<U0063>;<U0031>;<U0032>;<U0033>\n"
"toupper (<U0061>,<U0041>);(<U0062>,<U0042>);"
"(<U0063>,<U0043>);(<U1000>,<U1001>)\n"
"tolower (<U0041>,<U0061>);(<U0042>,<U0062>);"
"(<U0043>,<U0063>);(<U1001>,<U1000>)\n"
"END LC_CTYPE\n"
};
std::fprintf (fout, "%s", text);
std::fclose (fout);
// create a temporary character map file
char cmfname [256];
std::sprintf (cmfname, "%s%scharmap.src", locale_root, SLASH);
fout = std::fopen (cmfname, "w");
if (!fout) {
std::fprintf (stderr, "%s:%d: fopen(\"%s\", \"w\") failed\n",
__FILE__, __LINE__, cmfname);
return 0;
}
write_string (fout, 0);
std::fclose (fout);
locname = "test-locale";
// process the locale definition file and character map
if (0 == rw_localedef ("-w", srcfname, cmfname, locname))
locname = 0;
return locname;
}
/**************************************************************************/
template <class charT>
void test_is (charT, const char *cname)
{
charT str [MAX_STR_SIZE + 1];
BEGIN_LOCALE_LOOP (NLOOPS, locname, j) {
std::size_t size = std::size_t (j % MAX_STR_SIZE);
gen_str (str, size);
// create a mask for each character in the string using the c library
// to make sure that it is the same as the mask returned by is
std::ctype_base::mask vec [MAX_STR_SIZE + 1];
// set the global C locale to default to make sure
// the C++ library does not asume a set value
std::setlocale (LC_CTYPE, "");
ctp.is (str, str + size, vec);
// set the global C locale to the current locale for calls to the
// C library
std::setlocale (LC_CTYPE, locname);
for (std::size_t i = 0; i < size ; i++) {
int libc_result = 0;
if (libc_isalpha (str [i]))
libc_result |= ALPHA;
if (libc_isprint (str [i]))
libc_result |= PRINT;
if (libc_isspace (str [i]))
libc_result |= SPACE;
if (libc_iscntrl (str [i]))
libc_result |= CNTRL;
if (libc_isupper (str [i]))
libc_result |= UPPER;
if (libc_islower (str [i]))
libc_result |= LOWER;
if (libc_isdigit (str [i]))
libc_result |= DIGIT;
if (libc_ispunct (str [i]))
libc_result |= PUNCT;
if (libc_isxdigit (str [i]))
libc_result |= XDIGIT;
if (libc_isgraph (str [i]))
libc_result |= GRAPH;
rw_assert (vec [i] == libc_result, 0, __LINE__,
"ctype<%s>::is(%{*Ac}, ..., v) in locale(%#s) "
"at offset %zu; got %{LC} expected %{LC}\n",
cname, int (sizeof *str), str,
locname, i, vec [i], libc_result);
}
} END_LOCALE_LOOP (locname);
}
/**************************************************************************/
template <class charT>
void test_toupper_tolower (charT, const char *cname)
{
rw_info (0, 0, __LINE__, "std::ctype<%s>::tolower(%1$s)", cname);
BEGIN_LOCALE_LOOP (UCHAR_MAX, locname, i) {
const charT ch = charT (i);
// set the global C locale to locname for the C library call
std::setlocale (LC_CTYPE, locname);
const charT lch = libc_tolower (ch);
// set the global C locale to default to make sure
// the C++ library does not asume a set value
std::setlocale (LC_CTYPE, "");
// exercise tolower using ctype<>::tolower ()
rw_assert (lch == (ctp.tolower)(ch), 0, __LINE__,
"ctype<%s>::tolower(%{#lc}) == %{#lc}, got %{#lc} "
"in locale(%#s)",
cname, ch, lch, (ctp.tolower)(ch), locname);
// exercise tolower using tolower (char, locale)
rw_assert (lch == (charT)(std::tolower)(ch, loc), 0, __LINE__,
"tolower<%s>(%{#lc}, locale(%#s)) == %{#lc}, got %{#lc}",
cname, ch, locname, lch, (std::tolower)(ch, loc));
} END_LOCALE_LOOP (locname);
rw_info (0, 0, __LINE__, "std::ctype<%s>::toupper(%1$s)", cname);
BEGIN_LOCALE_LOOP (UCHAR_MAX, locname2, i) {
const charT ch = charT (i);
// set the global C locale to the locale name for the C library call
std::setlocale (LC_CTYPE, locname2);
const charT uch = libc_toupper (ch);
// set the global C locale to default to make sure
// the C++ library does not asume a set value
std::setlocale (LC_CTYPE, "");
// exercise toupper using ctype<>::toupper ()
rw_assert (uch == (ctp.toupper)(ch), 0, __LINE__,
"ctype<%s>::toupper(%{#lc}) == %{#lc}, got %{#lc} "
"in locale(%#s)",
cname, ch, uch, (ctp.toupper)(ch), locname2);
// exercise toupper using toupper (char, locale)
rw_assert (uch == (charT)(std::toupper)(ch, loc), 0, __LINE__,
"toupper<%s>(%{#lc}, locale(%#s)) == %{#lc}, got %{#lc}",
cname, ch, locname2, uch, (std::toupper)(ch, loc));
} END_LOCALE_LOOP (locname2);
}
/**************************************************************************/
template <class charT>
void test_scan (charT, const char *cname)
{
rw_info (0, 0, __LINE__, "std::ctype<%s>::scan_is(), scan_not()", cname);
charT str [MAX_STR_SIZE + 1];
BEGIN_LOCALE_LOOP (NLOOPS, locname, j) {
const std::size_t size = std::size_t (j % MAX_STR_SIZE);
// generate a random string
gen_str (str, size);
// set the global C locale to default to make sure
// the C++ library does not asume a set value
std::setlocale (LC_CTYPE, "");
// call scan_is and scan_not using each mask and compare it to
// the results found when using the c library
const charT* alpha_is = ctp.scan_is (ALPHA, str, str + size);
const charT* alpha_not = ctp.scan_not (ALPHA, str, str + size);
const charT* space_is = ctp.scan_is (SPACE, str, str + size);
const charT* space_not = ctp.scan_not (SPACE, str, str + size);
const charT* print_is = ctp.scan_is (PRINT, str, str + size);
const charT* print_not = ctp.scan_not (PRINT, str, str + size);
const charT* cntrl_is = ctp.scan_is (CNTRL, str, str + size);
const charT* cntrl_not = ctp.scan_not (CNTRL, str, str + size);
const charT* upper_is = ctp.scan_is (UPPER, str, str + size);
const charT* upper_not = ctp.scan_not (UPPER, str, str + size);
const charT* lower_is = ctp.scan_is (LOWER, str, str + size);
const charT* lower_not = ctp.scan_not (LOWER, str, str + size);
const charT* digit_is = ctp.scan_is (DIGIT, str, str + size);
const charT* digit_not = ctp.scan_not (DIGIT, str, str + size);
const charT* punct_is = ctp.scan_is (PUNCT, str, str + size);
const charT* punct_not = ctp.scan_not (PUNCT, str, str + size);
const charT* xdigit_is = ctp.scan_is (XDIGIT, str, str + size);
const charT* xdigit_not = ctp.scan_not (XDIGIT, str, str + size);
// set the global C locale to locname for the C library call
std::setlocale (LC_CTYPE, locname);
// find the first character in the string that is of the specified
// type
charT first = 0;
charT first_not = 0;
_RWSTD_SIZE_T i;
int success;
#define SCAN(what) \
for (i = 0; i < size; i++) { \
if (libc_is##what (str [i])) { \
first = (str [i]); \
break; \
} \
} \
success = what##_is != (str + size) || 0 == first \
|| *what##_is != first; \
rw_assert (success, 0, __LINE__, \
"ctype<%s>::scan_is(" #what ", %{#lc}) " \
"== %{*Ac}, got %{*Ac} in locale (%#s)", \
cname, int (sizeof *str), first, \
int (sizeof *what##_is), what##_is, \
locname); \
\
first = 0; \
for (i = 0; i < size; i++) { \
if (!libc_is##what (str[i])) { \
first_not = (str[i]); \
break; \
} \
} \
success = what##_not != (str + size) || 0 == first_not \
|| *what##_not == first_not; \
rw_assert (success, 0, __LINE__, \
"ctype<%s>::scan_not(" #what ", %{#lc}) " \
"== %{*Ac}, got %{*Ac} in locale (%#s)", \
cname, int (sizeof *str), first, \
int (sizeof *what##_not), what##_not, \
locname); \
\
first_not = 0
// test all classes of characters
SCAN (alpha);
SCAN (space);
SCAN (print);
SCAN (cntrl);
SCAN (upper);
SCAN (lower);
SCAN (digit);
SCAN (punct);
SCAN (xdigit);
} END_LOCALE_LOOP (locname);
}
/**************************************************************************/
template <class charT>
void test_narrow_widen (charT, const char *cname)
{
// 22.2.1.1.2, p11 requires that the conditions below hold for all
// facets ctc and ct whose types are ctype<char> and ctype<charT>,
// respectively:
// [1] (ctc.is (M, c) || !ct.is (M, ctc.do_widen (c))) holds for
// all narrow characters c
// i.e., narrow characters that are NOT members of a certain
// category may not belong to the same category when widened
// Note: this implies that some sort of code conversion may
// be necessary in order to implement a conforming do_widen()
// 22.2.1.1.2, p13 requires that:
// [2] (ct.do_widen (ct.do_narrow (c, dfault)) == c) holds unless
// (ct.do_narrow (c, dfault) == dfault) holds
// [3] (ct.is (M, c) || !ctc.is (M, ct.do_narrow (c, dfault))) holds
// unless (ct.do_narrow(c, dfault) == dfault) holds
//
// C99: each of the iswxxx() functions returns true for each
// wide character that corresponds (as if by a call to the
// wctob() function) to a single-byte character for which the
// corresponding character classification function from 7.4.1
// returns true, except that the iswgraph() and iswpunct()
// functions may differ with respect to wide characters other
// than L' ' that are both printing and white-space wide
// characters.
//
// [4] (ct.do_narrow (c, default) - '0') evaluates to the digit
// value of the character for all c for which ct.is(digit, c)
// returns true
rw_info (0, 0, __LINE__,
"std::ctype<%s>::narrow(%1$s), widen(char)",
cname);
rw_info (0, 0, __LINE__,
"std::ctype<%s>::narrow(const %1$s*, const %1$s*, char*), "
"widen(const char*, const char*, %1$s*)", cname);
#define STR(x) #x
// verify condition [1] above; if it fails, verify that
// the same condition also fails to hold when using the
// corresponding libc functions
#define COND1(what) \
if (!(ctc.is (what, c) || !ctp.is (what, ctp.widen (c)))) { \
rw_assert (!cond1 (what, c, locname), 0, __LINE__, \
"ctype<char>::is (" STR (what) ", %{#lc})" \
" || !ctype<%1$s>::is (" STR (what) ", " \
"ctype<%s>::widen (%{#lc}) = %{#lc})" \
" returned false in locale(%#s)", \
c, cname, c, ctp.widen (c), locname); \
} else (void)0
// verify condition [3] above; if it fails, verify that
// the same condition also fails to hold when using the
// corresponding libc functions
#define COND3(what) \
if ( ctp.narrow (ch, dfault) != dfault \
&& !(ctp.is (what, ch) || !ctc.is (what, ctp.narrow (ch, dfault)))) { \
rw_assert (!cond3 (what, ch, locname), 0, __LINE__, \
"ctype<%s>::is (" STR (what) ", %{#lc})" \
" || !ctype<char>::is (" STR (what) ", " \
"ctype<%1$s>::narrow (%{#lc}, %{#c}) = %{#lc})" \
" returned false in locale(%#s)", cname, \
ch, cname, ch, dfault, ctp.narrow (ch, '\0'), \
locname); \
} else (void)0
char c_locname [256];
std::strcpy (c_locname, std::setlocale (LC_ALL, 0));
BEGIN_LOCALE_LOOP (UCHAR_MAX, locname, i) {
#if defined (_RWSTD_OS_SUNOS) && _RWSTD_OS_MAJOR == 5 && _RWSTD_OS_MINOR <= 10
// avoid a libc SIGSEGV in mbtowc() in zh_HK and zh_TW
// locales encoded using the BIG5 codeset (see bug #603)
if ( 0 == std::strncmp ("zh_HK.BIG5", locname, 10)
|| 0 == std::strncmp ("zh_TW.BIG5", locname, 10))
continue;
#endif // SunOS < 5.10
{
// verify that the global C locale stays unchanged
const char* const curlocname = std::setlocale (LC_ALL, 0);
rw_assert (!std::strcmp (c_locname, curlocname), 0, __LINE__,
"setlocale(LC_ALL, 0) == \"%s\", got \"%s\"",
c_locname, curlocname);
}
const char c = char (i);
const charT ch = charT (i);
// verify that condition [1] holds
COND1 (ALPHA);
COND1 (CNTRL);
COND1 (DIGIT);
COND1 (GRAPH);
COND1 (LOWER);
COND1 (PRINT);
COND1 (PUNCT);
COND1 (SPACE);
COND1 (UPPER);
COND1 (XDIGIT);
// verify that condition [2] holds
char dfault = c ? '\0' : '\1';
const charT ret = ctp.widen (ctp.narrow (ch, dfault));
if (ret != charT (dfault) && ret != ch) {
rw_assert (ch != widen (ch, narrow (ch, locname), locname),
0, __LINE__,
"ctype<%s>::widen (ctype<%1$s>::narrow "
"(%{#lc}, %{#c})) == %{#c}; got %{#c} "
"in locale (%#s)",
cname, ch, dfault, ch, ret, locname);
}
// finally verify that condition [3] holds
COND3 (ALPHA);
COND3 (CNTRL);
COND3 (DIGIT);
COND3 (GRAPH);
COND3 (LOWER);
COND3 (PRINT);
COND3 (PUNCT);
COND3 (SPACE);
COND3 (UPPER);
COND3 (XDIGIT);
// now perform a relitively simple sanity check on the 3-argument
// overloads of narrow() and widen(). Make sure that the 3-argument
// overloads return the same value that the other overload produces
// Only do this the first time through the locale list.
if (i == 0) {
// arrays of all tested narrow and wide characters
charT wide_chars [UCHAR_MAX + 1];
char narrow_chars [UCHAR_MAX + 1];
charT narrow_in [UCHAR_MAX + 1];
char widen_in [UCHAR_MAX + 1];
// zero out the last element to allow printing
wide_chars [UCHAR_MAX] = charT ();
narrow_chars [UCHAR_MAX] = char ();
narrow_in [UCHAR_MAX] = charT ();
widen_in [UCHAR_MAX] = char ();
// set the `dfault' character to something unlikely
// but other than '\0'
dfault = '\377';
for (unsigned j = 0; j <= UCHAR_MAX; j++) {
wide_chars [j] = ctp.widen (char (j));
narrow_chars [j] = ctp.narrow (wide_chars [j], dfault);
narrow_in [j] = ctp.widen (char (j));
widen_in [j] = char (j);
}
charT widen_out [UCHAR_MAX + 1];
char narrow_out [UCHAR_MAX + 1];
widen_out [UCHAR_MAX] = charT ();
narrow_out [UCHAR_MAX] = char ();
// narrow source buffer into the destination
// and compare with expected values
ctp.narrow (narrow_in,
narrow_in + UCHAR_MAX + 1,
dfault,
narrow_out);
bool success =
!std::memcmp (narrow_chars, narrow_out, sizeof narrow_chars);
rw_assert (success, 0, __LINE__,
"ctype<%s>::narrow (%{*.*Ac}\", ... , %#c) "
"== %{.*Ac}, got %{.Ac} in locale (%#s)", cname,
int (sizeof *narrow_in), UCHAR_MAX, narrow_in, dfault,
UCHAR_MAX, narrow_chars, UCHAR_MAX, narrow_out,
locname);
// widen source buffer into the destination
// and compare with expected values
ctp.widen (widen_in,
widen_in + UCHAR_MAX + 1,
widen_out);
success = !std::memcmp (wide_chars, widen_out, sizeof wide_chars);
rw_assert (success, 0, __LINE__,
"ctype<%s>::widen (%{.*Ac}, ...) == "
"%{*.*Ac}, got %{*.*Ac} in locale (%#s)",
cname, UCHAR_MAX, widen_in,
int (sizeof *wide_chars), UCHAR_MAX, wide_chars,
int (sizeof *wide_chars), UCHAR_MAX, widen_out,
locname);
}
} END_LOCALE_LOOP (locname);
}
/**************************************************************************/
template <class charT>
void test_is_ch (charT, const char *cname)
{
// buffers to hold the character classification
// e.g., for `a' in the "C" locale the string will be
// "print lower alpha xdigit alnum graph "
static char is_C [80]; // C character classification
static char is_CXX [80]; // C++ classification using ctype<char> facet
static char is_CXX2 [80]; // C++ classification using convenience funcs
rw_info (0, 0, __LINE__, "std::ctype<%s>::is(mask, %1$s)", cname);
BEGIN_LOCALE_LOOP (UCHAR_MAX, locname, i) {
const charT ch = charT (i);
const UChar uch = ch;
*is_C =
*is_CXX =
*is_CXX2 = '\0';
// set the global C locale to locname for the C library calls
std::setlocale (LC_CTYPE, locname);
#define IS(what) \
libc_is##what (ch) \
? (void)std::strcat (is_C, #what" ") : (void)0;
// test all classes of characters
IS (space); IS (print); IS (cntrl); IS (upper); IS (lower);
IS (alpha); IS (digit); IS (punct); IS (xdigit); IS (alnum);
IS (graph);
// set the global C locale to default to make sure
// the C++ library does not asume a set value
std::setlocale (LC_CTYPE, "");
#undef IS
// convenience macro
#define IS(what) \
ctp.is (std::ctype_base::what, ch) \
? (void)std::strcat (is_CXX, #what" ") : (void)0; \
(std::is##what)(ch, loc) \
? (void)std::strcat (is_CXX2, #what" ") : (void)0;
// test all classes of characters
// (must be in the same order as the IS() calls above)
IS (space); IS (print); IS (cntrl); IS (upper); IS (lower);
IS (alpha); IS (digit); IS (punct); IS (xdigit); IS (alnum);
IS (graph);
// compare the two strings (should match)
rw_assert (0 == std::strcmp (is_C, is_CXX), 0, __LINE__,
"ctype<%s>::is(..., %{#lc}) in locale(%#s) "
"expected to hold for { %s}, got { %s}",
cname, uch, locname, is_C, is_CXX);
// convenience functions must produce the same results
rw_assert (0 == std::strcmp (is_C, is_CXX2), 0, __LINE__,
"is* (%{#lc}, locale (%#s)) expected to hold for "
"{ %s}, got { %s}",
uch, locname, is_C, is_CXX2);
#if !defined (_WIN32) && !defined (_WIN64)
// exercise POSIX requirements only on POSIX platforms
static const std::ctype_base::mask masks[] = {
ALPHA, std::ctype_base::mask (), CNTRL, DIGIT,
GRAPH, LOWER, PRINT, PUNCT,
SPACE, UPPER, XDIGIT
};
// see the POSIX description of LC_CTYPE in Locale for a table
// of required and allowed combinations of character classes
// a character in a given /* class */ below is also required
// to be included in the following set of classes
static const int required [sizeof masks / sizeof *masks] = {
/* alpha */ ALPHA | GRAPH | PRINT,
/* blank */ SPACE,
/* cntrl */ CNTRL,
/* digit */ DIGIT | GRAPH | PRINT | XDIGIT,
/* graph */ GRAPH | PRINT,
/* lower */ ALPHA | GRAPH | LOWER | PRINT,
/* print */ PRINT,
/* punct */ GRAPH | PRINT | PUNCT,
/* space */ SPACE,
/* upper */ ALPHA | GRAPH | PRINT | UPPER,
/* xdigit */ GRAPH | PRINT | XDIGIT
};
// a character in a given /* class */ below may also
// be included in the following set of classes
static const int allowed [sizeof masks / sizeof *masks] = {
/* alpha */ UPPER | LOWER | XDIGIT,
/* blank */ CNTRL,
/* cntrl */ SPACE,
/* digit */ 0,
/* graph */ UPPER | LOWER | ALPHA | DIGIT | SPACE | PUNCT |XDIGIT,
/* lower */ UPPER | XDIGIT,
/* print */ UPPER | LOWER | ALPHA | DIGIT | SPACE | PUNCT |XDIGIT,
/* punct */ SPACE,
/* space */ CNTRL,
/* upper */ LOWER | XDIGIT,
/* xdigit */ UPPER | LOWER | ALPHA | DIGIT
};
// a character in a given /* class */ below is explicitly
// disallowed to be included in the following set of classes
static const int disallowed [sizeof masks / sizeof *masks] = {
/* alpha */ DIGIT | SPACE | CNTRL | PUNCT,
/* blank */ UPPER | LOWER | ALPHA | DIGIT,
/* cntrl */ UPPER | LOWER | ALPHA | DIGIT | PUNCT | GRAPH | PRINT,
/* digit */ UPPER | LOWER | ALPHA | SPACE | SPACE | CNTRL,
/* graph */ CNTRL,
/* lower */ DIGIT | SPACE | CNTRL | PUNCT,
/* print */ CNTRL,
/* punct */ UPPER | LOWER | ALPHA | DIGIT | CNTRL | XDIGIT,
/* space */ UPPER | LOWER | ALPHA | DIGIT | XDIGIT,
/* upper */ DIGIT | SPACE | CNTRL | PUNCT,
/* xdigit */ SPACE | CNTRL | PUNCT
};
// obtain the mask of a single character
std::ctype_base::mask m;
ctp.is (&ch, &ch + 1, &m);
int missing = 0; // required bits missing in ch's mask
int all_allowed = 0; // all bits allowed to be set in ch's mask
int all_disallowed = 0; // all bits disallowed to be set in the mask
for (std::size_t k = 0; k != sizeof masks / sizeof *masks; ++k) {
// assumes masks [k] has a single bit set
const int bit = m & masks [k];
if (!bit)
continue;
if ((bit & required [k]) && (m & required [k]) != required [k]) {
// character in a class given by masks[k] is required
// to also belong to all classes in required[k]
missing |= ~(m & required [k]) & required [k];
}
// character in a class given by masks[k] is only allowed
// to belong to classes in (allowed[k] | required[k]) and
// is not allowed to belong to those in disallowed[k]
all_allowed |= required [k] | allowed [k];
all_disallowed |= disallowed [k];
}
// the space character automatically belongs to the print class
// but cannot belong to the punct or graph classes; other characters
// that belong to the space class can belong to the punct and graph
// classes
if (' ' == ch) {
all_disallowed |= PUNCT | GRAPH;
if (!(m & PRINT))
missing |= PRINT;
}
int cmask = -1;
if (missing) {
if (-1 == cmask)
cmask = libc_mask (-1, ch, locname);
rw_assert (m == cmask, 0, __LINE__,
"mask of %{#lc} in locale(%#s) "
"is missing bits %{LC}: %{LC}",
ch, locname, missing, m);
}
if (m & ~all_allowed) {
if (-1 == cmask)
cmask = libc_mask (-1, ch, locname);
rw_assert (m == cmask, 0, __LINE__,
"mask of %{#lc} in locale (%#s) "
"contains extra bits %{LC}: %{LC}",
ch, locname, m & ~all_allowed, m);
}
if (m & all_disallowed) {
if (-1 == cmask)
cmask = libc_mask (-1, ch, locname);
rw_assert (m == cmask, 0, __LINE__,
"mask of %{#lc} in locale (%s#) "
"contains disallowed bits { %s }: { %s }",
ch, locname, m & all_disallowed, m);
}
#endif // !WIN32 && !WIN64
} END_LOCALE_LOOP (locname);
}
/**************************************************************************/
template <class charT>
void test_libc (charT, const char *cname)
{
test_is_ch (charT (), cname);
test_toupper_tolower (charT (), cname);
test_narrow_widen (charT (), cname);
test_is (charT (), cname);
test_scan (charT (), cname);
}
/**************************************************************************/
template <class charT>
void test_libstd_scan_is (charT, const char *cname,
const std::ctype<charT> &ct,
const char* str, std::ctype_base::mask mask,
int expected_idx, int fwiden = 0)
{
// convert narrow string to a (possibly) wide representation
charT wstrbuf [256];
charT* wstr = wstrbuf;
// If instructed to use the facet widen method, do so, otherwise
// use widen helper function that in turn uses C lib mbstowcs
if (fwiden == 0)
widen (wstrbuf, str);
else
ct.widen (str, str + std::strlen (str), wstrbuf);
const int success =
&wstr [expected_idx] == ct.scan_is (mask, wstr, wstr + c_strlen (wstr));
rw_assert (success, 0, __LINE__,
"ctype<%s>::scan_is() returned an unexpected value",
cname);
}
/**************************************************************************/
template <class charT>
void test_libstd_scan_not (charT, const char *cname,
const std::ctype<charT> &ct,
const char* str, std::ctype_base::mask mask,
int expected_idx, int fwiden = 0)
{
// convert narrow string to a (possibly) wide representation
charT wstrbuf [256];
charT* wstr = wstrbuf;
// If instructed to use the facet widen method, do so, otherwise
// use widen helper function that in turn uses C lib mbstowcs
if (fwiden == 0)
widen (wstrbuf, str);
else
ct.widen (str, str + std::strlen (str), wstrbuf);
const int success =
&wstr[expected_idx] == ct.scan_not (mask, wstr, wstr + c_strlen (wstr));
rw_assert (success, 0, __LINE__,
"ctype<%s>::scan_not() returned an unexpected value",
cname);
}
/**************************************************************************/
template <class charT>
void test_libstd_toupper_tolower (charT, const char *cname,
const std::ctype<charT> &ct,
const char *locname)
{
rw_info (0, 0, __LINE__,
"std::ctype<%s>::tolower(%1$s) toupper(%1$s) in locale(%#s)",
cname, locname);
int success;
#undef TEST
#define TEST(lower, upper) \
success = ct.widen (upper) == ct.toupper (ct.widen (lower)); \
rw_assert (success, 0, __LINE__, \
"ctype<%s>::toupper(%d) == %d, got %d", cname, \
lower, upper, ct.toupper((charT)lower)); \
success = ct.widen (lower) == ct.tolower (ct.widen (upper)); \
rw_assert (success, 0, __LINE__, \
"ctype<%s>::tolower(%d) == %d, got %d", \
cname, upper, lower, ct.tolower((charT)upper))
TEST ('a', 'A');
TEST ('b', 'B');
TEST ('c', 'C');
if (sizeof(charT) > 1)
TEST ('\xa0', '\xa1');
#undef TEST
}
/**************************************************************************/
template <class charT>
void test_libstd_mask (charT, const char *cname,
const std::ctype<charT> &ct, const char *locname)
{
rw_info (0, 0, __LINE__, "std::ctype<%s>::is(mask, %1$s) in locale(%#s)",
cname, locname);
#undef TEST
#define TEST(ch, m) \
rw_assert (ct.is (m, ch), 0, __LINE__, \
"ctype<%s>::is(%d, %d) failed", cname, m, ch)
// make sure the characters have the correct masks
TEST (charT ('a'), ALPHA);
TEST (charT ('a'), LOWER);
TEST (charT ('a'), XDIGIT);
TEST (charT ('a'), GRAPH);
TEST (charT ('a'), PRINT);
TEST (charT ('a'), ALPHA | LOWER | XDIGIT | PRINT);
TEST (charT ('b'), ALPHA);
TEST (charT ('b'), LOWER);
TEST (charT ('b'), XDIGIT);
TEST (charT ('b'), GRAPH);
TEST (charT ('b'), PRINT);
TEST (charT ('b'), ALPHA | LOWER | XDIGIT | PRINT);
TEST (charT ('c'), ALPHA);
TEST (charT ('c'), LOWER);
TEST (charT ('c'), XDIGIT);
TEST (charT ('c'), GRAPH);
TEST (charT ('c'), PRINT);
TEST (charT ('c'), ALPHA | LOWER | XDIGIT | PRINT);
TEST (charT ('A'), ALPHA);
TEST (charT ('A'), UPPER);
TEST (charT ('A'), XDIGIT);
TEST (charT ('A'), GRAPH);
TEST (charT ('A'), PRINT);
TEST (charT ('A'), ALPHA | UPPER | XDIGIT | PRINT);
TEST (charT ('B'), ALPHA);
TEST (charT ('B'), UPPER);
TEST (charT ('B'), XDIGIT);
TEST (charT ('B'), GRAPH);
TEST (charT ('B'), PRINT);
TEST (charT ('B'), ALPHA | UPPER | XDIGIT | PRINT);
TEST (charT ('C'), ALPHA);
TEST (charT ('C'), UPPER);
TEST (charT ('C'), XDIGIT);
TEST (charT ('C'), GRAPH);
TEST (charT ('C'), PRINT);
TEST (charT ('C'), ALPHA | UPPER | XDIGIT | PRINT);
TEST (charT ('1'), DIGIT);
TEST (charT ('1'), XDIGIT);
TEST (charT ('1'), GRAPH);
TEST (charT ('1'), PRINT);
TEST (charT ('1'), DIGIT | XDIGIT | GRAPH | PRINT);
TEST (charT ('2'), DIGIT);
TEST (charT ('2'), XDIGIT);
TEST (charT ('2'), GRAPH);
TEST (charT ('2'), PRINT);
TEST (charT ('2'), DIGIT | XDIGIT | GRAPH | PRINT);
TEST (charT ('3'), DIGIT);
TEST (charT ('3'), XDIGIT);
TEST (charT ('3'), GRAPH);
TEST (charT ('3'), PRINT);
TEST (charT ('3'), DIGIT | XDIGIT | GRAPH | PRINT);
TEST (charT (' '), SPACE);
TEST (charT (' '), GRAPH);
TEST (charT (' '), PRINT);
TEST (charT (' '), SPACE | GRAPH | PRINT);
if (sizeof (charT) > 1) {
TEST (ct.widen ('\xa0'), ALPHA);
TEST (ct.widen ('\xa0'), LOWER);
TEST (ct.widen ('\xa0'), GRAPH);
TEST (ct.widen ('\xa0'), PRINT);
TEST (ct.widen ('\xa0'), ALPHA | LOWER | GRAPH | PRINT);
TEST (ct.widen ('\xa1'), ALPHA);
TEST (ct.widen ('\xa1'), UPPER);
TEST (ct.widen ('\xa1'), GRAPH);
TEST (ct.widen ('\xa1'), PRINT);
TEST (ct.widen ('\xa1'), ALPHA | UPPER | GRAPH | PRINT);
TEST (ct.widen ('\xa2'), DIGIT);
TEST (ct.widen ('\xa2'), GRAPH);
TEST (ct.widen ('\xa2'), PRINT);
TEST (ct.widen ('\xa0'), DIGIT | GRAPH | PRINT);
}
}
/**************************************************************************/
template <class charT>
void test_libstd (charT, const char *cname)
{
// invoke localedef to build a locale to test with
const char* const locname = create_locale ();
if (!rw_error (0 != locname, 0, __LINE__,
"failed to create a locale in %s", locale_root))
return;
const std::locale loc (locname);
const std::ctype<charT> &ct =
_STD_USE_FACET (std::ctype<charT>, loc);
ct._C_opts |= ct._C_use_libstd;
ct._C_opts &= ~ct._C_use_libc;
test_libstd_mask (charT (), cname, ct, locname);
test_libstd_toupper_tolower (charT (), cname, ct, locname);
// now check the scan functions
rw_info (0, 0, __LINE__,
"std::ctype<%s>::scan_is(const %1$s*, const %1$s, mask*) "
"in locale(%#s)", cname, locname);
test_libstd_scan_is (charT (), cname, ct, "abc1BC", DIGIT, 3);
test_libstd_scan_is (charT (), cname, ct, "abc123B ", SPACE, 7);
test_libstd_scan_is (charT (), cname, ct, "abc123", LOWER, 0);
test_libstd_scan_is (charT (), cname, ct, "abc123", UPPER, 6);
test_libstd_scan_is (charT (), cname, ct, "abc1ABC", DIGIT | UPPER, 3);
test_libstd_scan_is (charT (), cname, ct, "abcA2BC", DIGIT | UPPER, 3);
if (sizeof(charT) > 1) {
test_libstd_scan_is (charT (), cname, ct, "ABC\xa0xyz", LOWER, 3, 1);
test_libstd_scan_is (charT (), cname, ct, "abcx\xa1yz", UPPER, 4, 1);
}
rw_info (0, 0, __LINE__,
"std::ctype<%s>::scan_not(const %1$s*, const %1$s, mask*) "
"in locale(%#s)", cname, locname);
test_libstd_scan_not (charT (), cname, ct, "abc1BC", ALPHA, 3);
test_libstd_scan_not (charT (), cname, ct, "aaBBcc", LOWER, 2);
test_libstd_scan_not (charT (), cname, ct, "abc1BC", DIGIT, 0);
test_libstd_scan_not (charT (), cname, ct, "abc1BC", PRINT, 6);
if (sizeof(charT) > 1) {
test_libstd_scan_not (charT (), cname, ct, "abc\xa2xyz", ALPHA, 3, 1);
test_libstd_scan_not (charT (), cname, ct, "123\xa1yz ", DIGIT, 3, 1);
}
}
/**************************************************************************/
template <class charT>
void run_test (charT, const char *cname)
{
if (0) {
// do a compile time only test on use_facet and has_facet
_STD_HAS_FACET (std::ctype_byname<charT>, std::locale ());
_STD_USE_FACET (std::ctype_byname<charT>, std::locale ());
}
test_libstd (charT (), cname);
test_libc (charT (), cname);
}
/**************************************************************************/
static int
run_test (int, char**)
{
run_test (char (), "char");
run_test (wchar_t (), "wchar_t");
return 0;
}
/**************************************************************************/
int main (int argc, char *argv[])
{
return rw_test (argc, argv, __FILE__,
"lib.category.ctype",
0 /* no comment */,
run_test,
"",
(void*)0 /* sentinel */);
}