root/lib/libc/citrus/citrus_ctype_template.h
/*      $NetBSD: citrus_ctype_template.h,v 1.40 2022/05/28 22:16:43 andvar Exp $        */

/*-
 * Copyright (c)2002 Citrus Project,
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*-
 * Copyright (c) 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Paul Borman at Krystal Technologies.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


/*
 * CAUTION: THIS IS NOT STANDALONE FILE
 *
 * function templates of ctype encoding handler for each encodings.
 *
 * you need to define the macros below:
 *
 *   _FUNCNAME(method) :
 *     It should convine the real function name for the method.
 *      e.g. _FUNCNAME(mbrtowc) should be expanded to
 *             _EUC_ctype_mbrtowc
 *           for EUC locale.
 *
 *   _CEI_TO_STATE(cei, method) :
 *     It should be expanded to the pointer of the method-internal state
 *     structures.
 *     e.g. _CEI_TO_STATE(cei, mbrtowc) might be expanded to
 *             (cei)->states.s_mbrtowc
 *     This structure may use if the function is called as
 *           mbrtowc(&wc, s, n, NULL);
 *     Such individual structures are needed by:
 *           mblen
 *           mbrlen
 *           mbrtowc
 *           mbtowc
 *           mbsrtowcs
 *           mbsnrtowcs
 *           wcrtomb
 *           wcsrtombs
 *           wcsnrtombs
 *           wctomb
 *     These need to be kept in the ctype encoding information structure,
 *     pointed by "cei".
 *
 *   _ENCODING_INFO :
 *     It should be expanded to the name of the encoding information structure.
 *     e.g. For EUC encoding, this macro is expanded to _EUCInfo.
 *     Encoding information structure need to contain the common informations
 *     for the codeset.
 *
 *   _ENCODING_STATE :
 *     It should be expanded to the name of the encoding state structure.
 *     e.g. For EUC encoding, this macro is expanded to _EUCState.
 *     Encoding state structure need to contain the context-dependent states,
 *     which are "unpacked-form" of mbstate_t type and kept during sequent
 *     calls of mb/wc functions,
 *
 *   _ENCODING_IS_STATE_DEPENDENT :
 *     If the encoding is state dependent, this should be expanded to
 *     non-zero integral value.  Otherwise, 0.
 *
 *   _STATE_NEEDS_EXPLICIT_INIT(ps) :
 *     some encodings, states needs some explicit initialization.
 *     (ie. initialization with memset isn't enough.)
 *     If the encoding state pointed by "ps" needs to be initialized
 *     explicitly, return non-zero. Otherwize, 0.
 *
 */

#include <stdalign.h>

/* prototypes */

__BEGIN_DECLS
static void _FUNCNAME(init_state)(_ENCODING_INFO * __restrict,
                                  _ENCODING_STATE * __restrict);
static void _FUNCNAME(pack_state)(_ENCODING_INFO * __restrict,
                                  void * __restrict,
                                  const _ENCODING_STATE * __restrict);
static void _FUNCNAME(unpack_state)(_ENCODING_INFO * __restrict,
                                    _ENCODING_STATE * __restrict,
                                    const void * __restrict);
#if _ENCODING_IS_STATE_DEPENDENT
static int _FUNCNAME(put_state_reset)(_ENCODING_INFO * __restrict,
                                      char * __restrict, size_t,
                                      _ENCODING_STATE * __restrict,
                                      size_t * __restrict);
#endif

/*
 * standard form of mbrtowc_priv.
 *
 * note (differences from real mbrtowc):
 *   - 3rd parameter is not "const char *s" but "const char **s".
 *     after the call of the function, *s will point the first byte of
 *     the next character.
 *   - additional 4th parameter is the size of src buffer.
 *   - 5th parameter is unpacked encoding-dependent state structure.
 *   - additional 6th parameter is the storage to be stored
 *     the return value in the real mbrtowc context.
 *   - return value means "errno" in the real mbrtowc context.
 */

