/**************************************************************************
 *
 * ctype.cpp - definitions of UserCtype facet members
 *
 * $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.
 *
 **************************************************************************/

// expand _TEST_EXPORT macros
#define _RWSTD_TEST_SRC

#include <rw_ctype.h>

#include <stdarg.h>   // for va_arg(), va_list
#include <string.h>   // for memset()

/**************************************************************************/

typedef unsigned char UChar;


_TEST_EXPORT int
rw_vasnprintf (char**, size_t*, const char*, va_list);


static void
_rw_throw (const char *file, int line, const char *fmt, ...)
{
    _RWSTD_UNUSED (file);
    _RWSTD_UNUSED (line);

    struct Exception: UserCtypeBase::Exception {
        char what_ [256];

        /* virtual */ const char* what () const {
            return what_;
        }
    };

    Exception ex;

    va_list va;
    va_start (va, fmt);

    char *buf = ex.what_;
    size_t bufsize = sizeof ex.what_;
    rw_vasnprintf (&buf, &bufsize, fmt, va);

    va_end (va);

    throw ex;
}

/**************************************************************************/

static const char* const
_rw_func_names[] = {
    "is(mask, char_type)",
    "is(const char_type*, const char_type*, mask*)",
    "scan_is(mask, const char_type*, const char_type*)",
    "scan_not(mask, const char_type*, const char_type*)",
    "to_upper(char_type)",
    "to_upper(char_type*, const char_type*)",
    "to_lower(char_type)",
    "to_lower(char_type*, const char_type*)",
    "widen(char)",
    "widen(const char*, const char*, char_type*)",
    "narrow(char_type, char)",
    "narrow(const char_type*, const char_type*, char, char*)"
};

/**************************************************************************/

UserCtypeBase::Exception::
~Exception ()
{
    // no-op
}

/**************************************************************************/

