xref: /netbsd-src/lib/libc/citrus/modules/citrus_iconv_std.c (revision 6234e98d3fbe514f5ba7f4c69b52c1e517202b88)
1*6234e98dSwiz /*	$NetBSD: citrus_iconv_std.c,v 1.16 2012/02/12 13:51:29 wiz Exp $	*/
204e58308Stshiozak 
304e58308Stshiozak /*-
404e58308Stshiozak  * Copyright (c)2003 Citrus Project,
504e58308Stshiozak  * All rights reserved.
604e58308Stshiozak  *
704e58308Stshiozak  * Redistribution and use in source and binary forms, with or without
804e58308Stshiozak  * modification, are permitted provided that the following conditions
904e58308Stshiozak  * are met:
1004e58308Stshiozak  * 1. Redistributions of source code must retain the above copyright
1104e58308Stshiozak  *    notice, this list of conditions and the following disclaimer.
1204e58308Stshiozak  * 2. Redistributions in binary form must reproduce the above copyright
1304e58308Stshiozak  *    notice, this list of conditions and the following disclaimer in the
1404e58308Stshiozak  *    documentation and/or other materials provided with the distribution.
1504e58308Stshiozak  *
1604e58308Stshiozak  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1704e58308Stshiozak  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1804e58308Stshiozak  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1904e58308Stshiozak  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2004e58308Stshiozak  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2104e58308Stshiozak  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2204e58308Stshiozak  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2304e58308Stshiozak  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2404e58308Stshiozak  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2504e58308Stshiozak  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2604e58308Stshiozak  * SUCH DAMAGE.
2704e58308Stshiozak  */
2804e58308Stshiozak 
2904e58308Stshiozak #include <sys/cdefs.h>
3004e58308Stshiozak #if defined(LIBC_SCCS) && !defined(lint)
31*6234e98dSwiz __RCSID("$NetBSD: citrus_iconv_std.c,v 1.16 2012/02/12 13:51:29 wiz Exp $");
3204e58308Stshiozak #endif /* LIBC_SCCS and not lint */
3304e58308Stshiozak 
3404e58308Stshiozak #include <assert.h>
3504e58308Stshiozak #include <errno.h>
3604e58308Stshiozak #include <limits.h>
3704e58308Stshiozak #include <stdio.h>
3804e58308Stshiozak #include <stdlib.h>
3904e58308Stshiozak #include <string.h>
4086811edbSdogcow #include <machine/endian.h>
4104e58308Stshiozak #include <sys/queue.h>
4204e58308Stshiozak 
4304e58308Stshiozak #include "citrus_namespace.h"
4404e58308Stshiozak #include "citrus_types.h"
4504e58308Stshiozak #include "citrus_module.h"
4604e58308Stshiozak #include "citrus_region.h"
4704e58308Stshiozak #include "citrus_mmap.h"
4858db2e99Stshiozak #include "citrus_hash.h"
4904e58308Stshiozak #include "citrus_iconv.h"
5004e58308Stshiozak #include "citrus_stdenc.h"
5104e58308Stshiozak #include "citrus_mapper.h"
5204e58308Stshiozak #include "citrus_csmapper.h"
5304e58308Stshiozak #include "citrus_memstream.h"
5404e58308Stshiozak #include "citrus_iconv_std.h"
5504e58308Stshiozak #include "citrus_esdb.h"
5604e58308Stshiozak 
5704e58308Stshiozak /* ---------------------------------------------------------------------- */
5804e58308Stshiozak 
5904e58308Stshiozak _CITRUS_ICONV_DECLS(iconv_std);
6004e58308Stshiozak _CITRUS_ICONV_DEF_OPS(iconv_std);
6104e58308Stshiozak 
6204e58308Stshiozak 
6304e58308Stshiozak /* ---------------------------------------------------------------------- */
6404e58308Stshiozak 
6504e58308Stshiozak int
_citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops * ops,size_t lenops,u_int32_t expected_version)6604e58308Stshiozak _citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops *ops, size_t lenops,
6704e58308Stshiozak 			       u_int32_t expected_version)
6804e58308Stshiozak {
6904e58308Stshiozak 	if (expected_version<_CITRUS_ICONV_ABI_VERSION || lenops<sizeof(*ops))
7004e58308Stshiozak 		return (EINVAL);
7104e58308Stshiozak 
7204e58308Stshiozak 	memcpy(ops, &_citrus_iconv_std_iconv_ops,
7304e58308Stshiozak 	       sizeof(_citrus_iconv_std_iconv_ops));
7404e58308Stshiozak 
7504e58308Stshiozak 	return (0);
7604e58308Stshiozak }
7704e58308Stshiozak 
7804e58308Stshiozak /* ---------------------------------------------------------------------- */
7904e58308Stshiozak 
8004e58308Stshiozak /*
8104e58308Stshiozak  * convenience routines for stdenc.
8204e58308Stshiozak  */
8304e58308Stshiozak static __inline void
save_encoding_state(struct _citrus_iconv_std_encoding * se)8404e58308Stshiozak save_encoding_state(struct _citrus_iconv_std_encoding *se)
8504e58308Stshiozak {
8604e58308Stshiozak 	if (se->se_ps)
8704e58308Stshiozak 		memcpy(se->se_pssaved, se->se_ps,
8804e58308Stshiozak 		       _stdenc_get_state_size(se->se_handle));
8904e58308Stshiozak }
9004e58308Stshiozak 
9104e58308Stshiozak static __inline void
restore_encoding_state(struct _citrus_iconv_std_encoding * se)9204e58308Stshiozak restore_encoding_state(struct _citrus_iconv_std_encoding *se)
9304e58308Stshiozak {
9404e58308Stshiozak 	if (se->se_ps)
9504e58308Stshiozak 		memcpy(se->se_ps, se->se_pssaved,
9604e58308Stshiozak 		       _stdenc_get_state_size(se->se_handle));
9704e58308Stshiozak }
9804e58308Stshiozak 
9904e58308Stshiozak static __inline void
init_encoding_state(struct _citrus_iconv_std_encoding * se)10004e58308Stshiozak init_encoding_state(struct _citrus_iconv_std_encoding *se)
10104e58308Stshiozak {
10204e58308Stshiozak 	if (se->se_ps)
10304e58308Stshiozak 		_stdenc_init_state(se->se_handle, se->se_ps);
10404e58308Stshiozak }
10504e58308Stshiozak 
10604e58308Stshiozak static __inline int
mbtocsx(struct _citrus_iconv_std_encoding * se,_csid_t * csid,_index_t * idx,const char ** s,size_t n,size_t * nresult)10704e58308Stshiozak mbtocsx(struct _citrus_iconv_std_encoding *se,
10804e58308Stshiozak 	_csid_t *csid, _index_t *idx, const char **s, size_t n,
10904e58308Stshiozak 	size_t *nresult)
11004e58308Stshiozak {
11104e58308Stshiozak 	return _stdenc_mbtocs(se->se_handle, csid, idx, s, n, se->se_ps,
11204e58308Stshiozak 			      nresult);
11304e58308Stshiozak }
11404e58308Stshiozak 
11504e58308Stshiozak static __inline int
cstombx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,_csid_t csid,_index_t idx,size_t * nresult)11604e58308Stshiozak cstombx(struct _citrus_iconv_std_encoding *se,
11704e58308Stshiozak 	char *s, size_t n, _csid_t csid, _index_t idx, size_t *nresult)
11804e58308Stshiozak {
11904e58308Stshiozak 	return _stdenc_cstomb(se->se_handle, s, n, csid, idx, se->se_ps,
12004e58308Stshiozak 			      nresult);
12104e58308Stshiozak }
12204e58308Stshiozak 
12304e58308Stshiozak static __inline int
wctombx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,_wc_t wc,size_t * nresult)12404e58308Stshiozak wctombx(struct _citrus_iconv_std_encoding *se,
12504e58308Stshiozak 	char *s, size_t n, _wc_t wc, size_t *nresult)
12604e58308Stshiozak {
12704e58308Stshiozak 	return _stdenc_wctomb(se->se_handle, s, n, wc, se->se_ps, nresult);
12804e58308Stshiozak }
12904e58308Stshiozak 
130f3995f1aStshiozak static __inline int
put_state_resetx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,size_t * nresult)131f3995f1aStshiozak put_state_resetx(struct _citrus_iconv_std_encoding *se,
132f3995f1aStshiozak 		 char *s, size_t n, size_t *nresult)
133f3995f1aStshiozak {
134f3995f1aStshiozak 	return _stdenc_put_state_reset(se->se_handle, s, n, se->se_ps, nresult);
135f3995f1aStshiozak }
136f3995f1aStshiozak 
1371beef8feStshiozak static __inline int
get_state_desc_gen(struct _citrus_iconv_std_encoding * se,int * rstate)1381beef8feStshiozak get_state_desc_gen(struct _citrus_iconv_std_encoding *se, int *rstate)
1391beef8feStshiozak {
1401beef8feStshiozak 	int ret;
1411beef8feStshiozak 	struct _stdenc_state_desc ssd;
1421beef8feStshiozak 
1431beef8feStshiozak 	ret = _stdenc_get_state_desc(se->se_handle, se->se_ps,
1441beef8feStshiozak 				     _STDENC_SDID_GENERIC, &ssd);
1451beef8feStshiozak 	if (!ret)
1461beef8feStshiozak 		*rstate = ssd.u.generic.state;
1471beef8feStshiozak 
1481beef8feStshiozak 	return ret;
1491beef8feStshiozak }
1501beef8feStshiozak 
15104e58308Stshiozak /*
15258db2e99Stshiozak  * init encoding context
15304e58308Stshiozak  */
15458db2e99Stshiozak static int
init_encoding(struct _citrus_iconv_std_encoding * se,struct _stdenc * cs,void * ps1,void * ps2)15558db2e99Stshiozak init_encoding(struct _citrus_iconv_std_encoding *se, struct _stdenc *cs,
15658db2e99Stshiozak 	      void *ps1, void *ps2)
15704e58308Stshiozak {
158f174420eSchristos 	int ret = -1;
15904e58308Stshiozak 
16058db2e99Stshiozak 	se->se_handle = cs;
16158db2e99Stshiozak 	se->se_ps = ps1;
16258db2e99Stshiozak 	se->se_pssaved = ps2;
16304e58308Stshiozak 
16458db2e99Stshiozak 	if (se->se_ps)
16558db2e99Stshiozak 		ret = _stdenc_init_state(cs, se->se_ps);
16658db2e99Stshiozak 	if (!ret && se->se_pssaved)
16758db2e99Stshiozak 		ret = _stdenc_init_state(cs, se->se_pssaved);
16804e58308Stshiozak 
16904e58308Stshiozak 	return ret;
17004e58308Stshiozak }
17104e58308Stshiozak 
17204e58308Stshiozak static int
open_csmapper(struct _csmapper ** rcm,const char * src,const char * dst,unsigned long * rnorm)17304e58308Stshiozak open_csmapper(struct _csmapper **rcm, const char *src, const char *dst,
17404e58308Stshiozak 	      unsigned long *rnorm)
17504e58308Stshiozak {
17604e58308Stshiozak 	int ret;
17704e58308Stshiozak 	struct _csmapper *cm;
17804e58308Stshiozak 
17904e58308Stshiozak 	ret = _csmapper_open(&cm, src, dst, 0, rnorm);
18004e58308Stshiozak 	if (ret)
18104e58308Stshiozak 		return ret;
18204e58308Stshiozak 	if (_csmapper_get_src_max(cm) != 1 || _csmapper_get_dst_max(cm) != 1 ||
18304e58308Stshiozak 	    _csmapper_get_state_size(cm) != 0) {
18404e58308Stshiozak 		_csmapper_close(cm);
18504e58308Stshiozak 		return EINVAL;
18604e58308Stshiozak 	}
18704e58308Stshiozak 
18804e58308Stshiozak 	*rcm = cm;
18904e58308Stshiozak 
19004e58308Stshiozak 	return 0;
19104e58308Stshiozak }
19204e58308Stshiozak 
19304e58308Stshiozak static void
close_dsts(struct _citrus_iconv_std_dst_list * dl)19404e58308Stshiozak close_dsts(struct _citrus_iconv_std_dst_list *dl)
19504e58308Stshiozak {
19604e58308Stshiozak 	struct _citrus_iconv_std_dst *sd;
19704e58308Stshiozak 
19804e58308Stshiozak 	while ((sd=TAILQ_FIRST(dl)) != NULL) {
19904e58308Stshiozak 		TAILQ_REMOVE(dl, sd, sd_entry);
20004e58308Stshiozak 		_csmapper_close(sd->sd_mapper);
20104e58308Stshiozak 		free(sd);
20204e58308Stshiozak 	}
20304e58308Stshiozak }
20404e58308Stshiozak 
20504e58308Stshiozak static int
open_dsts(struct _citrus_iconv_std_dst_list * dl,const struct _esdb_charset * ec,const struct _esdb * dbdst)20604e58308Stshiozak open_dsts(struct _citrus_iconv_std_dst_list *dl,
207acdc3d1eSyamt 	  const struct _esdb_charset *ec, const struct _esdb *dbdst)
20804e58308Stshiozak {
20904e58308Stshiozak 	int i, ret;
21004e58308Stshiozak 	struct _citrus_iconv_std_dst *sd, *sdtmp;
21104e58308Stshiozak 	unsigned long norm;
21204e58308Stshiozak 
21304e58308Stshiozak 	sd = malloc(sizeof(*sd));
21404e58308Stshiozak 	if (sd == NULL)
21504e58308Stshiozak 		return errno;
21604e58308Stshiozak 
21704e58308Stshiozak 	for (i=0; i<dbdst->db_num_charsets; i++) {
21804e58308Stshiozak 		ret = open_csmapper(&sd->sd_mapper, ec->ec_csname,
21904e58308Stshiozak 				    dbdst->db_charsets[i].ec_csname, &norm);
22004e58308Stshiozak 		if (ret == 0) {
22104e58308Stshiozak 			sd->sd_csid = dbdst->db_charsets[i].ec_csid;
22204e58308Stshiozak 			sd->sd_norm = norm;
22304e58308Stshiozak 			/* insert this mapper by sorted order. */
22404e58308Stshiozak 			TAILQ_FOREACH(sdtmp, dl, sd_entry) {
22504e58308Stshiozak 				if (sdtmp->sd_norm > norm) {
22604e58308Stshiozak 					TAILQ_INSERT_BEFORE(sdtmp, sd,
22704e58308Stshiozak 							    sd_entry);
22804e58308Stshiozak 					sd = NULL;
22904e58308Stshiozak 					break;
23004e58308Stshiozak 				}
23104e58308Stshiozak 			}
23204e58308Stshiozak 			if (sd)
23304e58308Stshiozak 				TAILQ_INSERT_TAIL(dl, sd, sd_entry);
23404e58308Stshiozak 			sd = malloc(sizeof(*sd));
23504e58308Stshiozak 			if (sd == NULL) {
23604e58308Stshiozak 				ret = errno;
23704e58308Stshiozak 				close_dsts(dl);
23804e58308Stshiozak 				return ret;
23904e58308Stshiozak 			}
24004e58308Stshiozak 		} else if (ret != ENOENT) {
24104e58308Stshiozak 			close_dsts(dl);
24204e58308Stshiozak 			free(sd);
24304e58308Stshiozak 			return ret;
24404e58308Stshiozak 		}
24504e58308Stshiozak 	}
24604e58308Stshiozak 	free(sd);
24704e58308Stshiozak 	return 0;
24804e58308Stshiozak }
24904e58308Stshiozak 
25004e58308Stshiozak static void
close_srcs(struct _citrus_iconv_std_src_list * sl)25104e58308Stshiozak close_srcs(struct _citrus_iconv_std_src_list *sl)
25204e58308Stshiozak {
25304e58308Stshiozak 	struct _citrus_iconv_std_src *ss;
25404e58308Stshiozak 
25504e58308Stshiozak 	while ((ss=TAILQ_FIRST(sl)) != NULL) {
25604e58308Stshiozak 		TAILQ_REMOVE(sl, ss, ss_entry);
25704e58308Stshiozak 		close_dsts(&ss->ss_dsts);
25804e58308Stshiozak 		free(ss);
25904e58308Stshiozak 	}
26004e58308Stshiozak }
26104e58308Stshiozak 
26204e58308Stshiozak static int
open_srcs(struct _citrus_iconv_std_src_list * sl,const struct _esdb * dbsrc,const struct _esdb * dbdst)26304e58308Stshiozak open_srcs(struct _citrus_iconv_std_src_list *sl,
264acdc3d1eSyamt 	  const struct _esdb *dbsrc, const struct _esdb *dbdst)
26504e58308Stshiozak {
26604e58308Stshiozak 	int i, ret, count = 0;
26704e58308Stshiozak 	struct _citrus_iconv_std_src *ss;
26804e58308Stshiozak 
26904e58308Stshiozak 	ss = malloc(sizeof(*ss));
27004e58308Stshiozak 	if (ss == NULL)
27104e58308Stshiozak 		return errno;
27204e58308Stshiozak 
27304e58308Stshiozak 	TAILQ_INIT(&ss->ss_dsts);
27404e58308Stshiozak 
27504e58308Stshiozak 	for (i=0; i<dbsrc->db_num_charsets; i++) {
27604e58308Stshiozak 		ret = open_dsts(&ss->ss_dsts, &dbsrc->db_charsets[i], dbdst);
27704e58308Stshiozak 		if (ret)
27804e58308Stshiozak 			goto err;
27904e58308Stshiozak 		if (!TAILQ_EMPTY(&ss->ss_dsts)) {
28004e58308Stshiozak 			ss->ss_csid = dbsrc->db_charsets[i].ec_csid;
28104e58308Stshiozak 			TAILQ_INSERT_TAIL(sl, ss, ss_entry);
28204e58308Stshiozak 			ss = malloc(sizeof(*ss));
28304e58308Stshiozak 			if (ss == NULL) {
28404e58308Stshiozak 				ret = errno;
28504e58308Stshiozak 				goto err;
28604e58308Stshiozak 			}
28704e58308Stshiozak 			count++;
28804e58308Stshiozak 			TAILQ_INIT(&ss->ss_dsts);
28904e58308Stshiozak 		}
29004e58308Stshiozak 	}
29104e58308Stshiozak 	free(ss);
29204e58308Stshiozak 
29304e58308Stshiozak 	return count ? 0 : ENOENT;
29404e58308Stshiozak 
29504e58308Stshiozak err:
29604e58308Stshiozak 	free(ss);
29704e58308Stshiozak 	close_srcs(sl);
29804e58308Stshiozak 	return ret;
29904e58308Stshiozak }
30004e58308Stshiozak 
30104e58308Stshiozak /* do convert a character */
30204e58308Stshiozak #define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */
30304e58308Stshiozak static int
30458db2e99Stshiozak /*ARGSUSED*/
do_conv(const struct _citrus_iconv_std_shared * is,struct _citrus_iconv_std_context * sc,_csid_t * csid,_index_t * idx)3055b3d2342Syamt do_conv(const struct _citrus_iconv_std_shared *is,
30658db2e99Stshiozak 	struct _citrus_iconv_std_context *sc, _csid_t *csid, _index_t *idx)
30704e58308Stshiozak {
30804e58308Stshiozak 	_index_t tmpidx;
30904e58308Stshiozak 	int ret;
31004e58308Stshiozak 	struct _citrus_iconv_std_src *ss;
31104e58308Stshiozak 	struct _citrus_iconv_std_dst *sd;
31204e58308Stshiozak 
31304e58308Stshiozak 	TAILQ_FOREACH(ss, &is->is_srcs, ss_entry) {
31404e58308Stshiozak 		if (ss->ss_csid == *csid) {
31504e58308Stshiozak 			TAILQ_FOREACH(sd, &ss->ss_dsts, sd_entry) {
31604e58308Stshiozak 				ret = _csmapper_convert(sd->sd_mapper,
31704e58308Stshiozak 							&tmpidx, *idx, NULL);
31804e58308Stshiozak 				switch (ret) {
319c1e771abStshiozak 				case _MAPPER_CONVERT_SUCCESS:
32004e58308Stshiozak 					*csid = sd->sd_csid;
32104e58308Stshiozak 					*idx = tmpidx;
32204e58308Stshiozak 					return 0;
323c1e771abStshiozak 				case _MAPPER_CONVERT_NONIDENTICAL:
32404e58308Stshiozak 					break;
325c1e771abStshiozak 				case _MAPPER_CONVERT_SRC_MORE:
32604e58308Stshiozak 					/*FALLTHROUGH*/
327c1e771abStshiozak 				case _MAPPER_CONVERT_DST_MORE:
32804e58308Stshiozak 					/*FALLTHROUGH*/
329c1e771abStshiozak 				case _MAPPER_CONVERT_FATAL:
33004e58308Stshiozak 					return EINVAL;
331c1e771abStshiozak 				case _MAPPER_CONVERT_ILSEQ:
33204e58308Stshiozak 					return EILSEQ;
33304e58308Stshiozak 				}
33404e58308Stshiozak 			}
33504e58308Stshiozak 			break;
33604e58308Stshiozak 		}
33704e58308Stshiozak 	}
33804e58308Stshiozak 
33904e58308Stshiozak 	return E_NO_CORRESPONDING_CHAR;
34004e58308Stshiozak }
34104e58308Stshiozak /* ---------------------------------------------------------------------- */
34204e58308Stshiozak 
34304e58308Stshiozak static int
34404e58308Stshiozak /*ARGSUSED*/
_citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared * ci,const char * __restrict curdir,const char * __restrict src,const char * __restrict dst,const void * __restrict var,size_t lenvar)34558db2e99Stshiozak _citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared *ci,
34604e58308Stshiozak 				    const char * __restrict curdir,
34704e58308Stshiozak 				    const char * __restrict src,
34804e58308Stshiozak 				    const char * __restrict dst,
34904e58308Stshiozak 				    const void * __restrict var, size_t lenvar)
35004e58308Stshiozak {
35104e58308Stshiozak 	int ret;
35258db2e99Stshiozak 	struct _citrus_iconv_std_shared *is;
35304e58308Stshiozak 	struct _citrus_esdb esdbsrc, esdbdst;
35404e58308Stshiozak 
35504e58308Stshiozak 	is = malloc(sizeof(*is));
35604e58308Stshiozak 	if (is==NULL) {
35704e58308Stshiozak 		ret = errno;
35804e58308Stshiozak 		goto err0;
35904e58308Stshiozak 	}
36004e58308Stshiozak 	ret = _citrus_esdb_open(&esdbsrc, src);
36104e58308Stshiozak 	if (ret)
36204e58308Stshiozak 		goto err1;
36304e58308Stshiozak 	ret = _citrus_esdb_open(&esdbdst, dst);
36404e58308Stshiozak 	if (ret)
36504e58308Stshiozak 		goto err2;
36658db2e99Stshiozak 	ret = _stdenc_open(&is->is_src_encoding, esdbsrc.db_encname,
36758db2e99Stshiozak 			   esdbsrc.db_variable, esdbsrc.db_len_variable);
36804e58308Stshiozak 	if (ret)
36904e58308Stshiozak 		goto err3;
37058db2e99Stshiozak 	ret = _stdenc_open(&is->is_dst_encoding, esdbdst.db_encname,
37158db2e99Stshiozak 			   esdbdst.db_variable, esdbdst.db_len_variable);
37204e58308Stshiozak 	if (ret)
37304e58308Stshiozak 		goto err4;
37404e58308Stshiozak 	is->is_use_invalid = esdbdst.db_use_invalid;
37504e58308Stshiozak 	is->is_invalid = esdbdst.db_invalid;
37604e58308Stshiozak 
37704e58308Stshiozak 	TAILQ_INIT(&is->is_srcs);
37804e58308Stshiozak 	ret = open_srcs(&is->is_srcs, &esdbsrc, &esdbdst);
37904e58308Stshiozak 	if (ret)
38004e58308Stshiozak 		goto err5;
38104e58308Stshiozak 
38204e58308Stshiozak 	_esdb_close(&esdbsrc);
38304e58308Stshiozak 	_esdb_close(&esdbdst);
38404e58308Stshiozak 	ci->ci_closure = is;
38504e58308Stshiozak 
38604e58308Stshiozak 	return 0;
38704e58308Stshiozak 
38804e58308Stshiozak err5:
38958db2e99Stshiozak 	_stdenc_close(is->is_dst_encoding);
39004e58308Stshiozak err4:
39158db2e99Stshiozak 	_stdenc_close(is->is_src_encoding);
39204e58308Stshiozak err3:
39304e58308Stshiozak 	_esdb_close(&esdbdst);
39404e58308Stshiozak err2:
39504e58308Stshiozak 	_esdb_close(&esdbsrc);
39604e58308Stshiozak err1:
39704e58308Stshiozak 	free(is);
39804e58308Stshiozak err0:
39904e58308Stshiozak 	return ret;
40004e58308Stshiozak }
40104e58308Stshiozak 
40204e58308Stshiozak static void
_citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared * ci)40358db2e99Stshiozak _citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared *ci)
40404e58308Stshiozak {
40558db2e99Stshiozak 	struct _citrus_iconv_std_shared *is = ci->ci_closure;
40604e58308Stshiozak 
40758db2e99Stshiozak 	if (is == NULL)
40804e58308Stshiozak 		return;
40904e58308Stshiozak 
41058db2e99Stshiozak 	_stdenc_close(is->is_src_encoding);
41158db2e99Stshiozak 	_stdenc_close(is->is_dst_encoding);
41204e58308Stshiozak 	close_srcs(&is->is_srcs);
41304e58308Stshiozak 	free(is);
41404e58308Stshiozak }
41504e58308Stshiozak 
41604e58308Stshiozak static int
_citrus_iconv_std_iconv_init_context(struct _citrus_iconv * cv)41758db2e99Stshiozak _citrus_iconv_std_iconv_init_context(struct _citrus_iconv *cv)
41858db2e99Stshiozak {
4195b3d2342Syamt 	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
42058db2e99Stshiozak 	struct _citrus_iconv_std_context *sc;
42158db2e99Stshiozak 	size_t szpssrc, szpsdst, sz;
42258db2e99Stshiozak 	char *ptr;
42358db2e99Stshiozak 
42458db2e99Stshiozak 	szpssrc = _stdenc_get_state_size(is->is_src_encoding);
42558db2e99Stshiozak 	szpsdst = _stdenc_get_state_size(is->is_dst_encoding);
42658db2e99Stshiozak 
42758db2e99Stshiozak 	sz = (szpssrc + szpsdst)*2 + sizeof(struct _citrus_iconv_std_context);
42858db2e99Stshiozak 	sc = malloc(sz);
42958db2e99Stshiozak 	if (sc == NULL)
43058db2e99Stshiozak 		return errno;
43158db2e99Stshiozak 
43258db2e99Stshiozak 	ptr = (char *)&sc[1];
43358db2e99Stshiozak 	if (szpssrc)
43458db2e99Stshiozak 		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
43558db2e99Stshiozak 			      ptr, ptr+szpssrc);
43658db2e99Stshiozak 	else
43758db2e99Stshiozak 		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
43858db2e99Stshiozak 			      NULL, NULL);
43958db2e99Stshiozak 	ptr += szpssrc*2;
44058db2e99Stshiozak 	if (szpsdst)
44158db2e99Stshiozak 		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
44258db2e99Stshiozak 			      ptr, ptr+szpsdst);
44358db2e99Stshiozak 	else
44458db2e99Stshiozak 		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
44558db2e99Stshiozak 			      NULL, NULL);
44658db2e99Stshiozak 
44758db2e99Stshiozak 	cv->cv_closure = (void *)sc;
44858db2e99Stshiozak 
44958db2e99Stshiozak 	return 0;
45058db2e99Stshiozak }
45158db2e99Stshiozak 
45258db2e99Stshiozak static void
_citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv * cv)45358db2e99Stshiozak _citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv)
45458db2e99Stshiozak {
45558db2e99Stshiozak 	free(cv->cv_closure);
45658db2e99Stshiozak }
45758db2e99Stshiozak 
45858db2e99Stshiozak static int
_citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,const char * __restrict * __restrict in,size_t * __restrict inbytes,char * __restrict * __restrict out,size_t * __restrict outbytes,u_int32_t flags,size_t * __restrict invalids)45958db2e99Stshiozak _citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,
46004e58308Stshiozak 				const char * __restrict * __restrict in,
46104e58308Stshiozak 				size_t * __restrict inbytes,
46204e58308Stshiozak 				char * __restrict * __restrict out,
46304e58308Stshiozak 				size_t * __restrict outbytes, u_int32_t flags,
46404e58308Stshiozak 				size_t * __restrict invalids)
46504e58308Stshiozak {
4665b3d2342Syamt 	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
46758db2e99Stshiozak 	struct _citrus_iconv_std_context *sc = cv->cv_closure;
46804e58308Stshiozak 	_index_t idx;
46904e58308Stshiozak 	_csid_t csid;
4701beef8feStshiozak 	int ret, state;
47104e58308Stshiozak 	size_t szrin, szrout;
47204e58308Stshiozak 	size_t inval;
47304e58308Stshiozak 	const char *tmpin;
47404e58308Stshiozak 
47504e58308Stshiozak 	inval = 0;
47604e58308Stshiozak 	if (in==NULL || *in==NULL) {
47704e58308Stshiozak 		/* special cases */
47804e58308Stshiozak 		if (out!=NULL && *out!=NULL) {
479cf33db0fStshiozak 			/* init output state and store the shift sequence */
48058db2e99Stshiozak 			save_encoding_state(&sc->sc_src_encoding);
48158db2e99Stshiozak 			save_encoding_state(&sc->sc_dst_encoding);
48204e58308Stshiozak 			szrout = 0;
48304e58308Stshiozak 
48458db2e99Stshiozak 			ret = put_state_resetx(&sc->sc_dst_encoding,
48504e58308Stshiozak 					       *out, *outbytes,
486f3995f1aStshiozak 					       &szrout);
48704e58308Stshiozak 			if (ret)
48804e58308Stshiozak 				goto err;
48904e58308Stshiozak 
49004e58308Stshiozak 			if (szrout == (size_t)-2) {
49104e58308Stshiozak 				/* too small to store the character */
49204e58308Stshiozak 				ret = EINVAL;
49304e58308Stshiozak 				goto err;
49404e58308Stshiozak 			}
49504e58308Stshiozak 			*out += szrout;
49604e58308Stshiozak 			*outbytes -= szrout;
497cf33db0fStshiozak 		} else
498cf33db0fStshiozak 			/* otherwise, discard the shift sequence */
499cf33db0fStshiozak 			init_encoding_state(&sc->sc_dst_encoding);
50058db2e99Stshiozak 		init_encoding_state(&sc->sc_src_encoding);
501cf33db0fStshiozak 		*invalids = 0;
50204e58308Stshiozak 		return 0;
50304e58308Stshiozak 	}
50404e58308Stshiozak 
50504e58308Stshiozak 	/* normal case */
50604e58308Stshiozak 	for (;;) {
50779a70a82Stnozaki 		if (*inbytes==0) {
50879a70a82Stnozaki 			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
50974fca02cStnozaki 			if (state == _STDENC_SDGEN_INITIAL ||
51074fca02cStnozaki 			    state == _STDENC_SDGEN_STABLE)
5119a4bbff8Syamt 				break;
51279a70a82Stnozaki 		}
5139a4bbff8Syamt 
51404e58308Stshiozak 		/* save the encoding states for the error recovery */
51558db2e99Stshiozak 		save_encoding_state(&sc->sc_src_encoding);
51658db2e99Stshiozak 		save_encoding_state(&sc->sc_dst_encoding);
51704e58308Stshiozak 
51804e58308Stshiozak 		/* mb -> csid/index */
51904e58308Stshiozak 		tmpin = *in;
52004e58308Stshiozak 		szrin = szrout = 0;
52158db2e99Stshiozak 		ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx,
52204e58308Stshiozak 			      &tmpin, *inbytes, &szrin);
52304e58308Stshiozak 		if (ret)
52404e58308Stshiozak 			goto err;
52504e58308Stshiozak 
52604e58308Stshiozak 		if (szrin == (size_t)-2) {
52704e58308Stshiozak 			/* incompleted character */
5281beef8feStshiozak 			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
5291beef8feStshiozak 			if (ret) {
5301beef8feStshiozak 				ret = EINVAL;
5311beef8feStshiozak 				goto err;
5321beef8feStshiozak 			}
5331beef8feStshiozak 			switch (state) {
5341beef8feStshiozak 			case _STDENC_SDGEN_INITIAL:
5351beef8feStshiozak 			case _STDENC_SDGEN_STABLE:
5361beef8feStshiozak 				/* fetch shift sequences only. */
5371beef8feStshiozak 				goto next;
5381beef8feStshiozak 			}
53904e58308Stshiozak 			ret = EINVAL;
54004e58308Stshiozak 			goto err;
54104e58308Stshiozak 		}
54204e58308Stshiozak 		/* convert the character */
54358db2e99Stshiozak 		ret = do_conv(is, sc, &csid, &idx);
54404e58308Stshiozak 		if (ret) {
54504e58308Stshiozak 			if (ret == E_NO_CORRESPONDING_CHAR) {
54604e58308Stshiozak 				inval++;
547f3995f1aStshiozak 				szrout = 0;
54804e58308Stshiozak 				if ((flags&_CITRUS_ICONV_F_HIDE_INVALID)==0 &&
54904e58308Stshiozak 				    is->is_use_invalid) {
55058db2e99Stshiozak 					ret = wctombx(&sc->sc_dst_encoding,
55104e58308Stshiozak 						      *out, *outbytes,
55204e58308Stshiozak 						      is->is_invalid,
55304e58308Stshiozak 						      &szrout);
55404e58308Stshiozak 					if (ret)
55504e58308Stshiozak 						goto err;
55604e58308Stshiozak 				}
55704e58308Stshiozak 				goto next;
55804e58308Stshiozak 			} else {
55904e58308Stshiozak 				goto err;
56004e58308Stshiozak 			}
56104e58308Stshiozak 		}
56204e58308Stshiozak 		/* csid/index -> mb */
56358db2e99Stshiozak 		ret = cstombx(&sc->sc_dst_encoding,
56404e58308Stshiozak 			      *out, *outbytes, csid, idx, &szrout);
56504e58308Stshiozak 		if (ret)
56604e58308Stshiozak 			goto err;
56704e58308Stshiozak next:
56804e58308Stshiozak 		_DIAGASSERT(*inbytes>=szrin && *outbytes>=szrout);
56904e58308Stshiozak 		*inbytes -= tmpin-*in; /* szrin is insufficient on \0. */
57004e58308Stshiozak 		*in = tmpin;
57104e58308Stshiozak 		*outbytes -= szrout;
57204e58308Stshiozak 		*out += szrout;
57304e58308Stshiozak 	}
57404e58308Stshiozak 	*invalids = inval;
57504e58308Stshiozak 
57604e58308Stshiozak 	return 0;
57704e58308Stshiozak 
57804e58308Stshiozak err:
57958db2e99Stshiozak 	restore_encoding_state(&sc->sc_src_encoding);
58058db2e99Stshiozak 	restore_encoding_state(&sc->sc_dst_encoding);
58104e58308Stshiozak err_norestore:
58204e58308Stshiozak 	*invalids = inval;
58304e58308Stshiozak 
58404e58308Stshiozak 	return ret;
58504e58308Stshiozak }
586