static int _FUNCNAME(mbrtowc_priv)(_ENCODING_INFO * __restrict,
                                   wchar_t * __restrict,
                                   const char ** __restrict,
                                   size_t, _ENCODING_STATE * __restrict,
                                   size_t * __restrict);

/*
 * standard form of wcrtomb_priv.
 *
 * note (differences from real wcrtomb):
 *   - additional 3th parameter is the size of src buffer.
 *   - 5th parameter is unpacked encoding-dependent state structure.
 *   - additional 6th parameter is the storage to be stored
 *     the return value in the real mbrtowc context.
 *   - return value means "errno" in the real wcrtomb context.
 *   - caller should ensure that 2nd parameter isn't NULL.
 *     (XXX inconsist with mbrtowc_priv)
 */

static int _FUNCNAME(wcrtomb_priv)(_ENCODING_INFO * __restrict,
                                   char * __restrict, size_t, wchar_t,
                                   _ENCODING_STATE * __restrict,
                                   size_t * __restrict);
__END_DECLS


/*
 * macros
 */

#define _TO_CEI(_cl_)   ((_CTYPE_INFO*)(_cl_))


/*
 * templates
 */

/* internal routines */

static __inline int
_FUNCNAME(mbtowc_priv)(_ENCODING_INFO * __restrict ei,
                       wchar_t * __restrict pwc,  const char * __restrict s,
                       size_t n, _ENCODING_STATE * __restrict psenc,
                       int * __restrict nresult)
{
        _ENCODING_STATE state;
        size_t nr;
        int err = 0;

        _DIAGASSERT(ei != NULL);
        _DIAGASSERT(psenc != NULL);

        if (s == NULL) {
                _FUNCNAME(init_state)(ei, psenc);
                *nresult = _ENCODING_IS_STATE_DEPENDENT;
                return (0);
        }

        state = *psenc;
        err = _FUNCNAME(mbrtowc_priv)(ei, pwc, (const char **)&s, n, psenc, &nr);
        if (nr == (size_t)-2)
                err = EILSEQ;
        if (err) {
                /* In error case, we should restore the state. */
                *psenc = state;
                *nresult = -1;
                return (err);
        }

        *nresult = (int)nr;

        return (0);
}

static int
_FUNCNAME(mbsrtowcs_priv)(_ENCODING_INFO * __restrict ei,
                          wchar_t * __restrict pwcs,
                          const char ** __restrict s,
                          size_t n, _ENCODING_STATE * __restrict psenc,
                          size_t * __restrict nresult)
{
        int err, cnt;
        size_t siz;
        const char *s0;
        size_t mbcurmax;

        _DIAGASSERT(nresult != 0);
        _DIAGASSERT(ei != NULL);
        _DIAGASSERT(psenc != NULL);
        _DIAGASSERT(s != NULL);
        _DIAGASSERT(*s != NULL);

        /* if pwcs is NULL, ignore n */
        if (pwcs == NULL)
                n = 1; /* arbitrary >0 value */

        err = cnt = 0;
        s0 = *s; /* to keep *s unchanged for now, use copy instead. */
        mbcurmax = _ENCODING_MB_CUR_MAX(ei);
        while (n > 0) {
                err = _FUNCNAME(mbrtowc_priv)(ei, pwcs, &s0, mbcurmax,
                                              psenc, &siz);
                if (siz == (size_t)-2)
                        err = EILSEQ;
                if (err) {
                        cnt = -1;
                        goto bye;
                }
                switch (siz) {
                case 0:
                        if (pwcs) {
                                _FUNCNAME(init_state)(ei, psenc);
                        }
                        s0 = 0;
                        goto bye;
                default:
                        if (pwcs) {
                                pwcs++;
                                n--;
                        }
                        cnt++;
                        break;
                }
        }
bye:
        if (pwcs)
                *s = s0;

        *nresult = (size_t)cnt;

        return err;
}