static const int
_rw_char_masks [256] = {

#define ALPHA     std::ctype_base::alpha
#define ALNUM     std::ctype_base::alnum
#define CNTRL     std::ctype_base::cntrl
#define DIGIT     std::ctype_base::digit
#define GRAPH     std::ctype_base::graph
#define LOWER     std::ctype_base::lower
#define PRINT     std::ctype_base::print
#define PUNCT     std::ctype_base::punct
#define SPACE     std::ctype_base::space
#define UPPER     std::ctype_base::upper
#define XDIGIT    std::ctype_base::xdigit


#undef  LETTER
#define LETTER(m) (PRINT | ALPHA | GRAPH | m)

#if 'A' == 0x41   // ASCII

    /* 0x00 NUL */ CNTRL,
    /* 0x01 SOH */ CNTRL,
    /* 0x02 STX */ CNTRL,
    /* 0x03 ETX */ CNTRL,
    /* 0x04 EOT */ CNTRL,
    /* 0x05 ENQ */ CNTRL,
    /* 0x06 ACK */ CNTRL,
    /* '\a' BEL */ CNTRL,
    /* 0x08 BS  */ CNTRL,
    /* '\t' TAB */ SPACE | CNTRL,
    /* '\n' LF  */ SPACE | CNTRL,
    /* 0x0b VT  */ SPACE | CNTRL,
    /* '\f' FF  */ SPACE | CNTRL,
    /* '\r' CR  */ SPACE | CNTRL,
    /* 0x0e SO  */ CNTRL,
    /* 0x0f SI  */ CNTRL,
    /* 0x10 DLE */ CNTRL,
    /* 0x11 DC1 */ CNTRL,
    /* 0x12 DC2 */ CNTRL,
    /* 0x13 DC3 */ CNTRL,
    /* 0x14 DC4 */ CNTRL,
    /* 0x15 NAK */ CNTRL,
    /* 0x16 SYN */ CNTRL,
    /* 0x17 ETB */ CNTRL,
    /* 0x18 CAN */ CNTRL,
    /* 0x19 EM  */ CNTRL,
    /* 0x1a SUB */ CNTRL,
    /* 0x1b ESC */ CNTRL,
    /* 0x1c FS  */ CNTRL,
    /* 0x1d GS  */ CNTRL,
    /* 0x1e RS  */ CNTRL,
    /* 0x1f US  */ CNTRL,
    /* ' '      */ SPACE | PRINT,
    /* '!'      */ PRINT | PUNCT | GRAPH,
    /* '"'      */ PRINT | PUNCT | GRAPH,
    /* '#'      */ PRINT | PUNCT | GRAPH,
    /* '$'      */ PRINT | PUNCT | GRAPH,
    /* '%'      */ PRINT | PUNCT | GRAPH,
    /* '&'      */ PRINT | PUNCT | GRAPH,
    /* '\''     */ PRINT | PUNCT | GRAPH,
    /* '('      */ PRINT | PUNCT | GRAPH,
    /* ')'      */ PRINT | PUNCT | GRAPH,
    /* '*'      */ PRINT | PUNCT | GRAPH,
    /* '+'      */ PRINT | PUNCT | GRAPH,
    /* ','      */ PRINT | PUNCT | GRAPH,
    /* '-'      */ PRINT | PUNCT | GRAPH,
    /* '.'      */ PRINT | PUNCT | GRAPH,
    /* '/'      */ PRINT | PUNCT | GRAPH,
    /* '0'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '1'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '2'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '3'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '4'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '5'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '6'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '7'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '8'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '9'      */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* ':'      */ PRINT | PUNCT | GRAPH,
    /* ';'      */ PRINT | PUNCT | GRAPH,
    /* '<'      */ PRINT | PUNCT | GRAPH,
    /* '='      */ PRINT | PUNCT | GRAPH,
    /* '>'      */ PRINT | PUNCT | GRAPH,
    /* '?'      */ PRINT | PUNCT | GRAPH,
    /* '@'      */ PRINT | PUNCT | GRAPH,

    /* 'A'      */ LETTER (UPPER | XDIGIT),
    /* 'B'      */ LETTER (UPPER | XDIGIT),
    /* 'C'      */ LETTER (UPPER | XDIGIT),
    /* 'D'      */ LETTER (UPPER | XDIGIT),
    /* 'E'      */ LETTER (UPPER | XDIGIT),
    /* 'F'      */ LETTER (UPPER | XDIGIT),
    /* 'G'      */ LETTER (UPPER),
    /* 'H'      */ LETTER (UPPER),
    /* 'I'      */ LETTER (UPPER),
    /* 'J'      */ LETTER (UPPER),
    /* 'K'      */ LETTER (UPPER),
    /* 'L'      */ LETTER (UPPER),
    /* 'M'      */ LETTER (UPPER),
    /* 'N'      */ LETTER (UPPER),
    /* 'O'      */ LETTER (UPPER),
    /* 'P'      */ LETTER (UPPER),
    /* 'Q'      */ LETTER (UPPER),
    /* 'R'      */ LETTER (UPPER),
    /* 'S'      */ LETTER (UPPER),
    /* 'T'      */ LETTER (UPPER),
    /* 'U'      */ LETTER (UPPER),
    /* 'V'      */ LETTER (UPPER),
    /* 'W'      */ LETTER (UPPER),
    /* 'X'      */ LETTER (UPPER),
    /* 'Y'      */ LETTER (UPPER),
    /* 'Z'      */ LETTER (UPPER),

    /* '['      */ PRINT | PUNCT | GRAPH,
    /* '\\'     */ PRINT | PUNCT | GRAPH,
    /* ']'      */ PRINT | PUNCT | GRAPH,
    /* '^'      */ PRINT | PUNCT | GRAPH,
    /* '_'      */ PRINT | PUNCT | GRAPH,
    /* '`'      */ PRINT | PUNCT | GRAPH,

    /* 'a'      */ LETTER (LOWER | XDIGIT),     
    /* 'b'      */ LETTER (LOWER | XDIGIT),     
    /* 'c'      */ LETTER (LOWER | XDIGIT),     
    /* 'd'      */ LETTER (LOWER | XDIGIT),     
    /* 'e'      */ LETTER (LOWER | XDIGIT),     
    /* 'f'      */ LETTER (LOWER | XDIGIT),     
    /* 'g'      */ LETTER (LOWER),              
    /* 'h'      */ LETTER (LOWER),              
    /* 'i'      */ LETTER (LOWER),              
    /* 'j'      */ LETTER (LOWER),              
    /* 'k'      */ LETTER (LOWER),              
    /* 'l'      */ LETTER (LOWER),              
    /* 'm'      */ LETTER (LOWER),              
    /* 'n'      */ LETTER (LOWER),              
    /* 'o'      */ LETTER (LOWER),              
    /* 'p'      */ LETTER (LOWER),              
    /* 'q'      */ LETTER (LOWER),              
    /* 'r'      */ LETTER (LOWER),              
    /* 's'      */ LETTER (LOWER),              
    /* 't'      */ LETTER (LOWER),              
    /* 'u'      */ LETTER (LOWER),              
    /* 'v'      */ LETTER (LOWER),              
    /* 'w'      */ LETTER (LOWER),              
    /* 'x'      */ LETTER (LOWER),              
    /* 'y'      */ LETTER (LOWER),              
    /* 'z'      */ LETTER (LOWER),

    /* '{'      */ PRINT | PUNCT | GRAPH,
    /* '|'      */ PRINT | PUNCT | GRAPH,
    /* '}'      */ PRINT | PUNCT | GRAPH,
    /* '~'      */ PRINT | PUNCT | GRAPH,
    /* 0x7f DEL */ CNTRL

#elif 'A' == 0xc1   // EBCDIC

    /* NUL */ CNTRL,
    /* SOH */ CNTRL,
    /* STX */ CNTRL,
    /* ETX */ CNTRL,
    /* PF  */ CNTRL,
    /* HT  */ CNTRL,
    /* LC  */ CNTRL,
    /* DEL */ CNTRL,
    /*     */ 0,
    /*     */ 0,
    /* SMM */ CNTRL,
    /* VT  */ SPACE | CNTRL,
    /* FF  */ SPACE | CNTRL,
    /* CR  */ SPACE | CNTRL,
    /* SO  */ CNTRL,
    /* SI  */ CNTRL,
    /* DLE */ CNTRL,
    /* DC1 */ CNTRL,
    /* DC2 */ CNTRL,
    /* TM  */ CNTRL,
    /* RES */ CNTRL,
    /* NL  */ CNTRL,
    /* BS  */ CNTRL,
    /* IL  */ CNTRL,
    /* CAN */ CNTRL,
    /* EM  */ CNTRL,
    /* CC  */ CNTRL,
    /* CU1 */ CNTRL,
    /* IFS */ CNTRL,
    /* IGS */ CNTRL,
    /* IRS */ CNTRL,
    /* IUS */ CNTRL,
    /* DS  */ CNTRL,
    /* SOS */ CNTRL,
    /* FS  */ CNTRL,
    /*     */ 0,
    /* BYP */ CNTRL,
    /* LF  */ SPACE | CNTRL,
    /* ETB */ CNTRL,
    /* ESC */ CNTRL,
    /*     */ 0,
    /*     */ 0,
    /* SM  */ CNTRL,
    /* CU2 */ CNTRL,
    /*     */ 0,
    /* ENQ */ CNTRL,
    /* ACK */ CNTRL,
    /* BEL */ CNTRL,
    /*     */ 0,
    /*     */ 0,
    /* SYN */ CNTRL,
    /*     */ 0,
    /* PN  */ CNTRL,
    /* RS  */ CNTRL,
    /* UC  */ CNTRL,
    /* EOT */ CNTRL,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* CU3 */ CNTRL,
    /* DC4 */ CNTRL,
    /* NAK */ CNTRL,
    /*     */ 0,
    /* SUB */ CNTRL,
    /* ' ' */ SPACE | PRINT,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* ct. */ PRINT | PUNCT | GRAPH,
    /* '.' */ PRINT | PUNCT | GRAPH,
    /* '<' */ PRINT | PUNCT | GRAPH,
    /* '(' */ PRINT | PUNCT | GRAPH,
    /* '+' */ PRINT | PUNCT | GRAPH,
    /* '|' */ PRINT | PUNCT | GRAPH,
    /* '&' */ PRINT | PUNCT | GRAPH,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* '!' */ PRINT | PUNCT | GRAPH,
    /* '$' */ PRINT | PUNCT | GRAPH,
    /* '*' */ PRINT | PUNCT | GRAPH,
    /* ')' */ PRINT | PUNCT | GRAPH,
    /* ';' */ PRINT | PUNCT | GRAPH,
    /* '~' */ PRINT | PUNCT | GRAPH,
    /* '-' */ PRINT | PUNCT | GRAPH,
    /* '/' */ PRINT | PUNCT | GRAPH,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* ',' */ PRINT | PUNCT | GRAPH,
    /* '%' */ PRINT | PUNCT | GRAPH,
    /* '_' */ PRINT | PUNCT | GRAPH,
    /* '>' */ PRINT | PUNCT | GRAPH,
    /* '?' */ PRINT | PUNCT | GRAPH,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* ':' */ PRINT | PUNCT | GRAPH,
    /* '#' */ PRINT | PUNCT | GRAPH,
    /* '@' */ PRINT | PUNCT | GRAPH,
    /* ''' */ PRINT | PUNCT | GRAPH,
    /* '=' */ PRINT | PUNCT | GRAPH,
    /* '"' */ PRINT | PUNCT | GRAPH,
    /*     */ 0,
    /* 'a' */ LETTER (LOWER | XDIGIT),
    /* 'b' */ LETTER (LOWER | XDIGIT),
    /* 'c' */ LETTER (LOWER | XDIGIT),
    /* 'd' */ LETTER (LOWER | XDIGIT),
    /* 'e' */ LETTER (LOWER | XDIGIT),
    /* 'f' */ LETTER (LOWER | XDIGIT),
    /* 'g' */ LETTER (LOWER),
    /* 'h' */ LETTER (LOWER),
    /* 'i' */ LETTER (LOWER),
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* 'j' */ LETTER (LOWER),
    /* 'k' */ LETTER (LOWER),
    /* 'l' */ LETTER (LOWER),
    /* 'm' */ LETTER (LOWER),
    /* 'n' */ LETTER (LOWER),
    /* 'o' */ LETTER (LOWER),
    /* 'p' */ LETTER (LOWER),
    /* 'q' */ LETTER (LOWER),
    /* 'r' */ LETTER (LOWER),
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* 's' */ LETTER (LOWER),
    /* 't' */ LETTER (LOWER),
    /* 'u' */ LETTER (LOWER),
    /* 'v' */ LETTER (LOWER),
    /* 'w' */ LETTER (LOWER),
    /* 'x' */ LETTER (LOWER),
    /* 'y' */ LETTER (LOWER),
    /* 'z' */ LETTER (LOWER),
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* '`' */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* 'A' */ LETTER (UPPER | XDIGIT),
    /* 'B' */ LETTER (UPPER | XDIGIT),
    /* 'C' */ LETTER (UPPER | XDIGIT),
    /* 'D' */ LETTER (UPPER | XDIGIT),
    /* 'E' */ LETTER (UPPER | XDIGIT),
    /* 'F' */ LETTER (UPPER | XDIGIT),
    /* 'G' */ LETTER (UPPER),
    /* 'H' */ LETTER (UPPER),
    /* 'I' */ LETTER (UPPER),
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* 'J' */ LETTER (UPPER),
    /* 'K' */ LETTER (UPPER),
    /* 'L' */ LETTER (UPPER),
    /* 'M' */ LETTER (UPPER),
    /* 'N' */ LETTER (UPPER),
    /* 'O' */ LETTER (UPPER),
    /* 'P' */ LETTER (UPPER),
    /* 'Q' */ LETTER (UPPER),
    /* 'R' */ LETTER (UPPER),
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* 'S' */ LETTER (UPPER),
    /* 'T' */ LETTER (UPPER),
    /* 'U' */ LETTER (UPPER),
    /* 'V' */ LETTER (UPPER),
    /* 'W' */ LETTER (UPPER),
    /* 'X' */ LETTER (UPPER),
    /* 'Y' */ LETTER (UPPER),
    /* 'Z' */ LETTER (UPPER),
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /* '0' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '1' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '2' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '3' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '4' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '5' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '6' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '7' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '8' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /* '9' */ PRINT | DIGIT | GRAPH | XDIGIT,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0,
    /*     */ 0

#else   // 'A' != 0x41 && 'A' != 0xc1
#  error unknown character set (neither ASCII nor EBCDIC)
#endif   // ASCII or EBCDIC

};


