/*
Copyright (c) 1990 Regents of the University of California.
All rights reserved.
 */
#include <stdlib.h>
#include <locale.h>
#include "mbctype.h"
#include <wchar.h>
#include <string.h>
#include <stdint.h>
#include <endian.h>
#include "local.h"
#include "../ctype/local.h"

int
__ascii_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    wchar_t        dummy;
    unsigned char *t = (unsigned char *)s;
    unsigned char  c;

    (void)state;
    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL)
        return 0;

    if (n == 0)
        return -2;

    c = *t;

    if (c >= 0x80)
        return -1;

    *pwc = (wchar_t)c;

    if (*t == '\0')
        return 0;

    return 1;
}

#ifdef __MB_CAPABLE

/* we override the mbstate_t __count field for more complex encodings and use it store a state value
 */
#define __state __count

int
__utf8_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    wchar_t        dummy;
    unsigned char *t = (unsigned char *)s;
    int            ch;
    int            i = 0;

    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL)
        return 0;

    if (n == 0)
        return -2;

    if (state->__count == 0)
        ch = t[i++];
    else
        ch = state->__value.__wchb[0];

    if (ch == '\0') {
        *pwc = 0;
        state->__count = 0;
        return 0; /* s points to the null character */
    }

    if (ch <= 0x7f) {
        /* single-byte sequence */
        state->__count = 0;
        *pwc = ch;
        return 1;
    }
    if (ch >= 0xc0 && ch <= 0xdf) {
        if (ch == 0xc0)
            return -1;
        /* two-byte sequence */
        state->__value.__wchb[0] = ch;
        if (state->__count == 0)
            state->__count = 1;
        else if (n < (size_t)-1)
            ++n;
        if (n < 2)
            return -2;
        ch = t[i++];
        if (ch < 0x80 || ch > 0xbf)
            return -1;
        if (state->__value.__wchb[0] < 0xc2) {
            /* overlong UTF-8 sequence */
            return -1;
        }
        state->__count = 0;
        *pwc = ((wchar_t)(state->__value.__wchb[0] & 0x1f) << 6) | (wchar_t)(ch & 0x3f);
        return i;
    }
    if (ch >= 0xe0 && ch <= 0xef) {
        /* three-byte sequence */
        wchar_t tmp;
        state->__value.__wchb[0] = ch;
        if (state->__count == 0)
            state->__count = 1;
        else if (n < (size_t)-1)
            ++n;
        if (n < 2)
            return -2;
        ch = (state->__count == 1) ? t[i++] : state->__value.__wchb[1];
        if (state->__value.__wchb[0] == 0xe0 && ch < 0xa0) {
            /* overlong UTF-8 sequence */
            return -1;
        }
        if (ch < 0x80 || ch > 0xbf) {
            return -1;
        }
        state->__value.__wchb[1] = ch;
        if (state->__count == 1)
            state->__count = 2;
        else if (n < (size_t)-1)
            ++n;
        if (n < 3)
            return -2;
        ch = t[i++];
        if (ch < 0x80 || ch > 0xbf) {
            return -1;
        }
        state->__count = 0;
        tmp = ((wchar_t)(state->__value.__wchb[0] & 0x0f) << 12)
            | ((wchar_t)(state->__value.__wchb[1] & 0x3f) << 6) | (wchar_t)(ch & 0x3f);
        /* Check for surrogates */
        if (0xd800 <= (uwchar_t)tmp && (uwchar_t)tmp <= 0xdfff) {
            return -1;
        }
        *pwc = tmp;
        return i;
    }
    if (ch >= 0xf0 && ch <= 0xf4) {
        /* four-byte sequence */
        uint32_t tmp;
        state->__value.__wchb[0] = ch;
        if (state->__count == 0)
            state->__count = 1;
        else if (n < (size_t)-1)
            ++n;
        if (n < 2)
            return -2;
        ch = (state->__count == 1) ? t[i++] : state->__value.__wchb[1];
        if ((state->__value.__wchb[0] == 0xf0 && ch < 0x90)
            || (state->__value.__wchb[0] == 0xf4 && ch >= 0x90)) {
            /* overlong UTF-8 sequence or result is > 0x10ffff */
            return -1;
        }
        if (ch < 0x80 || ch > 0xbf) {
            return -1;
        }
        state->__value.__wchb[1] = ch;
        if (state->__count == 1)
            state->__count = 2;
        else if (n < (size_t)-1)
            ++n;
        if (n < 3)
            return -2;
        ch = (state->__count == 2) ? t[i++] : state->__value.__wchb[2];
        if (ch < 0x80 || ch > 0xbf) {
            return -1;
        }
        state->__value.__wchb[2] = ch;
        if (state->__count == 2)
            state->__count = 3;
        else if (n < (size_t)-1)
            ++n;
#if __SIZEOF_WCHAR_T__ == 2
        if (state->__count == 3) {
            /* On systems which have wchar_t being UTF-16 values, the value
               doesn't fit into a single wchar_t in this case.  So what we
               do here is to store the state with a special value of __count
               and return the first half of a surrogate pair.  The first
               three bytes of a UTF-8 sequence are enough to generate the
               first half of a UTF-16 surrogate pair.  As return value we
               choose to return the number of bytes actually read up to
               here.
               The second half of the surrogate pair is returned in case we
               recognize the special __count value of four, and the next
               byte is actually a valid value.  See below. */
            tmp = (uint32_t)((state->__value.__wchb[0] & (uint32_t)0x07) << 18)
                | (uint32_t)((state->__value.__wchb[1] & (uint32_t)0x3f) << 12)
                | (uint32_t)((state->__value.__wchb[2] & (uint32_t)0x3f) << 6);
            state->__count = 4;
            *pwc = 0xd800 | ((tmp - 0x10000) >> 10);
            return i;
        }
#endif
        if (n < 4)
            return -2;
        ch = t[i++];
        if (ch < 0x80 || ch > 0xbf) {
            return -1;
        }
        tmp = (((uint32_t)state->__value.__wchb[0] & 0x07) << 18)
            | (((uint32_t)state->__value.__wchb[1] & 0x3f) << 12)
            | (((uint32_t)state->__value.__wchb[2] & 0x3f) << 6) | ((uint32_t)ch & 0x3f);
#if __SIZEOF_WCHAR_T__ == 2
        if (state->__count == 4)
            /* Create the second half of the surrogate pair for systems with
               wchar_t == UTF-16 . */
            *pwc = 0xdc00 | (tmp & 0x3ff);
        else
#endif
            *pwc = tmp;
        state->__count = 0;
        return i;
    }

    return -1;
}

