1219019Sgabor/* $FreeBSD$ */
2219019Sgabor/*	$NetBSD: citrus_iconv_std.c,v 1.15 2006/11/13 19:08:19 tnozaki Exp $	*/
3219019Sgabor
4219019Sgabor/*-
5219019Sgabor * Copyright (c)2003 Citrus Project,
6219019Sgabor * All rights reserved.
7219019Sgabor *
8219019Sgabor * Redistribution and use in source and binary forms, with or without
9219019Sgabor * modification, are permitted provided that the following conditions
10219019Sgabor * are met:
11219019Sgabor * 1. Redistributions of source code must retain the above copyright
12219019Sgabor *    notice, this list of conditions and the following disclaimer.
13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14219019Sgabor *    notice, this list of conditions and the following disclaimer in the
15219019Sgabor *    documentation and/or other materials provided with the distribution.
16219019Sgabor *
17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219019Sgabor * SUCH DAMAGE.
28219019Sgabor */
29219019Sgabor
30219019Sgabor#include <sys/cdefs.h>
31219019Sgabor#include <sys/endian.h>
32219019Sgabor#include <sys/queue.h>
33219019Sgabor
34219019Sgabor#include <assert.h>
35219019Sgabor#include <errno.h>
36219019Sgabor#include <limits.h>
37219019Sgabor#include <stdbool.h>
38219019Sgabor#include <stdio.h>
39219019Sgabor#include <stdlib.h>
40219019Sgabor#include <string.h>
41219019Sgabor
42219019Sgabor#include "citrus_namespace.h"
43219019Sgabor#include "citrus_types.h"
44219019Sgabor#include "citrus_module.h"
45219019Sgabor#include "citrus_region.h"
46219019Sgabor#include "citrus_mmap.h"
47219019Sgabor#include "citrus_hash.h"
48219019Sgabor#include "citrus_iconv.h"
49219019Sgabor#include "citrus_stdenc.h"
50219019Sgabor#include "citrus_mapper.h"
51219019Sgabor#include "citrus_csmapper.h"
52219019Sgabor#include "citrus_memstream.h"
53219019Sgabor#include "citrus_iconv_std.h"
54219019Sgabor#include "citrus_esdb.h"
55219019Sgabor
56219019Sgabor/* ---------------------------------------------------------------------- */
57219019Sgabor
58219019Sgabor_CITRUS_ICONV_DECLS(iconv_std);
59219019Sgabor_CITRUS_ICONV_DEF_OPS(iconv_std);
60219019Sgabor
61219019Sgabor
62219019Sgabor/* ---------------------------------------------------------------------- */
63219019Sgabor
64219019Sgaborint
65219019Sgabor_citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops *ops)
66219019Sgabor{
67219019Sgabor
68219019Sgabor	memcpy(ops, &_citrus_iconv_std_iconv_ops,
69219019Sgabor	    sizeof(_citrus_iconv_std_iconv_ops));
70219019Sgabor
71219019Sgabor	return (0);
72219019Sgabor}
73219019Sgabor
74219019Sgabor/* ---------------------------------------------------------------------- */
75219019Sgabor
76219019Sgabor/*
77219019Sgabor * convenience routines for stdenc.
78219019Sgabor */
79219019Sgaborstatic __inline void
80219019Sgaborsave_encoding_state(struct _citrus_iconv_std_encoding *se)
81219019Sgabor{
82219019Sgabor
83219019Sgabor	if (se->se_ps)
84219019Sgabor		memcpy(se->se_pssaved, se->se_ps,
85219019Sgabor		    _stdenc_get_state_size(se->se_handle));
86219019Sgabor}
87219019Sgabor
88219019Sgaborstatic __inline void
89219019Sgaborrestore_encoding_state(struct _citrus_iconv_std_encoding *se)
90219019Sgabor{
91219019Sgabor
92219019Sgabor	if (se->se_ps)
93219019Sgabor		memcpy(se->se_ps, se->se_pssaved,
94219019Sgabor		    _stdenc_get_state_size(se->se_handle));
95219019Sgabor}
96219019Sgabor
97219019Sgaborstatic __inline void
98219019Sgaborinit_encoding_state(struct _citrus_iconv_std_encoding *se)
99219019Sgabor{
100219019Sgabor
101219019Sgabor	if (se->se_ps)
102219019Sgabor		_stdenc_init_state(se->se_handle, se->se_ps);
103219019Sgabor}
104219019Sgabor
105219019Sgaborstatic __inline int
106219019Sgabormbtocsx(struct _citrus_iconv_std_encoding *se,
107252583Speter    _csid_t *csid, _index_t *idx, const char **s, size_t n, size_t *nresult,
108219019Sgabor    struct iconv_hooks *hooks)
109219019Sgabor{
110219019Sgabor
111219019Sgabor	return (_stdenc_mbtocs(se->se_handle, csid, idx, s, n, se->se_ps,
112219019Sgabor			      nresult, hooks));
113219019Sgabor}
114219019Sgabor
115219019Sgaborstatic __inline int
116219019Sgaborcstombx(struct _citrus_iconv_std_encoding *se,
117219019Sgabor    char *s, size_t n, _csid_t csid, _index_t idx, size_t *nresult,
118219019Sgabor    struct iconv_hooks *hooks)
119219019Sgabor{
120219019Sgabor
121219019Sgabor	return (_stdenc_cstomb(se->se_handle, s, n, csid, idx, se->se_ps,
122219019Sgabor			      nresult, hooks));
123219019Sgabor}
124219019Sgabor
125219019Sgaborstatic __inline int
126219019Sgaborwctombx(struct _citrus_iconv_std_encoding *se,
127219019Sgabor    char *s, size_t n, _wc_t wc, size_t *nresult,
128219019Sgabor    struct iconv_hooks *hooks)
129219019Sgabor{
130219019Sgabor
131219019Sgabor	return (_stdenc_wctomb(se->se_handle, s, n, wc, se->se_ps, nresult,
132219019Sgabor			     hooks));
133219019Sgabor}
134219019Sgabor
135219019Sgaborstatic __inline int
136219019Sgaborput_state_resetx(struct _citrus_iconv_std_encoding *se, char *s, size_t n,
137219019Sgabor    size_t *nresult)
138219019Sgabor{
139219019Sgabor
140219019Sgabor	return (_stdenc_put_state_reset(se->se_handle, s, n, se->se_ps, nresult));
141219019Sgabor}
142219019Sgabor
143219019Sgaborstatic __inline int
144219019Sgaborget_state_desc_gen(struct _citrus_iconv_std_encoding *se, int *rstate)
145219019Sgabor{
146219019Sgabor	struct _stdenc_state_desc ssd;
147219019Sgabor	int ret;
148219019Sgabor
149219019Sgabor	ret = _stdenc_get_state_desc(se->se_handle, se->se_ps,
150219019Sgabor	    _STDENC_SDID_GENERIC, &ssd);
151219019Sgabor	if (!ret)
152219019Sgabor		*rstate = ssd.u.generic.state;
153219019Sgabor
154219019Sgabor	return (ret);
155219019Sgabor}
156219019Sgabor
157219019Sgabor/*
158219019Sgabor * init encoding context
159219019Sgabor */
160219019Sgaborstatic int
161219019Sgaborinit_encoding(struct _citrus_iconv_std_encoding *se, struct _stdenc *cs,
162219019Sgabor    void *ps1, void *ps2)
163219019Sgabor{
164219019Sgabor	int ret = -1;
165219019Sgabor
166219019Sgabor	se->se_handle = cs;
167219019Sgabor	se->se_ps = ps1;
168219019Sgabor	se->se_pssaved = ps2;
169219019Sgabor
170219019Sgabor	if (se->se_ps)
171219019Sgabor		ret = _stdenc_init_state(cs, se->se_ps);
172219019Sgabor	if (!ret && se->se_pssaved)
173219019Sgabor		ret = _stdenc_init_state(cs, se->se_pssaved);
174219019Sgabor
175219019Sgabor	return (ret);
176219019Sgabor}
177219019Sgabor
178219019Sgaborstatic int
179219019Sgaboropen_csmapper(struct _csmapper **rcm, const char *src, const char *dst,
180219019Sgabor    unsigned long *rnorm)
181219019Sgabor{
182219019Sgabor	struct _csmapper *cm;
183219019Sgabor	int ret;
184219019Sgabor
185219019Sgabor	ret = _csmapper_open(&cm, src, dst, 0, rnorm);
186219019Sgabor	if (ret)
187219019Sgabor		return (ret);
188219019Sgabor	if (_csmapper_get_src_max(cm) != 1 || _csmapper_get_dst_max(cm) != 1 ||
189219019Sgabor	    _csmapper_get_state_size(cm) != 0) {
190219019Sgabor		_csmapper_close(cm);
191219019Sgabor		return (EINVAL);
192219019Sgabor	}
193219019Sgabor
194219019Sgabor	*rcm = cm;
195219019Sgabor
196219019Sgabor	return (0);
197219019Sgabor}
198219019Sgabor
199219019Sgaborstatic void
200219019Sgaborclose_dsts(struct _citrus_iconv_std_dst_list *dl)
201219019Sgabor{
202219019Sgabor	struct _citrus_iconv_std_dst *sd;
203219019Sgabor
204219019Sgabor	while ((sd = TAILQ_FIRST(dl)) != NULL) {
205219019Sgabor		TAILQ_REMOVE(dl, sd, sd_entry);
206219019Sgabor		_csmapper_close(sd->sd_mapper);
207219019Sgabor		free(sd);
208219019Sgabor	}
209219019Sgabor}
210219019Sgabor
211219019Sgaborstatic int
212219019Sgaboropen_dsts(struct _citrus_iconv_std_dst_list *dl,
213219019Sgabor    const struct _esdb_charset *ec, const struct _esdb *dbdst)
214219019Sgabor{
215219019Sgabor	struct _citrus_iconv_std_dst *sd, *sdtmp;
216219019Sgabor	unsigned long norm;
217219019Sgabor	int i, ret;
218219019Sgabor
219219019Sgabor	sd = malloc(sizeof(*sd));
220219019Sgabor	if (sd == NULL)
221219019Sgabor		return (errno);
222219019Sgabor
223219019Sgabor	for (i = 0; i < dbdst->db_num_charsets; i++) {
224219019Sgabor		ret = open_csmapper(&sd->sd_mapper, ec->ec_csname,
225219019Sgabor		    dbdst->db_charsets[i].ec_csname, &norm);
226219019Sgabor		if (ret == 0) {
227219019Sgabor			sd->sd_csid = dbdst->db_charsets[i].ec_csid;
228219019Sgabor			sd->sd_norm = norm;
229219019Sgabor			/* insert this mapper by sorted order. */
230219019Sgabor			TAILQ_FOREACH(sdtmp, dl, sd_entry) {
231219019Sgabor				if (sdtmp->sd_norm > norm) {
232219019Sgabor					TAILQ_INSERT_BEFORE(sdtmp, sd,
233219019Sgabor					    sd_entry);
234219019Sgabor					sd = NULL;
235219019Sgabor					break;
236219019Sgabor				}
237219019Sgabor			}
238219019Sgabor			if (sd)
239219019Sgabor				TAILQ_INSERT_TAIL(dl, sd, sd_entry);
240219019Sgabor			sd = malloc(sizeof(*sd));
241219019Sgabor			if (sd == NULL) {
242219019Sgabor				ret = errno;
243219019Sgabor				close_dsts(dl);
244219019Sgabor				return (ret);
245219019Sgabor			}
246219019Sgabor		} else if (ret != ENOENT) {
247219019Sgabor			close_dsts(dl);
248219019Sgabor			free(sd);
249219019Sgabor			return (ret);
250219019Sgabor		}
251219019Sgabor	}
252219019Sgabor	free(sd);
253219019Sgabor	return (0);
254219019Sgabor}
255219019Sgabor
256219019Sgaborstatic void
257219019Sgaborclose_srcs(struct _citrus_iconv_std_src_list *sl)
258219019Sgabor{
259219019Sgabor	struct _citrus_iconv_std_src *ss;
260219019Sgabor
261219019Sgabor	while ((ss = TAILQ_FIRST(sl)) != NULL) {
262219019Sgabor		TAILQ_REMOVE(sl, ss, ss_entry);
263219019Sgabor		close_dsts(&ss->ss_dsts);
264219019Sgabor		free(ss);
265219019Sgabor	}
266219019Sgabor}
267219019Sgabor
268219019Sgaborstatic int
269219019Sgaboropen_srcs(struct _citrus_iconv_std_src_list *sl,
270219019Sgabor    const struct _esdb *dbsrc, const struct _esdb *dbdst)
271219019Sgabor{
272219019Sgabor	struct _citrus_iconv_std_src *ss;
273219019Sgabor	int count = 0, i, ret;
274219019Sgabor
275219019Sgabor	ss = malloc(sizeof(*ss));
276219019Sgabor	if (ss == NULL)
277219019Sgabor		return (errno);
278219019Sgabor
279219019Sgabor	TAILQ_INIT(&ss->ss_dsts);
280219019Sgabor
281219019Sgabor	for (i = 0; i < dbsrc->db_num_charsets; i++) {
282219019Sgabor		ret = open_dsts(&ss->ss_dsts, &dbsrc->db_charsets[i], dbdst);
283219019Sgabor		if (ret)
284219019Sgabor			goto err;
285219019Sgabor		if (!TAILQ_EMPTY(&ss->ss_dsts)) {
286219019Sgabor			ss->ss_csid = dbsrc->db_charsets[i].ec_csid;
287219019Sgabor			TAILQ_INSERT_TAIL(sl, ss, ss_entry);
288219019Sgabor			ss = malloc(sizeof(*ss));
289219019Sgabor			if (ss == NULL) {
290219019Sgabor				ret = errno;
291219019Sgabor				goto err;
292219019Sgabor			}
293219019Sgabor			count++;
294219019Sgabor			TAILQ_INIT(&ss->ss_dsts);
295219019Sgabor		}
296219019Sgabor	}
297219019Sgabor	free(ss);
298219019Sgabor
299219019Sgabor	return (count ? 0 : ENOENT);
300219019Sgabor
301219019Sgaborerr:
302219019Sgabor	free(ss);
303219019Sgabor	close_srcs(sl);
304219019Sgabor	return (ret);
305219019Sgabor}
306219019Sgabor
307219019Sgabor/* do convert a character */
308219019Sgabor#define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */
309219019Sgaborstatic int
310219019Sgabor/*ARGSUSED*/
311219019Sgabordo_conv(const struct _citrus_iconv_std_shared *is,
312219019Sgabor	_csid_t *csid, _index_t *idx)
313219019Sgabor{
314219019Sgabor	struct _citrus_iconv_std_dst *sd;
315219019Sgabor	struct _citrus_iconv_std_src *ss;
316219019Sgabor	_index_t tmpidx;
317219019Sgabor	int ret;
318219019Sgabor
319219019Sgabor	TAILQ_FOREACH(ss, &is->is_srcs, ss_entry) {
320219019Sgabor		if (ss->ss_csid == *csid) {
321219019Sgabor			TAILQ_FOREACH(sd, &ss->ss_dsts, sd_entry) {
322219019Sgabor				ret = _csmapper_convert(sd->sd_mapper,
323219019Sgabor				    &tmpidx, *idx, NULL);
324219019Sgabor				switch (ret) {
325219019Sgabor				case _MAPPER_CONVERT_SUCCESS:
326219019Sgabor					*csid = sd->sd_csid;
327219019Sgabor					*idx = tmpidx;
328219019Sgabor					return (0);
329219019Sgabor				case _MAPPER_CONVERT_NONIDENTICAL:
330219019Sgabor					break;
331219019Sgabor				case _MAPPER_CONVERT_SRC_MORE:
332219019Sgabor					/*FALLTHROUGH*/
333219019Sgabor				case _MAPPER_CONVERT_DST_MORE:
334219019Sgabor					/*FALLTHROUGH*/
335219019Sgabor				case _MAPPER_CONVERT_ILSEQ:
336219019Sgabor					return (EILSEQ);
337219019Sgabor				case _MAPPER_CONVERT_FATAL:
338219019Sgabor					return (EINVAL);
339219019Sgabor				}
340219019Sgabor			}
341219019Sgabor			break;
342219019Sgabor		}
343219019Sgabor	}
344219019Sgabor
345219019Sgabor	return (E_NO_CORRESPONDING_CHAR);
346219019Sgabor}
347219019Sgabor/* ---------------------------------------------------------------------- */
348219019Sgabor
349219019Sgaborstatic int
350219019Sgabor/*ARGSUSED*/
351219019Sgabor_citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared *ci,
352219019Sgabor    const char * __restrict src, const char * __restrict dst)
353219019Sgabor{
354219019Sgabor	struct _citrus_esdb esdbdst, esdbsrc;
355219019Sgabor	struct _citrus_iconv_std_shared *is;
356219019Sgabor	int ret;
357219019Sgabor
358219019Sgabor	is = malloc(sizeof(*is));
359219019Sgabor	if (is == NULL) {
360219019Sgabor		ret = errno;
361219019Sgabor		goto err0;
362219019Sgabor	}
363219019Sgabor	ret = _citrus_esdb_open(&esdbsrc, src);
364219019Sgabor	if (ret)
365219019Sgabor		goto err1;
366219019Sgabor	ret = _citrus_esdb_open(&esdbdst, dst);
367219019Sgabor	if (ret)
368219019Sgabor		goto err2;
369219019Sgabor	ret = _stdenc_open(&is->is_src_encoding, esdbsrc.db_encname,
370219019Sgabor	    esdbsrc.db_variable, esdbsrc.db_len_variable);
371219019Sgabor	if (ret)
372219019Sgabor		goto err3;
373219019Sgabor	ret = _stdenc_open(&is->is_dst_encoding, esdbdst.db_encname,
374219019Sgabor	    esdbdst.db_variable, esdbdst.db_len_variable);
375219019Sgabor	if (ret)
376219019Sgabor		goto err4;
377219019Sgabor	is->is_use_invalid = esdbdst.db_use_invalid;
378219019Sgabor	is->is_invalid = esdbdst.db_invalid;
379219019Sgabor
380219019Sgabor	TAILQ_INIT(&is->is_srcs);
381219019Sgabor	ret = open_srcs(&is->is_srcs, &esdbsrc, &esdbdst);
382219019Sgabor	if (ret)
383219019Sgabor		goto err5;
384219019Sgabor
385219019Sgabor	_esdb_close(&esdbsrc);
386219019Sgabor	_esdb_close(&esdbdst);
387219019Sgabor	ci->ci_closure = is;
388219019Sgabor
389219019Sgabor	return (0);
390219019Sgabor
391219019Sgaborerr5:
392219019Sgabor	_stdenc_close(is->is_dst_encoding);
393219019Sgaborerr4:
394219019Sgabor	_stdenc_close(is->is_src_encoding);
395219019Sgaborerr3:
396219019Sgabor	_esdb_close(&esdbdst);
397219019Sgaborerr2:
398219019Sgabor	_esdb_close(&esdbsrc);
399219019Sgaborerr1:
400219019Sgabor	free(is);
401219019Sgaborerr0:
402219019Sgabor	return (ret);
403219019Sgabor}
404219019Sgabor
405219019Sgaborstatic void
406219019Sgabor_citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared *ci)
407219019Sgabor{
408219019Sgabor	struct _citrus_iconv_std_shared *is = ci->ci_closure;
409219019Sgabor
410219019Sgabor	if (is == NULL)
411219019Sgabor		return;
412219019Sgabor
413219019Sgabor	_stdenc_close(is->is_src_encoding);
414219019Sgabor	_stdenc_close(is->is_dst_encoding);
415219019Sgabor	close_srcs(&is->is_srcs);
416219019Sgabor	free(is);
417219019Sgabor}
418219019Sgabor
419219019Sgaborstatic int
420219019Sgabor_citrus_iconv_std_iconv_init_context(struct _citrus_iconv *cv)
421219019Sgabor{
422219019Sgabor	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
423219019Sgabor	struct _citrus_iconv_std_context *sc;
424219019Sgabor	char *ptr;
425219019Sgabor	size_t sz, szpsdst, szpssrc;
426219019Sgabor
427219019Sgabor	szpssrc = _stdenc_get_state_size(is->is_src_encoding);
428219019Sgabor	szpsdst = _stdenc_get_state_size(is->is_dst_encoding);
429219019Sgabor
430219019Sgabor	sz = (szpssrc + szpsdst)*2 + sizeof(struct _citrus_iconv_std_context);
431219019Sgabor	sc = malloc(sz);
432219019Sgabor	if (sc == NULL)
433219019Sgabor		return (errno);
434219019Sgabor
435219019Sgabor	ptr = (char *)&sc[1];
436219019Sgabor	if (szpssrc > 0)
437219019Sgabor		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
438219019Sgabor		    ptr, ptr+szpssrc);
439219019Sgabor	else
440219019Sgabor		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
441219019Sgabor		    NULL, NULL);
442219019Sgabor	ptr += szpssrc*2;
443219019Sgabor	if (szpsdst > 0)
444219019Sgabor		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
445219019Sgabor		    ptr, ptr+szpsdst);
446219019Sgabor	else
447219019Sgabor		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
448219019Sgabor		    NULL, NULL);
449219019Sgabor
450219019Sgabor	cv->cv_closure = (void *)sc;
451219019Sgabor
452219019Sgabor	return (0);
453219019Sgabor}
454219019Sgabor
455219019Sgaborstatic void
456219019Sgabor_citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv)
457219019Sgabor{
458219019Sgabor
459219019Sgabor	free(cv->cv_closure);
460219019Sgabor}
461219019Sgabor
462219019Sgaborstatic int
463219019Sgabor_citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,
464252583Speter    const char * __restrict * __restrict in, size_t * __restrict inbytes,
465219019Sgabor    char * __restrict * __restrict out, size_t * __restrict outbytes,
466219019Sgabor    uint32_t flags, size_t * __restrict invalids)
467219019Sgabor{
468219019Sgabor	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
469219019Sgabor	struct _citrus_iconv_std_context *sc = cv->cv_closure;
470219019Sgabor	_csid_t csid;
471219019Sgabor	_index_t idx;
472252583Speter	const char *tmpin;
473219019Sgabor	size_t inval, szrin, szrout;
474219019Sgabor	int ret, state = 0;
475219019Sgabor
476219019Sgabor	inval = 0;
477219019Sgabor	if (in == NULL || *in == NULL) {
478219019Sgabor		/* special cases */
479219019Sgabor		if (out != NULL && *out != NULL) {
480219019Sgabor			/* init output state and store the shift sequence */
481219019Sgabor			save_encoding_state(&sc->sc_src_encoding);
482219019Sgabor			save_encoding_state(&sc->sc_dst_encoding);
483219019Sgabor			szrout = 0;
484219019Sgabor
485219019Sgabor			ret = put_state_resetx(&sc->sc_dst_encoding,
486219019Sgabor			    *out, *outbytes, &szrout);
487219019Sgabor			if (ret)
488219019Sgabor				goto err;
489219019Sgabor
490219019Sgabor			if (szrout == (size_t)-2) {
491219019Sgabor				/* too small to store the character */
492219019Sgabor				ret = EINVAL;
493219019Sgabor				goto err;
494219019Sgabor			}
495219019Sgabor			*out += szrout;
496219019Sgabor			*outbytes -= szrout;
497219019Sgabor		} else
498219019Sgabor			/* otherwise, discard the shift sequence */
499219019Sgabor			init_encoding_state(&sc->sc_dst_encoding);
500219019Sgabor		init_encoding_state(&sc->sc_src_encoding);
501219019Sgabor		*invalids = 0;
502219019Sgabor		return (0);
503219019Sgabor	}
504219019Sgabor
505219019Sgabor	/* normal case */
506219019Sgabor	for (;;) {
507219019Sgabor		if (*inbytes == 0) {
508219019Sgabor			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
509219019Sgabor			if (state == _STDENC_SDGEN_INITIAL ||
510219019Sgabor			    state == _STDENC_SDGEN_STABLE)
511219019Sgabor				break;
512219019Sgabor		}
513219019Sgabor
514219019Sgabor		/* save the encoding states for the error recovery */
515219019Sgabor		save_encoding_state(&sc->sc_src_encoding);
516219019Sgabor		save_encoding_state(&sc->sc_dst_encoding);
517219019Sgabor
518219019Sgabor		/* mb -> csid/index */
519219019Sgabor		tmpin = *in;
520219019Sgabor		szrin = szrout = 0;
521219019Sgabor		ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx, &tmpin,
522219019Sgabor		    *inbytes, &szrin, cv->cv_shared->ci_hooks);
523219019Sgabor		if (ret)
524219019Sgabor			goto err;
525219019Sgabor
526219019Sgabor		if (szrin == (size_t)-2) {
527219019Sgabor			/* incompleted character */
528219019Sgabor			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
529219019Sgabor			if (ret) {
530219019Sgabor				ret = EINVAL;
531219019Sgabor				goto err;
532219019Sgabor			}
533219019Sgabor			switch (state) {
534219019Sgabor			case _STDENC_SDGEN_INITIAL:
535219019Sgabor			case _STDENC_SDGEN_STABLE:
536219019Sgabor				/* fetch shift sequences only. */
537219019Sgabor				goto next;
538219019Sgabor			}
539219019Sgabor			ret = EINVAL;
540219019Sgabor			goto err;
541219019Sgabor		}
542219019Sgabor		/* convert the character */
543219019Sgabor		ret = do_conv(is, &csid, &idx);
544219019Sgabor		if (ret) {
545219019Sgabor			if (ret == E_NO_CORRESPONDING_CHAR) {
546219019Sgabor				inval++;
547219019Sgabor				szrout = 0;
548219019Sgabor				if ((((flags & _CITRUS_ICONV_F_HIDE_INVALID) == 0) &&
549219019Sgabor				    !cv->cv_shared->ci_discard_ilseq) &&
550219019Sgabor				    is->is_use_invalid) {
551219019Sgabor					ret = wctombx(&sc->sc_dst_encoding,
552219019Sgabor					    *out, *outbytes, is->is_invalid,
553219019Sgabor					    &szrout, cv->cv_shared->ci_hooks);
554219019Sgabor					if (ret)
555219019Sgabor						goto err;
556219019Sgabor				}
557219019Sgabor				goto next;
558219019Sgabor			} else
559219019Sgabor				goto err;
560219019Sgabor		}
561219019Sgabor		/* csid/index -> mb */
562219019Sgabor		ret = cstombx(&sc->sc_dst_encoding,
563219019Sgabor		    *out, *outbytes, csid, idx, &szrout,
564219019Sgabor		    cv->cv_shared->ci_hooks);
565219019Sgabor		if (ret)
566219019Sgabor			goto err;
567219019Sgabornext:
568219019Sgabor		*inbytes -= tmpin-*in; /* szrin is insufficient on \0. */
569219019Sgabor		*in = tmpin;
570219019Sgabor		*outbytes -= szrout;
571219019Sgabor		*out += szrout;
572219019Sgabor	}
573219019Sgabor	*invalids = inval;
574219019Sgabor
575219019Sgabor	return (0);
576219019Sgabor
577219019Sgaborerr:
578219019Sgabor	restore_encoding_state(&sc->sc_src_encoding);
579219019Sgabor	restore_encoding_state(&sc->sc_dst_encoding);
580219019Sgabor	*invalids = inval;
581219019Sgabor
582219019Sgabor	return (ret);
583219019Sgabor}
584