UserCtypeBase::
UserCtypeBase (const char *cname)
    : cname_ (cname), masks_ (_rw_char_masks), chars_ (0),
      upper_ (0), lower_ (0), narrow_ (0), wide_ (0),
      n_all_calls_ (0),
      throw_char_ (-1), throw_on_invalid_ (false)
{
    memset (n_calls_, 0, sizeof n_calls_);
    memset (n_throws_, 0, sizeof n_throws_);
    memset (throw_at_calls_, 0, sizeof throw_at_calls_);
}

/**************************************************************************/

// records a call to the object's member function fun and
// throws an exception if the number of calls to the member
// function reaches the limit specified by throw_at_calls_
static void
_rw_funcall (const UserCtypeBase    &ctp,
             UserCtypeBase::MemFunc  mf)
{
    UserCtypeBase* const pctp = _RWSTD_CONST_CAST (UserCtypeBase*, &ctp);

    // increment the number of calls regardless of success
    ++pctp->n_all_calls_;
    ++pctp->n_calls_ [mf];

    // check the number of calls and throw an exception
    // if the specified limit has been reached
    if (ctp.n_calls_ [mf] == ctp.throw_at_calls_ [mf]) {
        // increment the exception counter for this function
        ++pctp->n_throws_ [mf];

        _rw_throw (__FILE__, __LINE__,
                   "UserCtype<%s>::%s: reached call limit of %zu",
                   ctp.cname_, _rw_func_names [mf], ctp.throw_at_calls_);

        RW_ASSERT (!"logic error: should not reach");
    }
}

