1*0db70a6aSJohn Marino /* $FreeBSD: head/lib/libiconv_modules/iconv_std/citrus_iconv_std.c 281550 2015-04-15 09:09:20Z tijl $ */
2*0db70a6aSJohn Marino /*	$NetBSD: citrus_iconv_std.c,v 1.16 2012/02/12 13:51:29 wiz Exp $	*/
30d5acd74SJohn Marino 
40d5acd74SJohn Marino /*-
50d5acd74SJohn Marino  * Copyright (c)2003 Citrus Project,
60d5acd74SJohn Marino  * All rights reserved.
70d5acd74SJohn Marino  *
80d5acd74SJohn Marino  * Redistribution and use in source and binary forms, with or without
90d5acd74SJohn Marino  * modification, are permitted provided that the following conditions
100d5acd74SJohn Marino  * are met:
110d5acd74SJohn Marino  * 1. Redistributions of source code must retain the above copyright
120d5acd74SJohn Marino  *    notice, this list of conditions and the following disclaimer.
130d5acd74SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
140d5acd74SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
150d5acd74SJohn Marino  *    documentation and/or other materials provided with the distribution.
160d5acd74SJohn Marino  *
170d5acd74SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
180d5acd74SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
190d5acd74SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
200d5acd74SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
210d5acd74SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
220d5acd74SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
230d5acd74SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
240d5acd74SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
250d5acd74SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
260d5acd74SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
270d5acd74SJohn Marino  * SUCH DAMAGE.
280d5acd74SJohn Marino  */
290d5acd74SJohn Marino 
300d5acd74SJohn Marino #include <sys/cdefs.h>
310d5acd74SJohn Marino #include <sys/endian.h>
320d5acd74SJohn Marino #include <sys/queue.h>
330d5acd74SJohn Marino 
340d5acd74SJohn Marino #include <assert.h>
350d5acd74SJohn Marino #include <errno.h>
360d5acd74SJohn Marino #include <limits.h>
370d5acd74SJohn Marino #include <stdbool.h>
380d5acd74SJohn Marino #include <stdio.h>
390d5acd74SJohn Marino #include <stdlib.h>
400d5acd74SJohn Marino #include <string.h>
410d5acd74SJohn Marino 
420d5acd74SJohn Marino #include "citrus_namespace.h"
430d5acd74SJohn Marino #include "citrus_types.h"
440d5acd74SJohn Marino #include "citrus_module.h"
450d5acd74SJohn Marino #include "citrus_region.h"
460d5acd74SJohn Marino #include "citrus_mmap.h"
470d5acd74SJohn Marino #include "citrus_hash.h"
480d5acd74SJohn Marino #include "citrus_iconv.h"
490d5acd74SJohn Marino #include "citrus_stdenc.h"
500d5acd74SJohn Marino #include "citrus_mapper.h"
510d5acd74SJohn Marino #include "citrus_csmapper.h"
520d5acd74SJohn Marino #include "citrus_memstream.h"
530d5acd74SJohn Marino #include "citrus_iconv_std.h"
540d5acd74SJohn Marino #include "citrus_esdb.h"
550d5acd74SJohn Marino 
560d5acd74SJohn Marino /* ---------------------------------------------------------------------- */
570d5acd74SJohn Marino 
580d5acd74SJohn Marino _CITRUS_ICONV_DECLS(iconv_std);
590d5acd74SJohn Marino _CITRUS_ICONV_DEF_OPS(iconv_std);
600d5acd74SJohn Marino 
610d5acd74SJohn Marino 
620d5acd74SJohn Marino /* ---------------------------------------------------------------------- */
630d5acd74SJohn Marino 
640d5acd74SJohn Marino int
_citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops * ops)650d5acd74SJohn Marino _citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops *ops)
660d5acd74SJohn Marino {
670d5acd74SJohn Marino 
680d5acd74SJohn Marino 	memcpy(ops, &_citrus_iconv_std_iconv_ops,
690d5acd74SJohn Marino 	    sizeof(_citrus_iconv_std_iconv_ops));
700d5acd74SJohn Marino 
710d5acd74SJohn Marino 	return (0);
720d5acd74SJohn Marino }
730d5acd74SJohn Marino 
740d5acd74SJohn Marino /* ---------------------------------------------------------------------- */
750d5acd74SJohn Marino 
760d5acd74SJohn Marino /*
770d5acd74SJohn Marino  * convenience routines for stdenc.
780d5acd74SJohn Marino  */
790d5acd74SJohn Marino static __inline void
save_encoding_state(struct _citrus_iconv_std_encoding * se)800d5acd74SJohn Marino save_encoding_state(struct _citrus_iconv_std_encoding *se)
810d5acd74SJohn Marino {
820d5acd74SJohn Marino 
830d5acd74SJohn Marino 	if (se->se_ps)
840d5acd74SJohn Marino 		memcpy(se->se_pssaved, se->se_ps,
850d5acd74SJohn Marino 		    _stdenc_get_state_size(se->se_handle));
860d5acd74SJohn Marino }
870d5acd74SJohn Marino 
880d5acd74SJohn Marino static __inline void
restore_encoding_state(struct _citrus_iconv_std_encoding * se)890d5acd74SJohn Marino restore_encoding_state(struct _citrus_iconv_std_encoding *se)
900d5acd74SJohn Marino {
910d5acd74SJohn Marino 
920d5acd74SJohn Marino 	if (se->se_ps)
930d5acd74SJohn Marino 		memcpy(se->se_ps, se->se_pssaved,
940d5acd74SJohn Marino 		    _stdenc_get_state_size(se->se_handle));
950d5acd74SJohn Marino }
960d5acd74SJohn Marino 
970d5acd74SJohn Marino static __inline void
init_encoding_state(struct _citrus_iconv_std_encoding * se)980d5acd74SJohn Marino init_encoding_state(struct _citrus_iconv_std_encoding *se)
990d5acd74SJohn Marino {
1000d5acd74SJohn Marino 
1010d5acd74SJohn Marino 	if (se->se_ps)
1020d5acd74SJohn Marino 		_stdenc_init_state(se->se_handle, se->se_ps);
1030d5acd74SJohn Marino }
1040d5acd74SJohn Marino 
1050d5acd74SJohn Marino static __inline int
mbtocsx(struct _citrus_iconv_std_encoding * se,_csid_t * csid,_index_t * idx,char ** s,size_t n,size_t * nresult,struct iconv_hooks * hooks)1060d5acd74SJohn Marino mbtocsx(struct _citrus_iconv_std_encoding *se,
107*0db70a6aSJohn Marino     _csid_t *csid, _index_t *idx, char **s, size_t n, size_t *nresult,
1080d5acd74SJohn Marino     struct iconv_hooks *hooks)
1090d5acd74SJohn Marino {
1100d5acd74SJohn Marino 
1110d5acd74SJohn Marino 	return (_stdenc_mbtocs(se->se_handle, csid, idx, s, n, se->se_ps,
1120d5acd74SJohn Marino 			      nresult, hooks));
1130d5acd74SJohn Marino }
1140d5acd74SJohn Marino 
1150d5acd74SJohn Marino static __inline int
cstombx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,_csid_t csid,_index_t idx,size_t * nresult,struct iconv_hooks * hooks)1160d5acd74SJohn Marino cstombx(struct _citrus_iconv_std_encoding *se,
1170d5acd74SJohn Marino     char *s, size_t n, _csid_t csid, _index_t idx, size_t *nresult,
1180d5acd74SJohn Marino     struct iconv_hooks *hooks)
1190d5acd74SJohn Marino {
1200d5acd74SJohn Marino 
1210d5acd74SJohn Marino 	return (_stdenc_cstomb(se->se_handle, s, n, csid, idx, se->se_ps,
1220d5acd74SJohn Marino 			      nresult, hooks));
1230d5acd74SJohn Marino }
1240d5acd74SJohn Marino 
1250d5acd74SJohn Marino static __inline int
wctombx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,_wc_t wc,size_t * nresult,struct iconv_hooks * hooks)1260d5acd74SJohn Marino wctombx(struct _citrus_iconv_std_encoding *se,
1270d5acd74SJohn Marino     char *s, size_t n, _wc_t wc, size_t *nresult,
1280d5acd74SJohn Marino     struct iconv_hooks *hooks)
1290d5acd74SJohn Marino {
1300d5acd74SJohn Marino 
1310d5acd74SJohn Marino 	return (_stdenc_wctomb(se->se_handle, s, n, wc, se->se_ps, nresult,
1320d5acd74SJohn Marino 			     hooks));
1330d5acd74SJohn Marino }
1340d5acd74SJohn Marino 
1350d5acd74SJohn Marino static __inline int
put_state_resetx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,size_t * nresult)1360d5acd74SJohn Marino put_state_resetx(struct _citrus_iconv_std_encoding *se, char *s, size_t n,
1370d5acd74SJohn Marino     size_t *nresult)
1380d5acd74SJohn Marino {
1390d5acd74SJohn Marino 
1400d5acd74SJohn Marino 	return (_stdenc_put_state_reset(se->se_handle, s, n, se->se_ps, nresult));
1410d5acd74SJohn Marino }
1420d5acd74SJohn Marino 
1430d5acd74SJohn Marino static __inline int
get_state_desc_gen(struct _citrus_iconv_std_encoding * se,int * rstate)1440d5acd74SJohn Marino get_state_desc_gen(struct _citrus_iconv_std_encoding *se, int *rstate)
1450d5acd74SJohn Marino {
1460d5acd74SJohn Marino 	struct _stdenc_state_desc ssd;
1470d5acd74SJohn Marino 	int ret;
1480d5acd74SJohn Marino 
1490d5acd74SJohn Marino 	ret = _stdenc_get_state_desc(se->se_handle, se->se_ps,
1500d5acd74SJohn Marino 	    _STDENC_SDID_GENERIC, &ssd);
1510d5acd74SJohn Marino 	if (!ret)
1520d5acd74SJohn Marino 		*rstate = ssd.u.generic.state;
1530d5acd74SJohn Marino 
1540d5acd74SJohn Marino 	return (ret);
1550d5acd74SJohn Marino }
1560d5acd74SJohn Marino 
1570d5acd74SJohn Marino /*
1580d5acd74SJohn Marino  * init encoding context
1590d5acd74SJohn Marino  */
1600d5acd74SJohn Marino static int
init_encoding(struct _citrus_iconv_std_encoding * se,struct _stdenc * cs,void * ps1,void * ps2)1610d5acd74SJohn Marino init_encoding(struct _citrus_iconv_std_encoding *se, struct _stdenc *cs,
1620d5acd74SJohn Marino     void *ps1, void *ps2)
1630d5acd74SJohn Marino {
1640d5acd74SJohn Marino 	int ret = -1;
1650d5acd74SJohn Marino 
1660d5acd74SJohn Marino 	se->se_handle = cs;
1670d5acd74SJohn Marino 	se->se_ps = ps1;
1680d5acd74SJohn Marino 	se->se_pssaved = ps2;
1690d5acd74SJohn Marino 
1700d5acd74SJohn Marino 	if (se->se_ps)
1710d5acd74SJohn Marino 		ret = _stdenc_init_state(cs, se->se_ps);
1720d5acd74SJohn Marino 	if (!ret && se->se_pssaved)
1730d5acd74SJohn Marino 		ret = _stdenc_init_state(cs, se->se_pssaved);
1740d5acd74SJohn Marino 
1750d5acd74SJohn Marino 	return (ret);
1760d5acd74SJohn Marino }
1770d5acd74SJohn Marino 
1780d5acd74SJohn Marino static int
open_csmapper(struct _csmapper ** rcm,const char * src,const char * dst,unsigned long * rnorm)1790d5acd74SJohn Marino open_csmapper(struct _csmapper **rcm, const char *src, const char *dst,
1800d5acd74SJohn Marino     unsigned long *rnorm)
1810d5acd74SJohn Marino {
1820d5acd74SJohn Marino 	struct _csmapper *cm;
1830d5acd74SJohn Marino 	int ret;
1840d5acd74SJohn Marino 
1850d5acd74SJohn Marino 	ret = _csmapper_open(&cm, src, dst, 0, rnorm);
1860d5acd74SJohn Marino 	if (ret)
1870d5acd74SJohn Marino 		return (ret);
1880d5acd74SJohn Marino 	if (_csmapper_get_src_max(cm) != 1 || _csmapper_get_dst_max(cm) != 1 ||
1890d5acd74SJohn Marino 	    _csmapper_get_state_size(cm) != 0) {
1900d5acd74SJohn Marino 		_csmapper_close(cm);
1910d5acd74SJohn Marino 		return (EINVAL);
1920d5acd74SJohn Marino 	}
1930d5acd74SJohn Marino 
1940d5acd74SJohn Marino 	*rcm = cm;
1950d5acd74SJohn Marino 
1960d5acd74SJohn Marino 	return (0);
1970d5acd74SJohn Marino }
1980d5acd74SJohn Marino 
1990d5acd74SJohn Marino static void
close_dsts(struct _citrus_iconv_std_dst_list * dl)2000d5acd74SJohn Marino close_dsts(struct _citrus_iconv_std_dst_list *dl)
2010d5acd74SJohn Marino {
2020d5acd74SJohn Marino 	struct _citrus_iconv_std_dst *sd;
2030d5acd74SJohn Marino 
2040d5acd74SJohn Marino 	while ((sd = TAILQ_FIRST(dl)) != NULL) {
2050d5acd74SJohn Marino 		TAILQ_REMOVE(dl, sd, sd_entry);
2060d5acd74SJohn Marino 		_csmapper_close(sd->sd_mapper);
2070d5acd74SJohn Marino 		free(sd);
2080d5acd74SJohn Marino 	}
2090d5acd74SJohn Marino }
2100d5acd74SJohn Marino 
2110d5acd74SJohn Marino static int
open_dsts(struct _citrus_iconv_std_dst_list * dl,const struct _esdb_charset * ec,const struct _esdb * dbdst)2120d5acd74SJohn Marino open_dsts(struct _citrus_iconv_std_dst_list *dl,
2130d5acd74SJohn Marino     const struct _esdb_charset *ec, const struct _esdb *dbdst)
2140d5acd74SJohn Marino {
2150d5acd74SJohn Marino 	struct _citrus_iconv_std_dst *sd, *sdtmp;
2160d5acd74SJohn Marino 	unsigned long norm;
2170d5acd74SJohn Marino 	int i, ret;
2180d5acd74SJohn Marino 
2190d5acd74SJohn Marino 	sd = malloc(sizeof(*sd));
2200d5acd74SJohn Marino 	if (sd == NULL)
2210d5acd74SJohn Marino 		return (errno);
2220d5acd74SJohn Marino 
2230d5acd74SJohn Marino 	for (i = 0; i < dbdst->db_num_charsets; i++) {
2240d5acd74SJohn Marino 		ret = open_csmapper(&sd->sd_mapper, ec->ec_csname,
2250d5acd74SJohn Marino 		    dbdst->db_charsets[i].ec_csname, &norm);
2260d5acd74SJohn Marino 		if (ret == 0) {
2270d5acd74SJohn Marino 			sd->sd_csid = dbdst->db_charsets[i].ec_csid;
2280d5acd74SJohn Marino 			sd->sd_norm = norm;
2290d5acd74SJohn Marino 			/* insert this mapper by sorted order. */
2300d5acd74SJohn Marino 			TAILQ_FOREACH(sdtmp, dl, sd_entry) {
2310d5acd74SJohn Marino 				if (sdtmp->sd_norm > norm) {
2320d5acd74SJohn Marino 					TAILQ_INSERT_BEFORE(sdtmp, sd,
2330d5acd74SJohn Marino 					    sd_entry);
2340d5acd74SJohn Marino 					sd = NULL;
2350d5acd74SJohn Marino 					break;
2360d5acd74SJohn Marino 				}
2370d5acd74SJohn Marino 			}
2380d5acd74SJohn Marino 			if (sd)
2390d5acd74SJohn Marino 				TAILQ_INSERT_TAIL(dl, sd, sd_entry);
2400d5acd74SJohn Marino 			sd = malloc(sizeof(*sd));
2410d5acd74SJohn Marino 			if (sd == NULL) {
2420d5acd74SJohn Marino 				ret = errno;
2430d5acd74SJohn Marino 				close_dsts(dl);
2440d5acd74SJohn Marino 				return (ret);
2450d5acd74SJohn Marino 			}
2460d5acd74SJohn Marino 		} else if (ret != ENOENT) {
2470d5acd74SJohn Marino 			close_dsts(dl);
2480d5acd74SJohn Marino 			free(sd);
2490d5acd74SJohn Marino 			return (ret);
2500d5acd74SJohn Marino 		}
2510d5acd74SJohn Marino 	}
2520d5acd74SJohn Marino 	free(sd);
2530d5acd74SJohn Marino 	return (0);
2540d5acd74SJohn Marino }
2550d5acd74SJohn Marino 
2560d5acd74SJohn Marino static void
close_srcs(struct _citrus_iconv_std_src_list * sl)2570d5acd74SJohn Marino close_srcs(struct _citrus_iconv_std_src_list *sl)
2580d5acd74SJohn Marino {
2590d5acd74SJohn Marino 	struct _citrus_iconv_std_src *ss;
2600d5acd74SJohn Marino 
2610d5acd74SJohn Marino 	while ((ss = TAILQ_FIRST(sl)) != NULL) {
2620d5acd74SJohn Marino 		TAILQ_REMOVE(sl, ss, ss_entry);
2630d5acd74SJohn Marino 		close_dsts(&ss->ss_dsts);
2640d5acd74SJohn Marino 		free(ss);
2650d5acd74SJohn Marino 	}
2660d5acd74SJohn Marino }
2670d5acd74SJohn Marino 
2680d5acd74SJohn Marino static int
open_srcs(struct _citrus_iconv_std_src_list * sl,const struct _esdb * dbsrc,const struct _esdb * dbdst)2690d5acd74SJohn Marino open_srcs(struct _citrus_iconv_std_src_list *sl,
2700d5acd74SJohn Marino     const struct _esdb *dbsrc, const struct _esdb *dbdst)
2710d5acd74SJohn Marino {
2720d5acd74SJohn Marino 	struct _citrus_iconv_std_src *ss;
2730d5acd74SJohn Marino 	int count = 0, i, ret;
2740d5acd74SJohn Marino 
2750d5acd74SJohn Marino 	ss = malloc(sizeof(*ss));
2760d5acd74SJohn Marino 	if (ss == NULL)
2770d5acd74SJohn Marino 		return (errno);
2780d5acd74SJohn Marino 
2790d5acd74SJohn Marino 	TAILQ_INIT(&ss->ss_dsts);
2800d5acd74SJohn Marino 
2810d5acd74SJohn Marino 	for (i = 0; i < dbsrc->db_num_charsets; i++) {
2820d5acd74SJohn Marino 		ret = open_dsts(&ss->ss_dsts, &dbsrc->db_charsets[i], dbdst);
2830d5acd74SJohn Marino 		if (ret)
2840d5acd74SJohn Marino 			goto err;
2850d5acd74SJohn Marino 		if (!TAILQ_EMPTY(&ss->ss_dsts)) {
2860d5acd74SJohn Marino 			ss->ss_csid = dbsrc->db_charsets[i].ec_csid;
2870d5acd74SJohn Marino 			TAILQ_INSERT_TAIL(sl, ss, ss_entry);
2880d5acd74SJohn Marino 			ss = malloc(sizeof(*ss));
2890d5acd74SJohn Marino 			if (ss == NULL) {
2900d5acd74SJohn Marino 				ret = errno;
2910d5acd74SJohn Marino 				goto err;
2920d5acd74SJohn Marino 			}
2930d5acd74SJohn Marino 			count++;
2940d5acd74SJohn Marino 			TAILQ_INIT(&ss->ss_dsts);
2950d5acd74SJohn Marino 		}
2960d5acd74SJohn Marino 	}
2970d5acd74SJohn Marino 	free(ss);
2980d5acd74SJohn Marino 
2990d5acd74SJohn Marino 	return (count ? 0 : ENOENT);
3000d5acd74SJohn Marino 
3010d5acd74SJohn Marino err:
3020d5acd74SJohn Marino 	free(ss);
3030d5acd74SJohn Marino 	close_srcs(sl);
3040d5acd74SJohn Marino 	return (ret);
3050d5acd74SJohn Marino }
3060d5acd74SJohn Marino 
3070d5acd74SJohn Marino /* do convert a character */
3080d5acd74SJohn Marino #define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */
3090d5acd74SJohn Marino static int
3100d5acd74SJohn Marino /*ARGSUSED*/
do_conv(const struct _citrus_iconv_std_shared * is,_csid_t * csid,_index_t * idx)3110d5acd74SJohn Marino do_conv(const struct _citrus_iconv_std_shared *is,
3120d5acd74SJohn Marino 	_csid_t *csid, _index_t *idx)
3130d5acd74SJohn Marino {
3140d5acd74SJohn Marino 	struct _citrus_iconv_std_dst *sd;
3150d5acd74SJohn Marino 	struct _citrus_iconv_std_src *ss;
3160d5acd74SJohn Marino 	_index_t tmpidx;
3170d5acd74SJohn Marino 	int ret;
3180d5acd74SJohn Marino 
3190d5acd74SJohn Marino 	TAILQ_FOREACH(ss, &is->is_srcs, ss_entry) {
3200d5acd74SJohn Marino 		if (ss->ss_csid == *csid) {
3210d5acd74SJohn Marino 			TAILQ_FOREACH(sd, &ss->ss_dsts, sd_entry) {
3220d5acd74SJohn Marino 				ret = _csmapper_convert(sd->sd_mapper,
3230d5acd74SJohn Marino 				    &tmpidx, *idx, NULL);
3240d5acd74SJohn Marino 				switch (ret) {
3250d5acd74SJohn Marino 				case _MAPPER_CONVERT_SUCCESS:
3260d5acd74SJohn Marino 					*csid = sd->sd_csid;
3270d5acd74SJohn Marino 					*idx = tmpidx;
3280d5acd74SJohn Marino 					return (0);
3290d5acd74SJohn Marino 				case _MAPPER_CONVERT_NONIDENTICAL:
3300d5acd74SJohn Marino 					break;
3310d5acd74SJohn Marino 				case _MAPPER_CONVERT_SRC_MORE:
3320d5acd74SJohn Marino 					/*FALLTHROUGH*/
3330d5acd74SJohn Marino 				case _MAPPER_CONVERT_DST_MORE:
3340d5acd74SJohn Marino 					/*FALLTHROUGH*/
3350d5acd74SJohn Marino 				case _MAPPER_CONVERT_ILSEQ:
3360d5acd74SJohn Marino 					return (EILSEQ);
3370d5acd74SJohn Marino 				case _MAPPER_CONVERT_FATAL:
3380d5acd74SJohn Marino 					return (EINVAL);
3390d5acd74SJohn Marino 				}
3400d5acd74SJohn Marino 			}
3410d5acd74SJohn Marino 			break;
3420d5acd74SJohn Marino 		}
3430d5acd74SJohn Marino 	}
3440d5acd74SJohn Marino 
3450d5acd74SJohn Marino 	return (E_NO_CORRESPONDING_CHAR);
3460d5acd74SJohn Marino }
3470d5acd74SJohn Marino /* ---------------------------------------------------------------------- */
3480d5acd74SJohn Marino 
3490d5acd74SJohn Marino static int
3500d5acd74SJohn Marino /*ARGSUSED*/
_citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared * ci,const char * __restrict src,const char * __restrict dst)3510d5acd74SJohn Marino _citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared *ci,
3520d5acd74SJohn Marino     const char * __restrict src, const char * __restrict dst)
3530d5acd74SJohn Marino {
3540d5acd74SJohn Marino 	struct _citrus_esdb esdbdst, esdbsrc;
3550d5acd74SJohn Marino 	struct _citrus_iconv_std_shared *is;
3560d5acd74SJohn Marino 	int ret;
3570d5acd74SJohn Marino 
3580d5acd74SJohn Marino 	is = malloc(sizeof(*is));
3590d5acd74SJohn Marino 	if (is == NULL) {
3600d5acd74SJohn Marino 		ret = errno;
3610d5acd74SJohn Marino 		goto err0;
3620d5acd74SJohn Marino 	}
3630d5acd74SJohn Marino 	ret = _citrus_esdb_open(&esdbsrc, src);
3640d5acd74SJohn Marino 	if (ret)
3650d5acd74SJohn Marino 		goto err1;
3660d5acd74SJohn Marino 	ret = _citrus_esdb_open(&esdbdst, dst);
3670d5acd74SJohn Marino 	if (ret)
3680d5acd74SJohn Marino 		goto err2;
3690d5acd74SJohn Marino 	ret = _stdenc_open(&is->is_src_encoding, esdbsrc.db_encname,
3700d5acd74SJohn Marino 	    esdbsrc.db_variable, esdbsrc.db_len_variable);
3710d5acd74SJohn Marino 	if (ret)
3720d5acd74SJohn Marino 		goto err3;
3730d5acd74SJohn Marino 	ret = _stdenc_open(&is->is_dst_encoding, esdbdst.db_encname,
3740d5acd74SJohn Marino 	    esdbdst.db_variable, esdbdst.db_len_variable);
3750d5acd74SJohn Marino 	if (ret)
3760d5acd74SJohn Marino 		goto err4;
3770d5acd74SJohn Marino 	is->is_use_invalid = esdbdst.db_use_invalid;
3780d5acd74SJohn Marino 	is->is_invalid = esdbdst.db_invalid;
3790d5acd74SJohn Marino 
3800d5acd74SJohn Marino 	TAILQ_INIT(&is->is_srcs);
3810d5acd74SJohn Marino 	ret = open_srcs(&is->is_srcs, &esdbsrc, &esdbdst);
3820d5acd74SJohn Marino 	if (ret)
3830d5acd74SJohn Marino 		goto err5;
3840d5acd74SJohn Marino 
3850d5acd74SJohn Marino 	_esdb_close(&esdbsrc);
3860d5acd74SJohn Marino 	_esdb_close(&esdbdst);
3870d5acd74SJohn Marino 	ci->ci_closure = is;
3880d5acd74SJohn Marino 
3890d5acd74SJohn Marino 	return (0);
3900d5acd74SJohn Marino 
3910d5acd74SJohn Marino err5:
3920d5acd74SJohn Marino 	_stdenc_close(is->is_dst_encoding);
3930d5acd74SJohn Marino err4:
3940d5acd74SJohn Marino 	_stdenc_close(is->is_src_encoding);
3950d5acd74SJohn Marino err3:
3960d5acd74SJohn Marino 	_esdb_close(&esdbdst);
3970d5acd74SJohn Marino err2:
3980d5acd74SJohn Marino 	_esdb_close(&esdbsrc);
3990d5acd74SJohn Marino err1:
4000d5acd74SJohn Marino 	free(is);
4010d5acd74SJohn Marino err0:
4020d5acd74SJohn Marino 	return (ret);
4030d5acd74SJohn Marino }
4040d5acd74SJohn Marino 
4050d5acd74SJohn Marino static void
_citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared * ci)4060d5acd74SJohn Marino _citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared *ci)
4070d5acd74SJohn Marino {
4080d5acd74SJohn Marino 	struct _citrus_iconv_std_shared *is = ci->ci_closure;
4090d5acd74SJohn Marino 
4100d5acd74SJohn Marino 	if (is == NULL)
4110d5acd74SJohn Marino 		return;
4120d5acd74SJohn Marino 
4130d5acd74SJohn Marino 	_stdenc_close(is->is_src_encoding);
4140d5acd74SJohn Marino 	_stdenc_close(is->is_dst_encoding);
4150d5acd74SJohn Marino 	close_srcs(&is->is_srcs);
4160d5acd74SJohn Marino 	free(is);
4170d5acd74SJohn Marino }
4180d5acd74SJohn Marino 
4190d5acd74SJohn Marino static int
_citrus_iconv_std_iconv_init_context(struct _citrus_iconv * cv)4200d5acd74SJohn Marino _citrus_iconv_std_iconv_init_context(struct _citrus_iconv *cv)
4210d5acd74SJohn Marino {
4220d5acd74SJohn Marino 	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
4230d5acd74SJohn Marino 	struct _citrus_iconv_std_context *sc;
4240d5acd74SJohn Marino 	char *ptr;
4250d5acd74SJohn Marino 	size_t sz, szpsdst, szpssrc;
4260d5acd74SJohn Marino 
4270d5acd74SJohn Marino 	szpssrc = _stdenc_get_state_size(is->is_src_encoding);
4280d5acd74SJohn Marino 	szpsdst = _stdenc_get_state_size(is->is_dst_encoding);
4290d5acd74SJohn Marino 
4300d5acd74SJohn Marino 	sz = (szpssrc + szpsdst)*2 + sizeof(struct _citrus_iconv_std_context);
4310d5acd74SJohn Marino 	sc = malloc(sz);
4320d5acd74SJohn Marino 	if (sc == NULL)
4330d5acd74SJohn Marino 		return (errno);
4340d5acd74SJohn Marino 
4350d5acd74SJohn Marino 	ptr = (char *)&sc[1];
4360d5acd74SJohn Marino 	if (szpssrc > 0)
4370d5acd74SJohn Marino 		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
4380d5acd74SJohn Marino 		    ptr, ptr+szpssrc);
4390d5acd74SJohn Marino 	else
4400d5acd74SJohn Marino 		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
4410d5acd74SJohn Marino 		    NULL, NULL);
4420d5acd74SJohn Marino 	ptr += szpssrc*2;
4430d5acd74SJohn Marino 	if (szpsdst > 0)
4440d5acd74SJohn Marino 		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
4450d5acd74SJohn Marino 		    ptr, ptr+szpsdst);
4460d5acd74SJohn Marino 	else
4470d5acd74SJohn Marino 		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
4480d5acd74SJohn Marino 		    NULL, NULL);
4490d5acd74SJohn Marino 
4500d5acd74SJohn Marino 	cv->cv_closure = (void *)sc;
4510d5acd74SJohn Marino 
4520d5acd74SJohn Marino 	return (0);
4530d5acd74SJohn Marino }
4540d5acd74SJohn Marino 
4550d5acd74SJohn Marino static void
_citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv * cv)4560d5acd74SJohn Marino _citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv)
4570d5acd74SJohn Marino {
4580d5acd74SJohn Marino 
4590d5acd74SJohn Marino 	free(cv->cv_closure);
4600d5acd74SJohn Marino }
4610d5acd74SJohn Marino 
4620d5acd74SJohn Marino static int
_citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,char * __restrict * __restrict in,size_t * __restrict inbytes,char * __restrict * __restrict out,size_t * __restrict outbytes,uint32_t flags,size_t * __restrict invalids)4630d5acd74SJohn Marino _citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,
464*0db70a6aSJohn Marino     char * __restrict * __restrict in, size_t * __restrict inbytes,
4650d5acd74SJohn Marino     char * __restrict * __restrict out, size_t * __restrict outbytes,
4660d5acd74SJohn Marino     uint32_t flags, size_t * __restrict invalids)
4670d5acd74SJohn Marino {
4680d5acd74SJohn Marino 	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
4690d5acd74SJohn Marino 	struct _citrus_iconv_std_context *sc = cv->cv_closure;
4700d5acd74SJohn Marino 	_csid_t csid;
4710d5acd74SJohn Marino 	_index_t idx;
472*0db70a6aSJohn Marino 	char *tmpin;
4730d5acd74SJohn Marino 	size_t inval, szrin, szrout;
4740d5acd74SJohn Marino 	int ret, state = 0;
4750d5acd74SJohn Marino 
4760d5acd74SJohn Marino 	inval = 0;
4770d5acd74SJohn Marino 	if (in == NULL || *in == NULL) {
4780d5acd74SJohn Marino 		/* special cases */
4790d5acd74SJohn Marino 		if (out != NULL && *out != NULL) {
4800d5acd74SJohn Marino 			/* init output state and store the shift sequence */
4810d5acd74SJohn Marino 			save_encoding_state(&sc->sc_src_encoding);
4820d5acd74SJohn Marino 			save_encoding_state(&sc->sc_dst_encoding);
4830d5acd74SJohn Marino 			szrout = 0;
4840d5acd74SJohn Marino 
4850d5acd74SJohn Marino 			ret = put_state_resetx(&sc->sc_dst_encoding,
4860d5acd74SJohn Marino 			    *out, *outbytes, &szrout);
4870d5acd74SJohn Marino 			if (ret)
4880d5acd74SJohn Marino 				goto err;
4890d5acd74SJohn Marino 
4900d5acd74SJohn Marino 			if (szrout == (size_t)-2) {
4910d5acd74SJohn Marino 				/* too small to store the character */
4920d5acd74SJohn Marino 				ret = EINVAL;
4930d5acd74SJohn Marino 				goto err;
4940d5acd74SJohn Marino 			}
4950d5acd74SJohn Marino 			*out += szrout;
4960d5acd74SJohn Marino 			*outbytes -= szrout;
4970d5acd74SJohn Marino 		} else
4980d5acd74SJohn Marino 			/* otherwise, discard the shift sequence */
4990d5acd74SJohn Marino 			init_encoding_state(&sc->sc_dst_encoding);
5000d5acd74SJohn Marino 		init_encoding_state(&sc->sc_src_encoding);
5010d5acd74SJohn Marino 		*invalids = 0;
5020d5acd74SJohn Marino 		return (0);
5030d5acd74SJohn Marino 	}
5040d5acd74SJohn Marino 
5050d5acd74SJohn Marino 	/* normal case */
5060d5acd74SJohn Marino 	for (;;) {
5070d5acd74SJohn Marino 		if (*inbytes == 0) {
5080d5acd74SJohn Marino 			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
5090d5acd74SJohn Marino 			if (state == _STDENC_SDGEN_INITIAL ||
5100d5acd74SJohn Marino 			    state == _STDENC_SDGEN_STABLE)
5110d5acd74SJohn Marino 				break;
5120d5acd74SJohn Marino 		}
5130d5acd74SJohn Marino 
5140d5acd74SJohn Marino 		/* save the encoding states for the error recovery */
5150d5acd74SJohn Marino 		save_encoding_state(&sc->sc_src_encoding);
5160d5acd74SJohn Marino 		save_encoding_state(&sc->sc_dst_encoding);
5170d5acd74SJohn Marino 
5180d5acd74SJohn Marino 		/* mb -> csid/index */
5190d5acd74SJohn Marino 		tmpin = *in;
5200d5acd74SJohn Marino 		szrin = szrout = 0;
5210d5acd74SJohn Marino 		ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx, &tmpin,
5220d5acd74SJohn Marino 		    *inbytes, &szrin, cv->cv_shared->ci_hooks);
5230d5acd74SJohn Marino 		if (ret)
5240d5acd74SJohn Marino 			goto err;
5250d5acd74SJohn Marino 
5260d5acd74SJohn Marino 		if (szrin == (size_t)-2) {
5270d5acd74SJohn Marino 			/* incompleted character */
5280d5acd74SJohn Marino 			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
5290d5acd74SJohn Marino 			if (ret) {
5300d5acd74SJohn Marino 				ret = EINVAL;
5310d5acd74SJohn Marino 				goto err;
5320d5acd74SJohn Marino 			}
5330d5acd74SJohn Marino 			switch (state) {
5340d5acd74SJohn Marino 			case _STDENC_SDGEN_INITIAL:
5350d5acd74SJohn Marino 			case _STDENC_SDGEN_STABLE:
5360d5acd74SJohn Marino 				/* fetch shift sequences only. */
5370d5acd74SJohn Marino 				goto next;
5380d5acd74SJohn Marino 			}
5390d5acd74SJohn Marino 			ret = EINVAL;
5400d5acd74SJohn Marino 			goto err;
5410d5acd74SJohn Marino 		}
5420d5acd74SJohn Marino 		/* convert the character */
5430d5acd74SJohn Marino 		ret = do_conv(is, &csid, &idx);
5440d5acd74SJohn Marino 		if (ret) {
5450d5acd74SJohn Marino 			if (ret == E_NO_CORRESPONDING_CHAR) {
5469d944071SJohn Marino 				/*
5479d944071SJohn Marino 				 * GNU iconv returns EILSEQ when no
5489d944071SJohn Marino 				 * corresponding character in the output.
5499d944071SJohn Marino 				 * Some software depends on this behavior
5509d944071SJohn Marino 				 * though this is against POSIX specification.
5519d944071SJohn Marino 				 */
5529d944071SJohn Marino 				if (cv->cv_shared->ci_ilseq_invalid != 0) {
5539d944071SJohn Marino 					ret = EILSEQ;
5549d944071SJohn Marino 					goto err;
5559d944071SJohn Marino 				}
5560d5acd74SJohn Marino 				inval++;
5570d5acd74SJohn Marino 				szrout = 0;
5580d5acd74SJohn Marino 				if ((((flags & _CITRUS_ICONV_F_HIDE_INVALID) == 0) &&
5590d5acd74SJohn Marino 				    !cv->cv_shared->ci_discard_ilseq) &&
5600d5acd74SJohn Marino 				    is->is_use_invalid) {
5610d5acd74SJohn Marino 					ret = wctombx(&sc->sc_dst_encoding,
5620d5acd74SJohn Marino 					    *out, *outbytes, is->is_invalid,
5630d5acd74SJohn Marino 					    &szrout, cv->cv_shared->ci_hooks);
5640d5acd74SJohn Marino 					if (ret)
5650d5acd74SJohn Marino 						goto err;
5660d5acd74SJohn Marino 				}
5670d5acd74SJohn Marino 				goto next;
5680d5acd74SJohn Marino 			} else
5690d5acd74SJohn Marino 				goto err;
5700d5acd74SJohn Marino 		}
5710d5acd74SJohn Marino 		/* csid/index -> mb */
5720d5acd74SJohn Marino 		ret = cstombx(&sc->sc_dst_encoding,
5730d5acd74SJohn Marino 		    *out, *outbytes, csid, idx, &szrout,
5740d5acd74SJohn Marino 		    cv->cv_shared->ci_hooks);
5750d5acd74SJohn Marino 		if (ret)
5760d5acd74SJohn Marino 			goto err;
5770d5acd74SJohn Marino next:
5780d5acd74SJohn Marino 		*inbytes -= tmpin-*in; /* szrin is insufficient on \0. */
5790d5acd74SJohn Marino 		*in = tmpin;
5800d5acd74SJohn Marino 		*outbytes -= szrout;
5810d5acd74SJohn Marino 		*out += szrout;
5820d5acd74SJohn Marino 	}
5830d5acd74SJohn Marino 	*invalids = inval;
5840d5acd74SJohn Marino 
5850d5acd74SJohn Marino 	return (0);
5860d5acd74SJohn Marino 
5870d5acd74SJohn Marino err:
5880d5acd74SJohn Marino 	restore_encoding_state(&sc->sc_src_encoding);
5890d5acd74SJohn Marino 	restore_encoding_state(&sc->sc_dst_encoding);
5900d5acd74SJohn Marino 	*invalids = inval;
5910d5acd74SJohn Marino 
5920d5acd74SJohn Marino 	return (ret);
5930d5acd74SJohn Marino }
594