static int
_FUNCNAME(mbsnrtowcs_priv)(_ENCODING_INFO * __restrict ei,
                          wchar_t * __restrict pwcs,
                          const char ** __restrict s, size_t in,
                          size_t n, _ENCODING_STATE * __restrict psenc,
                          size_t * __restrict nresult)
{
        int err;
        size_t cnt, siz;
        const char *s0, *se;

        _DIAGASSERT(nresult != 0);
        _DIAGASSERT(ei != NULL);
        _DIAGASSERT(psenc != NULL);
        _DIAGASSERT(s != NULL);
        _DIAGASSERT(*s != NULL);

        /* if pwcs is NULL, ignore n */
        if (pwcs == NULL)
                n = 1; /* arbitrary >0 value */

        err = 0;
        cnt = 0;
        se = *s + in;
        s0 = *s; /* to keep *s unchanged for now, use copy instead. */
        while (s0 < se && n > 0) {
                err = _FUNCNAME(mbrtowc_priv)(ei, pwcs, &s0, se - s0,
                                              psenc, &siz);
                if (err) {
                        cnt = (size_t)-1;
                        goto bye;
                }
                if (siz == (size_t)-2) {
                        s0 = se;
                        goto bye;
                }
                switch (siz) {
                case 0:
                        if (pwcs) {
                                _FUNCNAME(init_state)(ei, psenc);
                        }
                        s0 = 0;
                        goto bye;
                default:
                        if (pwcs) {
                                pwcs++;
                                n--;
                        }
                        cnt++;
                        break;
                }
        }
bye:
        if (pwcs)
                *s = s0;

        *nresult = cnt;

        return err;
}

static int
_FUNCNAME(wcsrtombs_priv)(_ENCODING_INFO * __restrict ei, char * __restrict s,
                          const wchar_t ** __restrict pwcs,
                          size_t n, _ENCODING_STATE * __restrict psenc,
                          size_t * __restrict nresult)
{
        int err;
        char buf[MB_LEN_MAX];
        size_t cnt, siz;
        const wchar_t* pwcs0;
#if _ENCODING_IS_STATE_DEPENDENT
        _ENCODING_STATE state;
#endif

        pwcs0 = *pwcs;

        cnt = 0;
        if (!s)
                n = 1;

        while (n > 0) {
#if _ENCODING_IS_STATE_DEPENDENT
                state = *psenc;
#endif
                err = _FUNCNAME(wcrtomb_priv)(ei, buf, sizeof(buf),
                                              *pwcs0, psenc, &siz);
                if (siz == (size_t)-1) {
                        *pwcs = pwcs0;
                        *nresult = siz;
                        return (err);
                }

                if (s) {
                        if (n < siz) {
#if _ENCODING_IS_STATE_DEPENDENT
                                *psenc = state;
#endif
                                break;
                        }
                        memcpy(s, buf, siz);
                        s += siz;
                        n -= siz;
                }
                cnt += siz;
                if (!*pwcs0) {
                        if (s) {
                                _FUNCNAME(init_state)(ei, psenc);
                        }
                        pwcs0 = 0;
                        cnt--; /* don't include terminating null */
                        break;
                }
                pwcs0++;
        }
        if (s)
                *pwcs = pwcs0;

        *nresult = cnt;
        return (0);
}