/**************************************************************************/

static void
_rw_check_char (const UserCtypeBase   &ctp,
                UserCtypeBase::MemFunc mf,
                size_t                 uch)
{
    if (0 <= ctp.throw_char_ && size_t (ctp.throw_char_) == uch) {

        UserCtypeBase* const pctp = _RWSTD_CONST_CAST (UserCtypeBase*, &ctp);
        ++pctp->n_throws_ [mf];

        _rw_throw (__FILE__, __LINE__,
                   "UserCtype<%s>::%s: invalid character: %{#lc}",
                   ctp.cname_, _rw_func_names [mf], int (uch));

        RW_ASSERT (!"logic error: should not reach");
    }
}

/**************************************************************************/

static UserCtype<char>::mask*
_rw_make_mask_vec (const int *chars, const int *masks)
{
    if (0 == chars) {
        RW_ASSERT (0 == masks);
        return 0;
    }

    RW_ASSERT (0 != masks);

    typedef UserCtype<char>::mask Mask;

    const size_t size = UserCtype<char>::table_size;
    Mask* const  vec  = new Mask [size];

    memset (vec, 0, size * sizeof *vec);

    for (size_t i = 0; 0 <= chars [i]; ++i)
        vec [UChar (chars [i])] = Mask (masks [i]);

    return vec;
}


UserCtype<char>::
UserCtype (size_t refs /* = 0 */)
    : Base (0, false, refs),
      UserCtypeBase ("char")
{
    chars_ = 0;
    masks_ = _rw_char_masks;
}


UserCtype<char>::
UserCtype (const int *chars, const int *masks, size_t refs /* = 0 */)
    : Base (_rw_make_mask_vec (chars, masks), true, refs),
      UserCtypeBase ("char")
{
    chars_ = chars;
    masks_ = masks;
}