#ifdef __MB_EXTENDED_CHARSETS_UCS

#if _BYTE_ORDER == _LITTLE_ENDIAN
#define __ucs2le_mbtowc __ucs2_mbtowc
#define __ucs2be_mbtowc __ucs2swap_mbtowc
#define __ucs4le_mbtowc __ucs4_mbtowc
#define __ucs4be_mbtowc __ucs4swap_mbtowc
#else
#define __ucs2le_mbtowc __ucs2swap_mbtowc
#define __ucs2be_mbtowc __ucs2_mbtowc
#define __ucs4le_mbtowc __ucs4swap_mbtowc
#define __ucs4be_mbtowc __ucs4_mbtowc
#endif

static int
__ucs2_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    uint16_t uchar;

    (void)state;
    if (n < 2)
        return -1;
    memcpy(&uchar, s, 2);
    *pwc = uchar;
    return 2;
}

static int
__ucs2swap_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    uint16_t uchar;

    (void)state;
    if (n < 2)
        return -1;
    memcpy(&uchar, s, 2);
    *pwc = __bswap16(uchar);
    return 2;
}

static int
__ucs4_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    uint32_t uchar;

    (void)state;
    if (n < 4)
        return -1;
    memcpy(&uchar, s, 4);
    *pwc = uchar;
    return 4;
}

static int
__ucs4swap_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    uint32_t uchar;

    (void)state;
    if (n < 4)
        return -1;
    memcpy(&uchar, s, 4);
    *pwc = __bswap32(uchar);
    return 4;
}

#endif /* __MB_EXTENDED_CHARSETS_UCS */

#ifdef __MB_EXTENDED_CHARSETS_ISO
static int
___iso_mbtowc(wchar_t *pwc, const char *s, size_t n, enum locale_id id, mbstate_t *state)
{
    wchar_t        dummy;
    unsigned char *t = (unsigned char *)s;

    (void)state;
    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL)
        return 0;

    if (n == 0)
        return -2;

    if (*t >= 0xa0) {
        if (id > locale_ISO_8859_1) {
            *pwc = __iso_8859_conv[id - locale_ISO_8859_2][*t - 0xa0];
            if (*pwc == 0) /* Invalid character */
            {
                return -1;
            }
            return 1;
        }
    }

    *pwc = (wchar_t)*t;

    if (*t == '\0')
        return 0;

    return 1;
}

