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;
60252547Speter	const char *src;
61252547Speter	char *dst;
62251314Sed	size_t srcleft, dstleft, invlen;
63251314Sed	int err;
64251314Sed
65251314Sed	FIX_LOCALE(locale);
66251314Sed	if (ps == NULL)
67251314Sed		ps = &locale->cXXrtomb;
68251314Sed	cs = (_ConversionState *)ps;
69251314Sed	handle = &cs->iconv;
70251314Sed
71251314Sed	/* Reinitialize mbstate_t. */
72251314Sed	if (s == NULL || !cs->initialized) {
73251314Sed		if (_citrus_iconv_open(&handle, UTF_XX_INTERNAL,
74251314Sed		    nl_langinfo_l(CODESET, locale)) != 0) {
75251314Sed			cs->initialized = false;
76251314Sed			errno = EINVAL;
77251314Sed			return (-1);
78251314Sed		}
79251314Sed		handle->cv_shared->ci_discard_ilseq = true;
80251314Sed		handle->cv_shared->ci_hooks = NULL;
81251314Sed		cs->srcbuf_len = 0;
82251314Sed		cs->initialized = true;
83251314Sed		if (s == NULL)
84251314Sed			return (1);
85251314Sed	}
86251314Sed
87251314Sed	assert(cs->srcbuf_len < sizeof(cs->srcbuf.widechar) / sizeof(charXX_t));
88251314Sed	cs->srcbuf.widechar[cs->srcbuf_len++] = c;
89251314Sed
90251314Sed	/* Perform conversion. */
91251314Sed	src = cs->srcbuf.bytes;
92251314Sed	srcleft = cs->srcbuf_len * sizeof(charXX_t);
93251314Sed	dst = s;
94251314Sed	dstleft = MB_CUR_MAX_L(locale);
95251314Sed	err = _citrus_iconv_convert(handle, &src, &srcleft, &dst, &dstleft,
96251314Sed	    0, &invlen);
97251314Sed
98251314Sed	/* Character is part of a surrogate pair. We need more input. */
99251314Sed	if (err == EINVAL)
100251314Sed		return (0);
101251314Sed	cs->srcbuf_len = 0;
102251314Sed
103251314Sed	/* Illegal sequence. */
104251314Sed	if (dst == s) {
105251314Sed		errno = EILSEQ;
106251314Sed		return ((size_t)-1);
107251314Sed	}
108251314Sed	return (dst - s);
109251314Sed}
110251314Sed
111251314Sedsize_t
112251314SedcXXrtomb(char * __restrict s, charXX_t c, mbstate_t * __restrict ps)
113251314Sed{
114251314Sed
115251314Sed	return (cXXrtomb_l(s, c, ps, __get_locale()));
116251314Sed}
117