UserCtype<char>::char_type
UserCtype<char>::
do_toupper (char_type ch) const
{
    _rw_funcall (*this, mf_toupper);

    const UChar uch = UChar (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (0 == upper_) {
        if (masks_ [uch] & LOWER) {
#if 'A' == 0x41   // ASCII
            return char_type (uch - 32);
#else   // EBCDIC
            return char_type (uch + 64);
#endif
        }
    }
    else {
        // FIXME: implement this
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return ch;
}


const UserCtype<char>::char_type*
UserCtype<char>::
do_toupper (char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_toupper_range);

    if (0 == upper_) {
        for ( ; lo < hi; ++lo) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            if (masks_ [uch] & LOWER) {
#if 'A' == 0x41   // ASCII
                *lo = char_type (uch - 32);
#else   // EBCDIC
                *lo = char_type (uch + 64);
#endif
            }
        }
    }
    else {
        // FIXME: implement this 
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return lo;
}


UserCtype<char>::char_type
UserCtype<char>::
do_tolower (char_type ch) const
{
    _rw_funcall (*this, mf_tolower);

    _rw_check_char (*this, mf_toupper, UChar (ch));

    if (0 == chars_) {
        if (masks_ [UChar (ch)] & UPPER) {
#if 'A' == 0x41   // ASCII
            return char_type (UChar (ch + 32));
#else   // EBCDIC
            return char_type (UChar (ch - 64));
#endif
        }
    }
    else {
        // FIXME: implement this
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return ch;
}


const UserCtype<char>::char_type*
UserCtype<char>::
do_tolower (char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_tolower_range);

    if (0 == chars_) {
        for ( ; lo < hi; ++lo) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            if (masks_ [uch] & UPPER) {
#if 'A' == 0x41   // ASCII
                *lo = char_type (uch - 32);
#else   // EBCDIC
                *lo = char_type (uch + 64);
#endif
            }
        }
    }
    else {
        // FIXME: implement this 
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return lo;
}


UserCtype<char>::char_type
UserCtype<char>::
do_widen (char ch) const
{
    _rw_funcall (*this, mf_widen);

    const UChar uch = UChar (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (narrow_ && wide_) {
        for (size_t i = 0; 0 <= narrow_ [i]; ++i) {
            if (uch == UChar (narrow_ [i])) {
                if (0 <= wide_ [i])
                    return UChar (wide_ [i]);

                if (throw_on_invalid_)
                    throw;

                break;
            }
        }
    }

    return ch;
}


const char*
UserCtype<char>::
do_widen (const char *lo, const char *hi, char_type *dst) const
{
    _rw_funcall (*this, mf_widen_range);

    if (narrow_ && wide_) {
        for ( ; lo < hi; ++lo, ++dst) {

            const UChar uch = *lo;

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= narrow_ [i]; ++i) {
                if (uch == UChar (narrow_ [i])) {
                    if (0 <= wide_ [i])
                        *dst = UChar (wide_ [i]);

                    if (throw_on_invalid_)
                        throw;

                    break;
                }
            }
        }
    }
    else if (throw_char_ < 0 || int (_RWSTD_UCHAR_MAX) < throw_char_) {
        const size_t nelems = size_t (hi - lo);
        memcpy (dst, lo, nelems);
        lo += nelems;
    }
    else {
        for (; lo < hi; ++lo, ++dst) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            *dst = char_type (uch);
        }
    }

    return lo;
}


char UserCtype<char>::
do_narrow (char_type ch, char dfault) const
{
    _rw_funcall (*this, mf_narrow);

    const UChar uch = UChar (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (narrow_ && wide_) {
        for (size_t i = 0; 0 <= wide_ [i]; ++i) {
            if (uch == UChar (wide_ [i])) {
                if (0 <= wide_ [i])
                    return UChar (wide_ [i]);

                if (throw_on_invalid_)
                    throw;

                break;
            }
        }

        return dfault;
    }

    return ch;
}


const UserCtype<char>::char_type*
UserCtype<char>::
do_narrow (const char_type *lo, const char_type *hi, char dflt, char *dst) const
{
    _rw_funcall (*this, mf_narrow_range);

    if (narrow_ && wide_) {
        for ( ; lo < hi; ++lo, ++dst) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= wide_ [i]; ++i) {
                if (uch == UChar (wide_ [i])) {
                    *dst = 0 <= narrow_ [i] ? UChar (wide_ [i]) : UChar (dflt);
                    break;
                }
            }
        }
    }
    else if (throw_char_ < 0 || int (_RWSTD_UCHAR_MAX) < throw_char_) {
        const size_t nelems = size_t (hi - lo);
        memcpy (dst, lo, nelems);
        lo += nelems;
    }
    else {
        for (; lo < hi; ++lo, ++dst) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            *dst = char_type (uch);
        }
    }

    return lo;
}

/**************************************************************************/

#ifndef _RWSTD_NO_WCHAR_T

UserCtype<wchar_t>::
UserCtype (size_t refs /* = 0 */)
    : Base (refs), UserCtypeBase ("wchar_t")
{
    chars_ = 0;
    masks_ = _rw_char_masks;
}


UserCtype<wchar_t>::
UserCtype (const int *chars, const int *masks, size_t refs)
    : Base (refs), UserCtypeBase ("wchar_t")
{
    if (0 == masks) {
        // when masks is null so must chars
        RW_ASSERT (0 == chars);
        masks = _rw_char_masks;
    }

    chars_ = chars;
    masks_ = masks;
}