static int
__iso_8859_1_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_1, state);
}

static int
__iso_8859_2_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_2, state);
}

static int
__iso_8859_3_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_3, state);
}

static int
__iso_8859_4_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_4, state);
}

static int
__iso_8859_5_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_5, state);
}

static int
__iso_8859_6_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_6, state);
}

static int
__iso_8859_7_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_7, state);
}

static int
__iso_8859_8_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_8, state);
}

static int
__iso_8859_9_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_9, state);
}

static int
__iso_8859_10_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_10, state);
}

static int
__iso_8859_11_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_11, state);
}

static int
__iso_8859_13_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_13, state);
}

static int
__iso_8859_14_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_14, state);
}

static int
__iso_8859_15_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_15, state);
}

static int
__iso_8859_16_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___iso_mbtowc(pwc, s, n, locale_ISO_8859_16, state);
}

#endif /* __MB_EXTENDED_CHARSETS_ISO */

#ifdef __MB_EXTENDED_CHARSETS_WINDOWS

static int
___cp_mbtowc(wchar_t *pwc, const char *s, size_t n, enum locale_id id, mbstate_t *state)
{
    wchar_t        dummy;
    unsigned char *t = (unsigned char *)s;

    (void)state;
    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL)
        return 0;

    if (n == 0)
        return -2;

    if (*t >= 0x80) {
        *pwc = __cp_conv[id - locale_WINDOWS_BASE][*t - 0x80];
        if (*pwc == 0) /* Invalid character */
            return -1;
        return 1;
    }

    *pwc = (wchar_t)*t;

    if (*t == '\0')
        return 0;

    return 1;
}

static int
__cp_437_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP437, state);
}

static int
__cp_720_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP720, state);
}

static int
__cp_737_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP737, state);
}

static int
__cp_775_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP775, state);
}

static int
__cp_850_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP850, state);
}

static int
__cp_852_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP852, state);
}

static int
__cp_855_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP855, state);
}

static int
__cp_857_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP857, state);
}

static int
__cp_858_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP858, state);
}

static int
__cp_862_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP862, state);
}

static int
__cp_866_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP866, state);
}

static int
__cp_874_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP874, state);
}

static int
__cp_1125_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1125, state);
}

static int
__cp_1250_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1250, state);
}

static int
__cp_1251_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1251, state);
}

static int
__cp_1252_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1252, state);
}

static int
__cp_1253_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1253, state);
}

static int
__cp_1254_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1254, state);
}

static int
__cp_1255_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1255, state);
}

static int
__cp_1256_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1256, state);
}

static int
__cp_1257_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1257, state);
}

static int
__cp_1258_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_CP1258, state);
}

static int
__cp_20866_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_KOI8_R, state);
}

static int
__cp_21866_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_KOI8_U, state);
}

static int
__cp_101_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_GEORGIAN_PS, state);
}

static int
__cp_102_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_PT154, state);
}

static int
__cp_103_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    return ___cp_mbtowc(pwc, s, n, locale_KOI8_T, state);
}

#endif /* __MB_EXTENDED_CHARSETS_WINDOWS */

#ifdef __MB_EXTENDED_CHARSETS_JIS

#include "jis.h"

/* clang-format off */
typedef enum __packed { ESCAPE, DOLLAR, BRACKET, AT, B, J,
               NUL, JIS_CHAR, OTHER, JIS_C_NUM } JIS_CHAR_TYPE;
typedef enum __packed { ASCII, JIS, A_ESC, A_ESC_DL, JIS_1, J_ESC, J_ESC_BR,
               INV, JIS_S_NUM } JIS_STATE;
typedef enum __packed { COPY_A, COPY_J1, COPY_J2, MAKE_A, NOOP, EMPTY, ERROR } JIS_ACTION;

/**************************************************************************************
 * state/action tables for processing JIS encoding
 * Where possible, switches to JIS are grouped with proceding JIS characters and switches
 * to ASCII are grouped with preceding JIS characters.  Thus, maximum returned length
 * is 2 (switch to JIS) + 2 (JIS characters) + 2 (switch back to ASCII) = 6.
 *************************************************************************************/