static int
_FUNCNAME(wcsnrtombs_priv)(_ENCODING_INFO * __restrict ei, char * __restrict s,
                          const wchar_t ** __restrict pwcs, size_t in,
                          size_t n, _ENCODING_STATE * __restrict psenc,
                          size_t * __restrict nresult)
{
        int cnt = 0, err;
        char buf[MB_LEN_MAX];
        size_t siz;
        const wchar_t* pwcs0;
#if _ENCODING_IS_STATE_DEPENDENT
        _ENCODING_STATE state;
#endif

        pwcs0 = *pwcs;

        if (!s)
                n = 1;

        while (in > 0 && n > 0) {
#if _ENCODING_IS_STATE_DEPENDENT
                state = *psenc;
#endif
                err = _FUNCNAME(wcrtomb_priv)(ei, buf, sizeof(buf),
                                              *pwcs0, psenc, &siz);
                if (siz == (size_t)-1) {
                        *nresult = siz;
                        return (err);
                }

                if (s) {
                        if (n < siz) {
#if _ENCODING_IS_STATE_DEPENDENT
                                *psenc = state;
#endif
                                break;
                        }
                        memcpy(s, buf, siz);
                        s += siz;
                        n -= siz;
                }
                cnt += siz;
                if (!*pwcs0) {
                        if (s) {
                                _FUNCNAME(init_state)(ei, psenc);
                        }
                        pwcs0 = 0;
                        cnt--; /* don't include terminating null */
                        break;
                }
                pwcs0++;
                --in;
        }
        if (s)
                *pwcs = pwcs0;

        *nresult = (size_t)cnt;
        return (0);
}


/* ----------------------------------------------------------------------
 * templates for public functions
 */

#define _RESTART_BEGIN(_func_, _cei_, _pspriv_, _pse_)                  \
do {                                                                    \
        _ENCODING_STATE _state;                                         \
        do {                                                            \
                if (_pspriv_ == NULL) {                                 \
                        _pse_ = &_CEI_TO_STATE(_cei_, _func_);          \
                        if (_STATE_NEEDS_EXPLICIT_INIT(_pse_))          \
                            _FUNCNAME(init_state)(_CEI_TO_EI(_cei_),    \
                                                        (_pse_));       \
                } else {                                                \
                        _pse_ = &_state;                                \
                        _FUNCNAME(unpack_state)(_CEI_TO_EI(_cei_),      \
                                                _pse_, _pspriv_);       \
                }                                                       \
        } while (0)

#define _RESTART_END(_func_, _cei_, _pspriv_, _pse_)                    \
        if (_pspriv_ != NULL) {                                         \
                _FUNCNAME(pack_state)(_CEI_TO_EI(_cei_), _pspriv_,      \
                                      _pse_);                           \
        }                                                               \
} while (0)

int
_FUNCNAME(ctype_getops)(_citrus_ctype_ops_rec_t *ops, size_t lenops,
                        uint32_t expected_version)
{
        if (expected_version<_CITRUS_CTYPE_ABI_VERSION || lenops<sizeof(*ops))
                return (EINVAL);

        memcpy(ops, &_FUNCNAME(ctype_ops), sizeof(_FUNCNAME(ctype_ops)));

        return (0);
}

/* Ensure alignment matches guarantees from locale/multibyte.h */
__CTASSERT(alignof(_ENCODING_STATE) <= alignof(int) ||
           alignof(_ENCODING_STATE) <= alignof(void *));

static int
_FUNCNAME(ctype_init)(void ** __restrict cl,
                      void * __restrict var, size_t lenvar, size_t lenps)
{
        _CTYPE_INFO *cei;

        _DIAGASSERT(cl != NULL);

        /* sanity check to avoid overruns */
        if (sizeof(_ENCODING_STATE) > lenps)
                return (EINVAL);

        cei = calloc(1, sizeof(_CTYPE_INFO));
        if (cei == NULL)
                return (ENOMEM);

        *cl = (void *)cei;

        return _FUNCNAME(encoding_module_init)(_CEI_TO_EI(cei), var, lenvar);
}

static void
_FUNCNAME(ctype_uninit)(void *cl)
{
        if (cl) {
                _FUNCNAME(encoding_module_uninit)(_CEI_TO_EI(_TO_CEI(cl)));
                free(cl);
        }
}

static unsigned
/*ARGSUSED*/
_FUNCNAME(ctype_get_mb_cur_max)(void *cl)
{
        return _ENCODING_MB_CUR_MAX(_CEI_TO_EI(_TO_CEI(cl)));
}