bool UserCtype<wchar_t>::
do_is (mask m, char_type ch) const
{
    _rw_funcall (*this, mf_is);

    const size_t uch = size_t (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (masks_ == _rw_char_masks) {
        return uch < 256 ? 0 != (masks_ [uch] & m) : false;
    }

    for (size_t i = 0; 0 <= chars_ [i]; ++i)
        if (chars_ [i] == int (ch))
            return 0 != (masks_ [i] & m);

    return false;
}


const UserCtype<wchar_t>::char_type*
UserCtype<wchar_t>::
do_is (const char_type *lo, const char_type *hi, mask *vec) const
{
    _rw_funcall (*this, mf_is_range);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo, ++vec) {

            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            *vec = mask (uch < 256 ? masks_ [uch] : 0);
        }
    }
    else {
        for ( ; lo < hi; ++lo, ++vec) {

            _rw_check_char (*this, mf_toupper, size_t (*lo));

            bool found = false;

            for (size_t i = 0; 0 <= chars_ [i]; ++i) {
                if (chars_ [i] == int (*lo)) {
                    *vec  = mask (masks_ [i]);
                    found = true;
                    break;
                }
            }

            if (!found && throw_on_invalid_)
                throw;
        }
    }

    return lo;
}


const UserCtype<wchar_t>::char_type*
UserCtype<wchar_t>::
do_scan_is (mask m, const char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_scan_is);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo) {
            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            if (uch < 256 && masks_ [uch] & m)
                break;
        }
    }
    else {
        for ( ; lo < hi; ++lo) {

            _rw_check_char (*this, mf_toupper, size_t (*lo));

            for (size_t i = 0; 0 <= chars_ [i]; ++i) {
                if (chars_ [i] == int (*lo) && masks_ [i] & m)
                    return lo;
            }
        }
    }

    return lo;
}


const UserCtype<wchar_t>::char_type*
UserCtype<wchar_t>::
do_scan_not (mask m, const char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_scan_not);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo) {

            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            if (uch < 256 && 0 == (masks_ [uch] & m))
                break;
        }
    }
    else {
        for ( ; lo < hi; ++lo) {

            _rw_check_char (*this, mf_toupper, size_t (*lo));

            for (size_t i = 0; 0 <= chars_ [i]; ++i) {
                if (chars_ [i] == int (*lo) && 0 == (masks_ [i] & m))
                    return lo;
            }
        }
    }

    return lo;
}