static const JIS_STATE JIS_state_table[JIS_S_NUM][JIS_C_NUM] = {
/*              ESCAPE   DOLLAR    BRACKET   AT       B       J        NUL      JIS_CHAR  OTHER */
/* ASCII */   { A_ESC,   ASCII,    ASCII,    ASCII,   ASCII,  ASCII,   ASCII,   ASCII,    ASCII },
/* JIS */     { J_ESC,   JIS_1,    JIS_1,    JIS_1,   JIS_1,  JIS_1,   INV,     JIS_1,    INV },
/* A_ESC */   { ASCII,   A_ESC_DL, ASCII,    ASCII,   ASCII,  ASCII,   ASCII,   ASCII,    ASCII },
/* A_ESC_DL */{ ASCII,   ASCII,    ASCII,    JIS,     JIS,    ASCII,   ASCII,   ASCII,    ASCII },
/* JIS_1 */   { INV,     JIS,      JIS,      JIS,     JIS,    JIS,     INV,     JIS,      INV },
/* J_ESC */   { INV,     INV,      J_ESC_BR, INV,     INV,    INV,     INV,     INV,      INV },
/* J_ESC_BR */{ INV,     INV,      INV,      INV,     ASCII,  ASCII,   INV,     INV,      INV },
};

static const JIS_ACTION JIS_action_table[JIS_S_NUM][JIS_C_NUM] = {
/*              ESCAPE   DOLLAR    BRACKET   AT       B        J        NUL      JIS_CHAR  OTHER */
/* ASCII */   { NOOP,    COPY_A,   COPY_A,   COPY_A,  COPY_A,  COPY_A,  EMPTY,   COPY_A,  COPY_A},
/* JIS */     { NOOP,    COPY_J1,  COPY_J1,  COPY_J1, COPY_J1, COPY_J1, ERROR,   COPY_J1, ERROR },
/* A_ESC */   { COPY_A,  NOOP,     COPY_A,   COPY_A,  COPY_A,  COPY_A,  COPY_A,  COPY_A,  COPY_A},
/* A_ESC_DL */{ COPY_A,  COPY_A,   COPY_A,   NOOP,    NOOP,    COPY_A,  COPY_A,  COPY_A,  COPY_A},
/* JIS_1 */   { ERROR,   COPY_J2,  COPY_J2,  COPY_J2, COPY_J2, COPY_J2, ERROR,   COPY_J2, ERROR },
/* J_ESC */   { ERROR,   ERROR,    NOOP,     ERROR,   ERROR,   ERROR,   ERROR,   ERROR,   ERROR },
/* J_ESC_BR */{ ERROR,   ERROR,    ERROR,    ERROR,   MAKE_A,  MAKE_A,  ERROR,   ERROR,   ERROR },
};
/* clang-format on */

static int
__sjis_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    wchar_t  dummy;
    wint_t   jischar = 0, uchar;
    uint8_t *t = (uint8_t *)s;
    uint8_t *wchb;
    uint8_t  ch;
    int      i = 0;

    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL)
        return 0; /* not state-dependent */

    if (n == 0)
        return -2;

    wchb = state->__value.__wchb;
    ch = t[i++];
    if (state->__count == 0) {
        if (_issjis1(ch)) {
            wchb[0] = ch;
            state->__count = 1;
            if (n <= 1)
                return -2;
            ch = t[i++];
        } else {
            if (!_issjis1b(ch))
                return -1;
            if (ch == '\0') {
                *pwc = L'\0';
                return 0;
            }
            jischar = ch;
        }
    }
    if (state->__count == 1) {
        if (!_issjis2(ch))
            return -1;
        jischar = ((uint16_t)wchb[0] << 8) | ch;
    }

    uchar = __shift_jis_to_unicode(jischar);
    state->__count = 0;
    if (uchar == WEOF)
        return -1;

    *pwc = (wchar_t)uchar;

    return i;
}