static int
_FUNCNAME(ctype_mblen)(void * __restrict cl,
                       const char * __restrict s, size_t n,
                       int * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;

        _DIAGASSERT(cl != NULL);

        psenc = &_CEI_TO_STATE(_TO_CEI(cl), mblen);
        ei = _CEI_TO_EI(_TO_CEI(cl));
        if (_STATE_NEEDS_EXPLICIT_INIT(psenc))
                _FUNCNAME(init_state)(ei, psenc);
        return _FUNCNAME(mbtowc_priv)(ei, NULL, s, n, psenc, nresult);
}

static int
_FUNCNAME(ctype_mbrlen)(void * __restrict cl, const char * __restrict s,
                        size_t n, void * __restrict pspriv,
                        size_t * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _RESTART_BEGIN(mbrlen, _TO_CEI(cl), pspriv, psenc);
        if (s == NULL) {
                _FUNCNAME(init_state)(ei, psenc);
                *nresult = 0;
        } else {
                err = _FUNCNAME(mbrtowc_priv)(ei, NULL, (const char **)&s, n,
                    (void *)psenc, nresult);
        }
        _RESTART_END(mbrlen, _TO_CEI(cl), pspriv, psenc);

        return (err);
}

static int
_FUNCNAME(ctype_mbrtowc)(void * __restrict cl, wchar_t * __restrict pwc,
                         const char * __restrict s, size_t n,
                         void * __restrict pspriv, size_t * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _RESTART_BEGIN(mbrtowc, _TO_CEI(cl), pspriv, psenc);
        if (s == NULL) {
                _FUNCNAME(init_state)(ei, psenc);
                *nresult = 0;
        } else {
                err = _FUNCNAME(mbrtowc_priv)(ei, pwc, (const char **)&s, n,
                    (void *)psenc, nresult);
        }
        _RESTART_END(mbrtowc, _TO_CEI(cl), pspriv, psenc);

        return (err);
}

static int
/*ARGSUSED*/
_FUNCNAME(ctype_mbsinit)(void * __restrict cl, const void * __restrict pspriv,
                         int * __restrict nresult)
{
        _ENCODING_STATE state;

        if (pspriv == NULL) {
                *nresult = 1;
                return (0);
        }

        _FUNCNAME(unpack_state)(_CEI_TO_EI(_TO_CEI(cl)), &state, pspriv);

        *nresult = (state.chlen == 0); /* XXX: FIXME */

        return (0);
}

static int
_FUNCNAME(ctype_mbsrtowcs)(void * __restrict cl, wchar_t * __restrict pwcs,
                           const char ** __restrict s, size_t n,
                           void * __restrict pspriv,
                           size_t * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _RESTART_BEGIN(mbsrtowcs, _TO_CEI(cl), pspriv, psenc);
        err = _FUNCNAME(mbsrtowcs_priv)(ei, pwcs, s, n, psenc, nresult);
        _RESTART_END(mbsrtowcs, _TO_CEI(cl), pspriv, psenc);

        return (err);
}

static int __used
_FUNCNAME(ctype_mbsnrtowcs)(_citrus_ctype_rec_t * __restrict cc, wchar_t * __restrict pwcs,
                           const char ** __restrict s, size_t in, size_t n,
                           void * __restrict pspriv,
                           size_t * __restrict nresult)
{
        void *cl = cc->cc_closure;
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _RESTART_BEGIN(mbsnrtowcs, _TO_CEI(cl), pspriv, psenc);
        err = _FUNCNAME(mbsnrtowcs_priv)(ei, pwcs, s, in, n, psenc, nresult);
        _RESTART_END(mbsnrtowcs, _TO_CEI(cl), pspriv, psenc);

        return (err);
}

static int
_FUNCNAME(ctype_mbstowcs)(void * __restrict cl, wchar_t * __restrict pwcs,
                          const char * __restrict s, size_t n,
                          size_t * __restrict nresult)
{
        int err;
        _ENCODING_STATE state;
        _ENCODING_INFO *ei;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _FUNCNAME(init_state)(ei, &state);
        err = _FUNCNAME(mbsrtowcs_priv)(ei, pwcs, (const char **)&s, n,
                                        &state, nresult);
        if (*nresult == (size_t)-2) {
                err = EILSEQ;
                *nresult = (size_t)-1;
        }

        return (err);
}