UserCtype<wchar_t>::char_type
UserCtype<wchar_t>::
do_toupper (char_type ch) const
{
    _rw_funcall (*this, mf_toupper);

    const size_t uch = size_t (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (0 == upper_) {

        if (uch < 256 && masks_ [uch] & LOWER) {
#if 'A' == 0x41   // ASCII
            return char_type (uch - 32);
#else   // EBCDIC
            return char_type (uch + 64);
#endif
        }
    }
    else {
        // FIXME: implement this
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return ch;
}


const UserCtype<wchar_t>::char_type*
UserCtype<wchar_t>::
do_toupper (char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_toupper_range);

    if (0 == upper_) {
        for ( ; lo < hi; ++lo) {
            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            if (uch < 256 && masks_ [uch] & LOWER) {
#if 'A' == 0x41   // ASCII
                *lo = char_type (uch - 32);
#else   // EBCDIC
                *lo = char_type (uch + 64);
#endif
            }
        }
    }
    else {
        // FIXME: implement this 
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return lo;
}


UserCtype<wchar_t>::char_type
UserCtype<wchar_t>::
do_tolower (char_type ch) const
{
    _rw_funcall (*this, mf_tolower);

    const size_t uch = size_t (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (masks_ == _rw_char_masks) {
        if (uch < 256 && masks_ [uch] & UPPER) {
#if 'A' == 0x41   // ASCII
            return char_type (uch + 32);
#else   // EBCDIC
            return char_type (uch - 64);
#endif
        }
    }
    else {
        // FIXME: implement this
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return ch;
}


const UserCtype<wchar_t>::char_type*
UserCtype<wchar_t>::
do_tolower (char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_tolower_range);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo) {

            const size_t uch = size_t (*lo);

            if (uch < 256 && masks_ [uch] & UPPER) {
#if 'A' == 0x41   // ASCII
                *lo = char_type (uch - 32);
#else   // EBCDIC
                *lo = char_type (uch + 64);
#endif
            }
        }
    }
    else {
        // FIXME: implement this 
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return lo;
}


UserCtype<wchar_t>::char_type
UserCtype<wchar_t>::
do_widen (char ch) const
{
    _rw_funcall (*this, mf_widen);

    const UChar uch = UChar (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (narrow_ && wide_) {
        for (size_t i = 0; 0 <= narrow_ [i]; ++i) {
            if (int (uch) == narrow_ [i]) {
                if (0 <= wide_ [i])
                    return char_type (wide_ [i]);

                if (throw_on_invalid_)
                    throw;

                break;
            }
        }
    }

    return ch;
}


const char*
UserCtype<wchar_t>::
do_widen (const char *lo, const char *hi, char_type *dst) const
{
    _rw_funcall (*this, mf_widen_range);

    if (narrow_ && wide_) {
        for ( ; lo < hi; ++lo, ++dst) {

            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= narrow_ [i]; ++i) {
                if (int (uch) == narrow_ [i]) {
                    if (0 <= wide_ [i])
                        *dst = char_type (wide_ [i]);

                    if (throw_on_invalid_)
                        throw;

                    break;
                }
            }
        }
    }
    else {
        for (; lo < hi; ++lo, ++dst) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            *dst = char_type (uch);
        }
    }

    return lo;
}


char UserCtype<wchar_t>::
do_narrow (char_type ch, char dfault) const
{
    _rw_funcall (*this, mf_narrow);

    const size_t uch = size_t (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (narrow_ && wide_) {
        for (size_t i = 0; 0 <= wide_ [i]; ++i) {
            if (int (uch) == wide_ [i]) {
                if (0 <= wide_ [i])
                    return char (wide_ [i]);

                if (throw_on_invalid_)
                    throw;

                break;
            }
        }

        return dfault;
    }

    return char (uch);
}


const UserCtype<wchar_t>::char_type*
UserCtype<wchar_t>::
do_narrow (const char_type *lo, const char_type *hi, char dflt, char *dst) const
{
    _rw_funcall (*this, mf_narrow_range);

    if (narrow_ && wide_) {
        for ( ; lo < hi; ++lo, ++dst) {

            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= wide_ [i]; ++i) {
                if (int (uch) == wide_ [i]) {
                    *dst = 0 <= narrow_ [i] ? char (wide_ [i]) : dflt;
                    break;
                }
            }
        }
    }
    else {
        for (; lo < hi; ++lo, ++dst) {

            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            *dst = char (uch);
        }
    }

    return lo;
}

#endif   // _RWSTD_NO_WCHAR_T

/**************************************************************************/

_RWSTD_NAMESPACE (std) {


std::locale::id
ctype<UserChar>::
id;


ctype<UserChar>::
ctype (size_t refs /* = 0 */)
    : Base (refs), UserCtypeBase ("UserChar")
{
    masks_ = _rw_char_masks;
    chars_ = 0;
}


ctype<UserChar>::
ctype (const int *chars, const int *masks, size_t refs /* = 0 */)
    : Base (refs), UserCtypeBase ("UserChar")
{
    if (0 == masks) {
        // when masks is null so must chars
        RW_ASSERT (0 == chars);
        masks = _rw_char_masks;
    }

    chars_ = chars;
    masks_ = masks;
}


bool ctype<UserChar>::
do_is (mask m, char_type ch) const
{
    _rw_funcall (*this, mf_is);

    const size_t uch = size_t (ch.c);

    _rw_check_char (*this, mf_toupper, uch);

    if (masks_ == _rw_char_masks) {
        return uch < 256 ? 0 != (masks_ [uch] & m) : false;
    }

    for (size_t i = 0; 0 <= chars_ [i]; ++i)
        if (chars_ [i] == int (uch))
            return 0 != (masks_ [i] & m);

    return false;
}


const ctype<UserChar>::char_type*
ctype<UserChar>::
do_is (const char_type *lo, const char_type *hi, mask *vec) const
{
    _rw_funcall (*this, mf_is_range);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo, ++vec) {

            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            *vec = mask (uch < 256 ? masks_ [uch] : 0);
        }
    }
    else {
        for ( ; lo < hi; ++lo, ++vec) {

            _rw_check_char (*this, mf_toupper, size_t (lo->c));

            bool found = false;

            for (size_t i = 0; 0 <= chars_ [i]; ++i) {
                if (chars_ [i] == int (UChar (lo->c))) {
                    *vec  = mask (masks_ [i]);
                    found = true;
                    break;
                }
            }

            if (!found && throw_on_invalid_)
                throw;
        }
    }

    return lo;
}


const ctype<UserChar>::char_type*
ctype<UserChar>::
do_scan_is (mask m, const char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_scan_is);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo) {
            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            if (uch < 256 && masks_ [uch] & m)
                break;
        }
    }
    else {
        for ( ; lo < hi; ++lo) {

            _rw_check_char (*this, mf_toupper, size_t (lo->c));

            for (size_t i = 0; 0 <= chars_ [i]; ++i) {
                if (chars_ [i] == int (UChar (lo->c)) && masks_ [i] & m)
                    return lo;
            }
        }
    }

    return lo;
}


const ctype<UserChar>::char_type*
ctype<UserChar>::
do_scan_not (mask m, const char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_scan_not);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo) {

            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            if (uch < 256 && 0 == (masks_ [uch] & m))
                break;
        }
    }
    else {
        for ( ; lo < hi; ++lo) {

            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= chars_ [i]; ++i) {
                if (chars_ [i] == int (uch) && 0 == (masks_ [i] & m))
                    return lo;
            }
        }
    }

    return lo;
}