static int
__eucjp_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    wchar_t        dummy;
    uint32_t       jischar = 0;
    wint_t         uchar;
    const uint8_t *t = (const uint8_t *)s;
    uint8_t        ch;
    uint8_t       *wchb;
    int            i = 0;

    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL)
        return 0; /* not state-dependent */

    if (n == 0)
        return -2;

    wchb = state->__value.__wchb;
    ch = t[i++];
    if (state->__count == 0) {
        if (_iseucjp1(ch)) {
            wchb[0] = ch;
            state->__count = 1;
            if (n <= (size_t)i)
                return -2;
            ch = t[i++];
        } else {
            if (ch == '\0') {
                *pwc = L'\0';
                return 0;
            }
            jischar = ch;
        }
    }
    if (state->__count == 1) {
        if (_iseucjp2(ch)) {
            if (wchb[0] == 0x8f) {
                wchb[1] = ch;
                state->__count = 2;
                if (n <= (size_t)i)
                    return -2;
                ch = t[i++];
            } else {
                jischar = ((uint32_t)wchb[0] << 8) | ch;
            }
        } else {
            return -1;
        }
    }
    if (state->__count == 2) {
        if (_iseucjp2(ch)) {
            jischar = ((uint32_t)wchb[0] << 16) | ((uint32_t)wchb[1] << 8) | ch;
        } else {
            return -1;
        }
    }

    uchar = __euc_jp_to_unicode(jischar);

    state->__count = 0;
    if (uchar == WEOF)
        return -1;

    *pwc = (wchar_t)uchar;

    return i;
}

static int
__jis_mbtowc(wchar_t *pwc, const char *s, size_t n, mbstate_t *state)
{
    wchar_t        dummy;
    wint_t         jischar, uchar;
    const uint8_t *t = (const uint8_t *)s;
    JIS_STATE      curr_state;
    JIS_ACTION     action;
    JIS_CHAR_TYPE  ch;
    const uint8_t *ptr;
    size_t         i;
    uint8_t       *wchb;
    uint8_t        curr_ch;

    if (pwc == NULL)
        pwc = &dummy;

    if (s == NULL) {
        state->__state = ASCII;
        return 1; /* state-dependent */
    }

    if (n == 0)
        return -2;

    wchb = state->__value.__wchb;
    curr_state = state->__state;
    ptr = t;

    for (i = 0; i < n; ++i) {
        curr_ch = t[i];
        switch (curr_ch) {
        case ESC_CHAR:
            ch = ESCAPE;
            break;
        case '$':
            ch = DOLLAR;
            break;
        case '@':
            ch = AT;
            break;
        case '(':
            ch = BRACKET;
            break;
        case 'B':
            ch = B;
            break;
        case 'J':
            ch = J;
            break;
        case '\0':
            ch = NUL;
            break;
        default:
            if (_isjis(curr_ch))
                ch = JIS_CHAR;
            else
                ch = OTHER;
        }

        action = JIS_action_table[curr_state][ch];
        curr_state = JIS_state_table[curr_state][ch];

        switch (action) {
        case NOOP:
            break;
        case EMPTY:
            state->__state = ASCII;
            *pwc = L'\0';
            return 0;
        case COPY_A:
            state->__state = ASCII;
            *pwc = (wchar_t)*ptr;
            return i + 1;
        case COPY_J1:
            wchb[0] = t[i];
            break;
        case COPY_J2:
            state->__state = JIS;
            jischar = ((uint16_t)wchb[0] << 8) | t[i];
            uchar = __jis_to_unicode(jischar);
            if (uchar == WEOF)
                return -1;
            *pwc = uchar;
            return i + 1;
        case MAKE_A:
            ptr = (uint8_t *)(t + i + 1);
            break;
        case ERROR:
        default:
            return -1;
        }
    }

    state->__state = curr_state;
    return -2; /* n < bytes needed */
}

#endif /* __MB_EXTENDED_CHARSETS_JIS */