static int
_FUNCNAME(ctype_mbtowc)(void * __restrict cl, wchar_t * __restrict pwc,
                        const char * __restrict s, size_t n,
                        int * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;

        _DIAGASSERT(cl != NULL);

        psenc = &_CEI_TO_STATE(_TO_CEI(cl), mbtowc);
        ei = _CEI_TO_EI(_TO_CEI(cl));
        if (_STATE_NEEDS_EXPLICIT_INIT(psenc))
                _FUNCNAME(init_state)(ei, psenc);
        return _FUNCNAME(mbtowc_priv)(ei, pwc, s, n, psenc, nresult);
}

static int
_FUNCNAME(ctype_wcrtomb)(void * __restrict cl, char * __restrict s, wchar_t wc,
                         void * __restrict pspriv, size_t * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        char buf[MB_LEN_MAX];
        int err = 0;
        size_t sz;
#if _ENCODING_IS_STATE_DEPENDENT
        size_t rsz = 0;
#endif

        _DIAGASSERT(cl != NULL);

        if (s == NULL) {
                /*
                 * use internal buffer.
                 */
                s = buf;
                wc = L'\0'; /* SUSv3 */
        }

        _RESTART_BEGIN(wcrtomb, _TO_CEI(cl), pspriv, psenc);
        sz = _ENCODING_MB_CUR_MAX(_CEI_TO_EI(_TO_CEI(cl)));
#if _ENCODING_IS_STATE_DEPENDENT
        if (wc == L'\0') {
                /* reset state */
                err = _FUNCNAME(put_state_reset)(_CEI_TO_EI(_TO_CEI(cl)), s,
                                                 sz, psenc, &rsz);
                if (err) {
                        *nresult = -1;
                        goto quit;
                }
                s += rsz;
                sz -= rsz;
        }
#endif
        err = _FUNCNAME(wcrtomb_priv)(_CEI_TO_EI(_TO_CEI(cl)), s, sz,
                                      wc, psenc, nresult);
#if _ENCODING_IS_STATE_DEPENDENT
        if (err == 0)
                *nresult += rsz;
quit:
#endif
        if (err == E2BIG)
                err = EINVAL;
        _RESTART_END(wcrtomb, _TO_CEI(cl), pspriv, psenc);

        return err;
}

static int
/*ARGSUSED*/
_FUNCNAME(ctype_wcsrtombs)(void * __restrict cl, char * __restrict s,
                           const wchar_t ** __restrict pwcs, size_t n,
                           void * __restrict pspriv,
                           size_t * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _RESTART_BEGIN(wcsrtombs, _TO_CEI(cl), pspriv, psenc);
        err = _FUNCNAME(wcsrtombs_priv)(ei, s, pwcs, n, psenc, nresult);
        _RESTART_END(wcsrtombs, _TO_CEI(cl), pspriv, psenc);

        return err;
}

static int __used
/*ARGSUSED*/
_FUNCNAME(ctype_wcsnrtombs)(_citrus_ctype_rec_t * __restrict cc,
                           char * __restrict s,
                           const wchar_t ** __restrict pwcs, size_t in,
                           size_t n, void * __restrict pspriv,
                           size_t * __restrict nresult)
{
        void *cl = cc->cc_closure;
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _RESTART_BEGIN(wcsnrtombs, _TO_CEI(cl), pspriv, psenc);
        err = _FUNCNAME(wcsnrtombs_priv)(ei, s, pwcs, in, n, psenc, nresult);
        _RESTART_END(wcsnrtombs, _TO_CEI(cl), pspriv, psenc);

        return err;
}