ctype<UserChar>::char_type
ctype<UserChar>::
do_toupper (char_type ch) const
{
    _rw_funcall (*this, mf_toupper);

    const size_t uch = size_t (ch.c);

    _rw_check_char (*this, mf_toupper, uch);

    if (0 == upper_) {

        if (uch < 256 && masks_ [uch] & LOWER) {
#if 'A' == 0x41   // ASCII
            ch.c = UChar (uch - 32);
#else   // EBCDIC
            ch.c = UChar (uch + 64);
#endif
        }
    }
    else {
        // FIXME: implement this
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return ch;
}


const ctype<UserChar>::char_type*
ctype<UserChar>::
do_toupper (char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_toupper_range);

    if (0 == upper_) {
        for ( ; lo < hi; ++lo) {
            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            if (uch < 256 && masks_ [uch] & LOWER) {
#if 'A' == 0x41   // ASCII
                lo->c = UChar (uch - 32);
#else   // EBCDIC
                lo->c = UChar (uch + 64);
#endif
            }
        }
    }
    else {
        // FIXME: implement this 
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return lo;
}


ctype<UserChar>::char_type
ctype<UserChar>::
do_tolower (char_type ch) const
{
    _rw_funcall (*this, mf_tolower);

    const size_t uch = size_t (ch.c);

    _rw_check_char (*this, mf_toupper, uch);

    if (masks_ == _rw_char_masks) {
        if (uch < 256 && masks_ [uch] & UPPER) {
#if 'A' == 0x41   // ASCII
            ch.c = UChar (uch + 32);
#else   // EBCDIC
            ch.c = UChar (uch - 64);
#endif
        }
    }
    else {
        // FIXME: implement this
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return ch;
}


const ctype<UserChar>::char_type*
ctype<UserChar>::
do_tolower (char_type *lo, const char_type *hi) const
{
    _rw_funcall (*this, mf_tolower_range);

    if (masks_ == _rw_char_masks) {
        for ( ; lo < hi; ++lo) {

            const size_t uch = size_t (lo->c);

            if (uch < 256 && masks_ [uch] & UPPER) {
#if 'A' == 0x41   // ASCII
                lo->c = UChar (uch - 32);
#else   // EBCDIC
                lo->c = UChar (uch + 64);
#endif
            }
        }
    }
    else {
        // FIXME: implement this 
        RW_ASSERT (!"do_toupper() not implemented");
    }

    return lo;
}


ctype<UserChar>::char_type
ctype<UserChar>::
do_widen (char ch) const
{
    _rw_funcall (*this, mf_widen);

    const UChar uch = UChar (ch);

    _rw_check_char (*this, mf_toupper, uch);

    if (narrow_ && wide_) {
        for (size_t i = 0; 0 <= narrow_ [i]; ++i) {
            if (int (uch) == narrow_ [i]) {
                if (0 <= wide_ [i])
                    return make_char (char (wide_ [i]), (UserChar*)0);

                if (throw_on_invalid_)
                    throw;

                break;
            }
        }
    }

    return make_char (ch, (UserChar*)0);
}


const char*
ctype<UserChar>::
do_widen (const char *lo, const char *hi, char_type *dst) const
{
    _rw_funcall (*this, mf_widen_range);

    if (narrow_ && wide_) {
        for ( ; lo < hi; ++lo, ++dst) {

            const size_t uch = size_t (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= narrow_ [i]; ++i) {
                if (int (uch) == narrow_ [i]) {
                    if (0 <= wide_ [i])
                        *dst = make_char (char (wide_ [i]), (UserChar*)0);

                    if (throw_on_invalid_)
                        throw;

                    break;
                }
            }
        }
    }
    else {
        for (; lo < hi; ++lo, ++dst) {

            const UChar uch = UChar (*lo);

            _rw_check_char (*this, mf_toupper, uch);

            *dst = make_char (*lo, (UserChar*)0);
        }
    }

    return lo;
}


char ctype<UserChar>::
do_narrow (char_type ch, char dfault) const
{
    _rw_funcall (*this, mf_narrow);

    const size_t uch = size_t (ch.c);

    _rw_check_char (*this, mf_toupper, uch);

    if (narrow_ && wide_) {
        for (size_t i = 0; 0 <= wide_ [i]; ++i) {
            if (int (uch) == wide_ [i]) {
                if (0 <= wide_ [i])
                    return char (wide_ [i]);

                if (throw_on_invalid_)
                    throw;

                break;
            }
        }

        return dfault;
    }

    return char (uch);
}


const ctype<UserChar>::char_type*
ctype<UserChar>::
do_narrow (const char_type *lo, const char_type *hi, char dflt, char *dst) const
{
    _rw_funcall (*this, mf_narrow_range);

    if (narrow_ && wide_) {
        for ( ; lo < hi; ++lo, ++dst) {

            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            for (size_t i = 0; 0 <= wide_ [i]; ++i) {
                if (int (uch) == wide_ [i]) {
                    *dst = 0 <= narrow_ [i] ? char (wide_ [i]) : dflt;
                    break;
                }
            }
        }
    }
    else {
        for (; lo < hi; ++lo, ++dst) {

            const size_t uch = size_t (lo->c);

            _rw_check_char (*this, mf_toupper, uch);

            *dst = char (uch);
        }
    }

    return lo;
}

}   // namespace std

/**************************************************************************/

UserCtype<UserChar>::
UserCtype (size_t refs /* = 0 */)
    : Base (refs)
{
    chars_ = 0;
    masks_ = _rw_char_masks;
}


UserCtype<UserChar>::
UserCtype (const int *chars, const int *masks, size_t refs)
    : Base (refs)
{
    if (0 == masks) {
        // when masks is null so must chars
        RW_ASSERT (0 == chars);
        masks = _rw_char_masks;
    }

    chars_ = chars;
    masks_ = masks;
}