const mbtowc_p __mbtowc[locale_END - locale_BASE] = {
    [locale_C - locale_BASE] = __ascii_mbtowc,
    [locale_UTF_8 - locale_BASE] = __utf8_mbtowc,
#ifdef __MB_EXTENDED_CHARSETS_UCS
    [locale_UCS_2 - locale_BASE] = __ucs2_mbtowc,
    [locale_UCS_2LE - locale_BASE] = __ucs2le_mbtowc,
    [locale_UCS_2BE - locale_BASE] = __ucs2be_mbtowc,
    [locale_UCS_4 - locale_BASE] = __ucs4_mbtowc,
    [locale_UCS_4LE - locale_BASE] = __ucs4le_mbtowc,
    [locale_UCS_4BE - locale_BASE] = __ucs4be_mbtowc,
#endif
#ifdef __MB_EXTENDED_CHARSETS_ISO
    [locale_ISO_8859_1 - locale_BASE] = __iso_8859_1_mbtowc,
    [locale_ISO_8859_2 - locale_BASE] = __iso_8859_2_mbtowc,
    [locale_ISO_8859_3 - locale_BASE] = __iso_8859_3_mbtowc,
    [locale_ISO_8859_4 - locale_BASE] = __iso_8859_4_mbtowc,
    [locale_ISO_8859_5 - locale_BASE] = __iso_8859_5_mbtowc,
    [locale_ISO_8859_6 - locale_BASE] = __iso_8859_6_mbtowc,
    [locale_ISO_8859_7 - locale_BASE] = __iso_8859_7_mbtowc,
    [locale_ISO_8859_8 - locale_BASE] = __iso_8859_8_mbtowc,
    [locale_ISO_8859_9 - locale_BASE] = __iso_8859_9_mbtowc,
    [locale_ISO_8859_10 - locale_BASE] = __iso_8859_10_mbtowc,
    [locale_ISO_8859_11 - locale_BASE] = __iso_8859_11_mbtowc,
    [locale_ISO_8859_13 - locale_BASE] = __iso_8859_13_mbtowc,
    [locale_ISO_8859_14 - locale_BASE] = __iso_8859_14_mbtowc,
    [locale_ISO_8859_15 - locale_BASE] = __iso_8859_15_mbtowc,
    [locale_ISO_8859_16 - locale_BASE] = __iso_8859_16_mbtowc,
#endif
#ifdef __MB_EXTENDED_CHARSETS_WINDOWS
    [locale_CP437 - locale_BASE] = __cp_437_mbtowc,
    [locale_CP720 - locale_BASE] = __cp_720_mbtowc,
    [locale_CP737 - locale_BASE] = __cp_737_mbtowc,
    [locale_CP775 - locale_BASE] = __cp_775_mbtowc,
    [locale_CP850 - locale_BASE] = __cp_850_mbtowc,
    [locale_CP852 - locale_BASE] = __cp_852_mbtowc,
    [locale_CP855 - locale_BASE] = __cp_855_mbtowc,
    [locale_CP857 - locale_BASE] = __cp_857_mbtowc,
    [locale_CP858 - locale_BASE] = __cp_858_mbtowc,
    [locale_CP862 - locale_BASE] = __cp_862_mbtowc,
    [locale_CP866 - locale_BASE] = __cp_866_mbtowc,
    [locale_CP874 - locale_BASE] = __cp_874_mbtowc,
    [locale_CP1125 - locale_BASE] = __cp_1125_mbtowc,
    [locale_CP1250 - locale_BASE] = __cp_1250_mbtowc,
    [locale_CP1251 - locale_BASE] = __cp_1251_mbtowc,
    [locale_CP1252 - locale_BASE] = __cp_1252_mbtowc,
    [locale_CP1253 - locale_BASE] = __cp_1253_mbtowc,
    [locale_CP1254 - locale_BASE] = __cp_1254_mbtowc,
    [locale_CP1255 - locale_BASE] = __cp_1255_mbtowc,
    [locale_CP1256 - locale_BASE] = __cp_1256_mbtowc,
    [locale_CP1257 - locale_BASE] = __cp_1257_mbtowc,
    [locale_CP1258 - locale_BASE] = __cp_1258_mbtowc,
    [locale_KOI8_R - locale_BASE] = __cp_20866_mbtowc,
    [locale_KOI8_U - locale_BASE] = __cp_21866_mbtowc,
    [locale_GEORGIAN_PS - locale_BASE] = __cp_101_mbtowc,
    [locale_PT154 - locale_BASE] = __cp_102_mbtowc,
    [locale_KOI8_T - locale_BASE] = __cp_103_mbtowc,
#endif
#ifdef __MB_EXTENDED_CHARSETS_JIS
    [locale_JIS - locale_BASE] = __jis_mbtowc,
    [locale_EUCJP - locale_BASE] = __eucjp_mbtowc,
    [locale_SJIS - locale_BASE] = __sjis_mbtowc,
#endif
};

#endif /* __MB_CAPABLE */