static int
/*ARGSUSED*/
_FUNCNAME(ctype_wcstombs)(void * __restrict cl, char * __restrict s,
                          const wchar_t * __restrict pwcs, size_t n,
                          size_t * __restrict nresult)
{
        _ENCODING_STATE state;
        _ENCODING_INFO *ei;
        int err;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        _FUNCNAME(init_state)(ei, &state);
        err = _FUNCNAME(wcsrtombs_priv)(ei, s, (const wchar_t **)&pwcs, n,
                                        &state, nresult);

        return err;
}

static int
_FUNCNAME(ctype_wctomb)(void * __restrict cl, char * __restrict s, wchar_t wc,
                        int * __restrict nresult)
{
        _ENCODING_STATE *psenc;
        _ENCODING_INFO *ei;
        size_t nr, sz;
#if _ENCODING_IS_STATE_DEPENDENT
        size_t rsz = 0;
#endif
        int err = 0;

        _DIAGASSERT(cl != NULL);

        ei = _CEI_TO_EI(_TO_CEI(cl));
        psenc = &_CEI_TO_STATE(_TO_CEI(cl), wctomb);
        if (_STATE_NEEDS_EXPLICIT_INIT(psenc))
                _FUNCNAME(init_state)(ei, psenc);
        if (s == NULL) {
                _FUNCNAME(init_state)(ei, psenc);
                *nresult = _ENCODING_IS_STATE_DEPENDENT;
                return 0;
        }
        sz = _ENCODING_MB_CUR_MAX(_CEI_TO_EI(_TO_CEI(cl)));
#if _ENCODING_IS_STATE_DEPENDENT
        if (wc == L'\0') {
                /* reset state */
                err = _FUNCNAME(put_state_reset)(_CEI_TO_EI(_TO_CEI(cl)), s,
                                                 sz, psenc, &rsz);
                if (err) {
                        *nresult = -1; /* XXX */
                        return 0;
                }
                s += rsz;
                sz -= rsz;
        }
#endif
        err = _FUNCNAME(wcrtomb_priv)(ei, s, sz, wc, psenc, &nr);
#if _ENCODING_IS_STATE_DEPENDENT
        if (err == 0)
                *nresult = (int)(nr + rsz);
        else
#endif
        *nresult = (int)nr;

        return 0;
}

static int
/*ARGSUSED*/
_FUNCNAME(ctype_btowc)(_citrus_ctype_rec_t * __restrict cc,
                       int c, wint_t * __restrict wcresult)
{
        _ENCODING_STATE state;
        _ENCODING_INFO *ei;
        char mb;
        char const *s;
        wchar_t wc;
        size_t nr;
        int err;

        _DIAGASSERT(cc != NULL && cc->cc_closure != NULL);

        if (c == EOF) {
                *wcresult = WEOF;
                return 0;
        }
        ei = _CEI_TO_EI(_TO_CEI(cc->cc_closure));
        _FUNCNAME(init_state)(ei, &state);
        mb = (char)(unsigned)c;
        s = &mb;
        err = _FUNCNAME(mbrtowc_priv)(ei, &wc, &s, 1, &state, &nr);
        if (!err && (nr == 0 || nr == 1))
                *wcresult = (wint_t)wc;
        else
                *wcresult = WEOF;

        return 0;
}

static int
/*ARGSUSED*/
_FUNCNAME(ctype_wctob)(_citrus_ctype_rec_t * __restrict cc,
                       wint_t wc, int * __restrict cresult)
{
        _ENCODING_STATE state;
        _ENCODING_INFO *ei;
        char buf[MB_LEN_MAX];
        size_t nr;
        int err;

        _DIAGASSERT(cc != NULL && cc->cc_closure != NULL);

        if (wc == WEOF) {
                *cresult = EOF;
                return 0;
        }
        ei = _CEI_TO_EI(_TO_CEI(cc->cc_closure));
        _FUNCNAME(init_state)(ei, &state);
        err = _FUNCNAME(wcrtomb_priv)(ei, buf, _ENCODING_MB_CUR_MAX(ei),
                                      (wchar_t)wc, &state, &nr);
        if (!err && nr == 1)
                *cresult = buf[0];
        else
                *cresult = EOF;

        return 0;
}