1251314Sed/*-
2251314Sed * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
3251314Sed * All rights reserved.
4251314Sed *
5251314Sed * Redistribution and use in source and binary forms, with or without
6251314Sed * modification, are permitted provided that the following conditions
7251314Sed * are met:
8251314Sed * 1. Redistributions of source code must retain the above copyright
9251314Sed *    notice, this list of conditions and the following disclaimer.
10251314Sed * 2. Redistributions in binary form must reproduce the above copyright
11251314Sed *    notice, this list of conditions and the following disclaimer in the
12251314Sed *    documentation and/or other materials provided with the distribution.
13251314Sed *
14251314Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15251314Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16251314Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17251314Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18251314Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19251314Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20251314Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21251314Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22251314Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23251314Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24251314Sed * SUCH DAMAGE.
25251314Sed */
26251314Sed
27251314Sed#include <sys/cdefs.h>
28251314Sed__FBSDID("$FreeBSD$");
29251314Sed
30251314Sed#include <sys/queue.h>
31251314Sed
32251314Sed#include <assert.h>
33251314Sed#include <errno.h>
34251314Sed#include <langinfo.h>
35251314Sed#include <uchar.h>
36251314Sed
37251314Sed#include "../iconv/citrus_hash.h"
38251314Sed#include "../iconv/citrus_module.h"
39251314Sed#include "../iconv/citrus_iconv.h"
40251314Sed#include "xlocale_private.h"
41251314Sed
42251314Sedtypedef struct {
43251314Sed	bool			initialized;
44251314Sed	struct _citrus_iconv	iconv;
45251314Sed	union {
46251314Sed		charXX_t	widechar[SRCBUF_LEN];
47251314Sed		char		bytes[sizeof(charXX_t) * SRCBUF_LEN];
48251314Sed	} srcbuf;
49251314Sed	size_t			srcbuf_len;
50251314Sed} _ConversionState;
51251314Sed_Static_assert(sizeof(_ConversionState) <= sizeof(mbstate_t),
52251314Sed    "Size of _ConversionState must not exceed mbstate_t's size.");
53251314Sed
54251314Sedsize_t
55251314SedcXXrtomb_l(char * __restrict s, charXX_t c, mbstate_t * __restrict ps,
56251314Sed    locale_t locale)
57251314Sed{
58251314Sed	_ConversionState *cs;
59251314Sed	struct _citrus_iconv *handle;
60282275Stijl	char *src, *dst;
61251314Sed	size_t srcleft, dstleft, invlen;
62251314Sed	int err;
63251314Sed
64251314Sed	FIX_LOCALE(locale);
65251314Sed	if (ps == NULL)
66251314Sed		ps = &locale->cXXrtomb;
67251314Sed	cs = (_ConversionState *)ps;
68251314Sed	handle = &cs->iconv;
69251314Sed
70251314Sed	/* Reinitialize mbstate_t. */
71251314Sed	if (s == NULL || !cs->initialized) {
72251314Sed		if (_citrus_iconv_open(&handle, UTF_XX_INTERNAL,
73251314Sed		    nl_langinfo_l(CODESET, locale)) != 0) {
74251314Sed			cs->initialized = false;
75251314Sed			errno = EINVAL;
76251314Sed			return (-1);
77251314Sed		}
78251314Sed		handle->cv_shared->ci_discard_ilseq = true;
79251314Sed		handle->cv_shared->ci_hooks = NULL;
80251314Sed		cs->srcbuf_len = 0;
81251314Sed		cs->initialized = true;
82251314Sed		if (s == NULL)
83251314Sed			return (1);
84251314Sed	}
85251314Sed
86251314Sed	assert(cs->srcbuf_len < sizeof(cs->srcbuf.widechar) / sizeof(charXX_t));
87251314Sed	cs->srcbuf.widechar[cs->srcbuf_len++] = c;
88251314Sed
89251314Sed	/* Perform conversion. */
90251314Sed	src = cs->srcbuf.bytes;
91251314Sed	srcleft = cs->srcbuf_len * sizeof(charXX_t);
92251314Sed	dst = s;
93251314Sed	dstleft = MB_CUR_MAX_L(locale);
94251314Sed	err = _citrus_iconv_convert(handle, &src, &srcleft, &dst, &dstleft,
95251314Sed	    0, &invlen);
96251314Sed
97251314Sed	/* Character is part of a surrogate pair. We need more input. */
98251314Sed	if (err == EINVAL)
99251314Sed		return (0);
100251314Sed	cs->srcbuf_len = 0;
101251314Sed
102251314Sed	/* Illegal sequence. */
103251314Sed	if (dst == s) {
104251314Sed		errno = EILSEQ;
105251314Sed		return ((size_t)-1);
106251314Sed	}
107251314Sed	return (dst - s);
108251314Sed}
109251314Sed
110251314Sedsize_t
111251314SedcXXrtomb(char * __restrict s, charXX_t c, mbstate_t * __restrict ps)
112251314Sed{
113251314Sed
114251314Sed	return (cXXrtomb_l(s, c, ps, __get_locale()));
115251314Sed}
116