19ca40936STijl Coosemans /*	$NetBSD: citrus_iconv_std.c,v 1.16 2012/02/12 13:51:29 wiz Exp $	*/
2ad30f8e7SGabor Kovesdan 
3ad30f8e7SGabor Kovesdan /*-
45e53a4f9SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause
55e53a4f9SPedro F. Giffuni  *
6ad30f8e7SGabor Kovesdan  * Copyright (c)2003 Citrus Project,
7ad30f8e7SGabor Kovesdan  * All rights reserved.
8ad30f8e7SGabor Kovesdan  *
9ad30f8e7SGabor Kovesdan  * Redistribution and use in source and binary forms, with or without
10ad30f8e7SGabor Kovesdan  * modification, are permitted provided that the following conditions
11ad30f8e7SGabor Kovesdan  * are met:
12ad30f8e7SGabor Kovesdan  * 1. Redistributions of source code must retain the above copyright
13ad30f8e7SGabor Kovesdan  *    notice, this list of conditions and the following disclaimer.
14ad30f8e7SGabor Kovesdan  * 2. Redistributions in binary form must reproduce the above copyright
15ad30f8e7SGabor Kovesdan  *    notice, this list of conditions and the following disclaimer in the
16ad30f8e7SGabor Kovesdan  *    documentation and/or other materials provided with the distribution.
17ad30f8e7SGabor Kovesdan  *
18ad30f8e7SGabor Kovesdan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19ad30f8e7SGabor Kovesdan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20ad30f8e7SGabor Kovesdan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21ad30f8e7SGabor Kovesdan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22ad30f8e7SGabor Kovesdan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23ad30f8e7SGabor Kovesdan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24ad30f8e7SGabor Kovesdan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25ad30f8e7SGabor Kovesdan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26ad30f8e7SGabor Kovesdan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27ad30f8e7SGabor Kovesdan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28ad30f8e7SGabor Kovesdan  * SUCH DAMAGE.
29ad30f8e7SGabor Kovesdan  */
30ad30f8e7SGabor Kovesdan 
31ad30f8e7SGabor Kovesdan #include <sys/cdefs.h>
32ad30f8e7SGabor Kovesdan #include <sys/endian.h>
33ad30f8e7SGabor Kovesdan #include <sys/queue.h>
34ad30f8e7SGabor Kovesdan 
35ad30f8e7SGabor Kovesdan #include <assert.h>
36ad30f8e7SGabor Kovesdan #include <errno.h>
37ad30f8e7SGabor Kovesdan #include <limits.h>
38ad30f8e7SGabor Kovesdan #include <stdbool.h>
39ad30f8e7SGabor Kovesdan #include <stdio.h>
40ad30f8e7SGabor Kovesdan #include <stdlib.h>
41ad30f8e7SGabor Kovesdan #include <string.h>
42ad30f8e7SGabor Kovesdan 
43ad30f8e7SGabor Kovesdan #include "citrus_namespace.h"
44ad30f8e7SGabor Kovesdan #include "citrus_types.h"
45ad30f8e7SGabor Kovesdan #include "citrus_module.h"
46ad30f8e7SGabor Kovesdan #include "citrus_region.h"
47ad30f8e7SGabor Kovesdan #include "citrus_mmap.h"
48ad30f8e7SGabor Kovesdan #include "citrus_hash.h"
49ad30f8e7SGabor Kovesdan #include "citrus_iconv.h"
50ad30f8e7SGabor Kovesdan #include "citrus_stdenc.h"
51ad30f8e7SGabor Kovesdan #include "citrus_mapper.h"
52ad30f8e7SGabor Kovesdan #include "citrus_csmapper.h"
53ad30f8e7SGabor Kovesdan #include "citrus_memstream.h"
54ad30f8e7SGabor Kovesdan #include "citrus_iconv_std.h"
55ad30f8e7SGabor Kovesdan #include "citrus_esdb.h"
56ad30f8e7SGabor Kovesdan 
57ad30f8e7SGabor Kovesdan /* ---------------------------------------------------------------------- */
58ad30f8e7SGabor Kovesdan 
59ad30f8e7SGabor Kovesdan _CITRUS_ICONV_DECLS(iconv_std);
60ad30f8e7SGabor Kovesdan _CITRUS_ICONV_DEF_OPS(iconv_std);
61ad30f8e7SGabor Kovesdan 
62ad30f8e7SGabor Kovesdan 
63ad30f8e7SGabor Kovesdan /* ---------------------------------------------------------------------- */
64ad30f8e7SGabor Kovesdan 
65ad30f8e7SGabor Kovesdan int
_citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops * ops)66ad30f8e7SGabor Kovesdan _citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops *ops)
67ad30f8e7SGabor Kovesdan {
68ad30f8e7SGabor Kovesdan 
69ad30f8e7SGabor Kovesdan 	memcpy(ops, &_citrus_iconv_std_iconv_ops,
70ad30f8e7SGabor Kovesdan 	    sizeof(_citrus_iconv_std_iconv_ops));
71ad30f8e7SGabor Kovesdan 
72ad30f8e7SGabor Kovesdan 	return (0);
73ad30f8e7SGabor Kovesdan }
74ad30f8e7SGabor Kovesdan 
75ad30f8e7SGabor Kovesdan /* ---------------------------------------------------------------------- */
76ad30f8e7SGabor Kovesdan 
77ad30f8e7SGabor Kovesdan /*
78ad30f8e7SGabor Kovesdan  * convenience routines for stdenc.
79ad30f8e7SGabor Kovesdan  */
80ad30f8e7SGabor Kovesdan static __inline void
save_encoding_state(struct _citrus_iconv_std_encoding * se)81ad30f8e7SGabor Kovesdan save_encoding_state(struct _citrus_iconv_std_encoding *se)
82ad30f8e7SGabor Kovesdan {
83ad30f8e7SGabor Kovesdan 
84ad30f8e7SGabor Kovesdan 	if (se->se_ps)
85ad30f8e7SGabor Kovesdan 		memcpy(se->se_pssaved, se->se_ps,
86ad30f8e7SGabor Kovesdan 		    _stdenc_get_state_size(se->se_handle));
87ad30f8e7SGabor Kovesdan }
88ad30f8e7SGabor Kovesdan 
89ad30f8e7SGabor Kovesdan static __inline void
restore_encoding_state(struct _citrus_iconv_std_encoding * se)90ad30f8e7SGabor Kovesdan restore_encoding_state(struct _citrus_iconv_std_encoding *se)
91ad30f8e7SGabor Kovesdan {
92ad30f8e7SGabor Kovesdan 
93ad30f8e7SGabor Kovesdan 	if (se->se_ps)
94ad30f8e7SGabor Kovesdan 		memcpy(se->se_ps, se->se_pssaved,
95ad30f8e7SGabor Kovesdan 		    _stdenc_get_state_size(se->se_handle));
96ad30f8e7SGabor Kovesdan }
97ad30f8e7SGabor Kovesdan 
98ad30f8e7SGabor Kovesdan static __inline void
init_encoding_state(struct _citrus_iconv_std_encoding * se)99ad30f8e7SGabor Kovesdan init_encoding_state(struct _citrus_iconv_std_encoding *se)
100ad30f8e7SGabor Kovesdan {
101ad30f8e7SGabor Kovesdan 
102ad30f8e7SGabor Kovesdan 	if (se->se_ps)
103ad30f8e7SGabor Kovesdan 		_stdenc_init_state(se->se_handle, se->se_ps);
104ad30f8e7SGabor Kovesdan }
105ad30f8e7SGabor Kovesdan 
106ad30f8e7SGabor Kovesdan 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)107ad30f8e7SGabor Kovesdan mbtocsx(struct _citrus_iconv_std_encoding *se,
1081243a98eSTijl Coosemans     _csid_t *csid, _index_t *idx, char **s, size_t n, size_t *nresult,
109ad30f8e7SGabor Kovesdan     struct iconv_hooks *hooks)
110ad30f8e7SGabor Kovesdan {
111ad30f8e7SGabor Kovesdan 
112ad30f8e7SGabor Kovesdan 	return (_stdenc_mbtocs(se->se_handle, csid, idx, s, n, se->se_ps,
113ad30f8e7SGabor Kovesdan 			      nresult, hooks));
114ad30f8e7SGabor Kovesdan }
115ad30f8e7SGabor Kovesdan 
116ad30f8e7SGabor Kovesdan 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)117ad30f8e7SGabor Kovesdan cstombx(struct _citrus_iconv_std_encoding *se,
118ad30f8e7SGabor Kovesdan     char *s, size_t n, _csid_t csid, _index_t idx, size_t *nresult,
119ad30f8e7SGabor Kovesdan     struct iconv_hooks *hooks)
120ad30f8e7SGabor Kovesdan {
121ad30f8e7SGabor Kovesdan 
122ad30f8e7SGabor Kovesdan 	return (_stdenc_cstomb(se->se_handle, s, n, csid, idx, se->se_ps,
123ad30f8e7SGabor Kovesdan 			      nresult, hooks));
124ad30f8e7SGabor Kovesdan }
125ad30f8e7SGabor Kovesdan 
126ad30f8e7SGabor Kovesdan 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)127ad30f8e7SGabor Kovesdan wctombx(struct _citrus_iconv_std_encoding *se,
128ad30f8e7SGabor Kovesdan     char *s, size_t n, _wc_t wc, size_t *nresult,
129ad30f8e7SGabor Kovesdan     struct iconv_hooks *hooks)
130ad30f8e7SGabor Kovesdan {
131ad30f8e7SGabor Kovesdan 
132ad30f8e7SGabor Kovesdan 	return (_stdenc_wctomb(se->se_handle, s, n, wc, se->se_ps, nresult,
133ad30f8e7SGabor Kovesdan 			     hooks));
134ad30f8e7SGabor Kovesdan }
135ad30f8e7SGabor Kovesdan 
136ad30f8e7SGabor Kovesdan static __inline int
put_state_resetx(struct _citrus_iconv_std_encoding * se,char * s,size_t n,size_t * nresult)137ad30f8e7SGabor Kovesdan put_state_resetx(struct _citrus_iconv_std_encoding *se, char *s, size_t n,
138ad30f8e7SGabor Kovesdan     size_t *nresult)
139ad30f8e7SGabor Kovesdan {
140ad30f8e7SGabor Kovesdan 
141ad30f8e7SGabor Kovesdan 	return (_stdenc_put_state_reset(se->se_handle, s, n, se->se_ps, nresult));
142ad30f8e7SGabor Kovesdan }
143ad30f8e7SGabor Kovesdan 
144ad30f8e7SGabor Kovesdan static __inline int
get_state_desc_gen(struct _citrus_iconv_std_encoding * se,int * rstate)145ad30f8e7SGabor Kovesdan get_state_desc_gen(struct _citrus_iconv_std_encoding *se, int *rstate)
146ad30f8e7SGabor Kovesdan {
147ad30f8e7SGabor Kovesdan 	struct _stdenc_state_desc ssd;
148ad30f8e7SGabor Kovesdan 	int ret;
149ad30f8e7SGabor Kovesdan 
150ad30f8e7SGabor Kovesdan 	ret = _stdenc_get_state_desc(se->se_handle, se->se_ps,
151ad30f8e7SGabor Kovesdan 	    _STDENC_SDID_GENERIC, &ssd);
152ad30f8e7SGabor Kovesdan 	if (!ret)
153ad30f8e7SGabor Kovesdan 		*rstate = ssd.u.generic.state;
154ad30f8e7SGabor Kovesdan 
155ad30f8e7SGabor Kovesdan 	return (ret);
156ad30f8e7SGabor Kovesdan }
157ad30f8e7SGabor Kovesdan 
158ad30f8e7SGabor Kovesdan /*
159ad30f8e7SGabor Kovesdan  * init encoding context
160ad30f8e7SGabor Kovesdan  */
161ad30f8e7SGabor Kovesdan static int
init_encoding(struct _citrus_iconv_std_encoding * se,struct _stdenc * cs,void * ps1,void * ps2)162ad30f8e7SGabor Kovesdan init_encoding(struct _citrus_iconv_std_encoding *se, struct _stdenc *cs,
163ad30f8e7SGabor Kovesdan     void *ps1, void *ps2)
164ad30f8e7SGabor Kovesdan {
165ad30f8e7SGabor Kovesdan 	int ret = -1;
166ad30f8e7SGabor Kovesdan 
167ad30f8e7SGabor Kovesdan 	se->se_handle = cs;
168ad30f8e7SGabor Kovesdan 	se->se_ps = ps1;
169ad30f8e7SGabor Kovesdan 	se->se_pssaved = ps2;
170ad30f8e7SGabor Kovesdan 
171ad30f8e7SGabor Kovesdan 	if (se->se_ps)
172ad30f8e7SGabor Kovesdan 		ret = _stdenc_init_state(cs, se->se_ps);
173ad30f8e7SGabor Kovesdan 	if (!ret && se->se_pssaved)
174ad30f8e7SGabor Kovesdan 		ret = _stdenc_init_state(cs, se->se_pssaved);
175ad30f8e7SGabor Kovesdan 
176ad30f8e7SGabor Kovesdan 	return (ret);
177ad30f8e7SGabor Kovesdan }
178ad30f8e7SGabor Kovesdan 
179ad30f8e7SGabor Kovesdan static int
open_csmapper(struct _csmapper ** rcm,const char * src,const char * dst,unsigned long * rnorm)180ad30f8e7SGabor Kovesdan open_csmapper(struct _csmapper **rcm, const char *src, const char *dst,
181ad30f8e7SGabor Kovesdan     unsigned long *rnorm)
182ad30f8e7SGabor Kovesdan {
183ad30f8e7SGabor Kovesdan 	struct _csmapper *cm;
184ad30f8e7SGabor Kovesdan 	int ret;
185ad30f8e7SGabor Kovesdan 
186ad30f8e7SGabor Kovesdan 	ret = _csmapper_open(&cm, src, dst, 0, rnorm);
187ad30f8e7SGabor Kovesdan 	if (ret)
188ad30f8e7SGabor Kovesdan 		return (ret);
189ad30f8e7SGabor Kovesdan 	if (_csmapper_get_src_max(cm) != 1 || _csmapper_get_dst_max(cm) != 1 ||
190ad30f8e7SGabor Kovesdan 	    _csmapper_get_state_size(cm) != 0) {
191ad30f8e7SGabor Kovesdan 		_csmapper_close(cm);
192ad30f8e7SGabor Kovesdan 		return (EINVAL);
193ad30f8e7SGabor Kovesdan 	}
194ad30f8e7SGabor Kovesdan 
195ad30f8e7SGabor Kovesdan 	*rcm = cm;
196ad30f8e7SGabor Kovesdan 
197ad30f8e7SGabor Kovesdan 	return (0);
198ad30f8e7SGabor Kovesdan }
199ad30f8e7SGabor Kovesdan 
200ad30f8e7SGabor Kovesdan static void
close_dsts(struct _citrus_iconv_std_dst_list * dl)201ad30f8e7SGabor Kovesdan close_dsts(struct _citrus_iconv_std_dst_list *dl)
202ad30f8e7SGabor Kovesdan {
203ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_dst *sd;
204ad30f8e7SGabor Kovesdan 
205ad30f8e7SGabor Kovesdan 	while ((sd = TAILQ_FIRST(dl)) != NULL) {
206ad30f8e7SGabor Kovesdan 		TAILQ_REMOVE(dl, sd, sd_entry);
207ad30f8e7SGabor Kovesdan 		_csmapper_close(sd->sd_mapper);
208ad30f8e7SGabor Kovesdan 		free(sd);
209ad30f8e7SGabor Kovesdan 	}
210ad30f8e7SGabor Kovesdan }
211ad30f8e7SGabor Kovesdan 
212ad30f8e7SGabor Kovesdan static int
open_dsts(struct _citrus_iconv_std_dst_list * dl,const struct _esdb_charset * ec,const struct _esdb * dbdst)213ad30f8e7SGabor Kovesdan open_dsts(struct _citrus_iconv_std_dst_list *dl,
214ad30f8e7SGabor Kovesdan     const struct _esdb_charset *ec, const struct _esdb *dbdst)
215ad30f8e7SGabor Kovesdan {
216ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_dst *sd, *sdtmp;
217ad30f8e7SGabor Kovesdan 	unsigned long norm;
218ad30f8e7SGabor Kovesdan 	int i, ret;
219ad30f8e7SGabor Kovesdan 
220ad30f8e7SGabor Kovesdan 	sd = malloc(sizeof(*sd));
221ad30f8e7SGabor Kovesdan 	if (sd == NULL)
222ad30f8e7SGabor Kovesdan 		return (errno);
223ad30f8e7SGabor Kovesdan 
224ad30f8e7SGabor Kovesdan 	for (i = 0; i < dbdst->db_num_charsets; i++) {
225ad30f8e7SGabor Kovesdan 		ret = open_csmapper(&sd->sd_mapper, ec->ec_csname,
226ad30f8e7SGabor Kovesdan 		    dbdst->db_charsets[i].ec_csname, &norm);
227ad30f8e7SGabor Kovesdan 		if (ret == 0) {
228ad30f8e7SGabor Kovesdan 			sd->sd_csid = dbdst->db_charsets[i].ec_csid;
229ad30f8e7SGabor Kovesdan 			sd->sd_norm = norm;
230ad30f8e7SGabor Kovesdan 			/* insert this mapper by sorted order. */
231ad30f8e7SGabor Kovesdan 			TAILQ_FOREACH(sdtmp, dl, sd_entry) {
232ad30f8e7SGabor Kovesdan 				if (sdtmp->sd_norm > norm) {
233ad30f8e7SGabor Kovesdan 					TAILQ_INSERT_BEFORE(sdtmp, sd,
234ad30f8e7SGabor Kovesdan 					    sd_entry);
235ad30f8e7SGabor Kovesdan 					sd = NULL;
236ad30f8e7SGabor Kovesdan 					break;
237ad30f8e7SGabor Kovesdan 				}
238ad30f8e7SGabor Kovesdan 			}
239ad30f8e7SGabor Kovesdan 			if (sd)
240ad30f8e7SGabor Kovesdan 				TAILQ_INSERT_TAIL(dl, sd, sd_entry);
241ad30f8e7SGabor Kovesdan 			sd = malloc(sizeof(*sd));
242ad30f8e7SGabor Kovesdan 			if (sd == NULL) {
243ad30f8e7SGabor Kovesdan 				ret = errno;
244ad30f8e7SGabor Kovesdan 				close_dsts(dl);
245ad30f8e7SGabor Kovesdan 				return (ret);
246ad30f8e7SGabor Kovesdan 			}
247ad30f8e7SGabor Kovesdan 		} else if (ret != ENOENT) {
248ad30f8e7SGabor Kovesdan 			close_dsts(dl);
249ad30f8e7SGabor Kovesdan 			free(sd);
250ad30f8e7SGabor Kovesdan 			return (ret);
251ad30f8e7SGabor Kovesdan 		}
252ad30f8e7SGabor Kovesdan 	}
253ad30f8e7SGabor Kovesdan 	free(sd);
254ad30f8e7SGabor Kovesdan 	return (0);
255ad30f8e7SGabor Kovesdan }
256ad30f8e7SGabor Kovesdan 
257ad30f8e7SGabor Kovesdan static void
close_srcs(struct _citrus_iconv_std_src_list * sl)258ad30f8e7SGabor Kovesdan close_srcs(struct _citrus_iconv_std_src_list *sl)
259ad30f8e7SGabor Kovesdan {
260ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_src *ss;
261ad30f8e7SGabor Kovesdan 
262ad30f8e7SGabor Kovesdan 	while ((ss = TAILQ_FIRST(sl)) != NULL) {
263ad30f8e7SGabor Kovesdan 		TAILQ_REMOVE(sl, ss, ss_entry);
264ad30f8e7SGabor Kovesdan 		close_dsts(&ss->ss_dsts);
265ad30f8e7SGabor Kovesdan 		free(ss);
266ad30f8e7SGabor Kovesdan 	}
267ad30f8e7SGabor Kovesdan }
268ad30f8e7SGabor Kovesdan 
269ad30f8e7SGabor Kovesdan static int
open_srcs(struct _citrus_iconv_std_src_list * sl,const struct _esdb * dbsrc,const struct _esdb * dbdst)270ad30f8e7SGabor Kovesdan open_srcs(struct _citrus_iconv_std_src_list *sl,
271ad30f8e7SGabor Kovesdan     const struct _esdb *dbsrc, const struct _esdb *dbdst)
272ad30f8e7SGabor Kovesdan {
273ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_src *ss;
274ad30f8e7SGabor Kovesdan 	int count = 0, i, ret;
275ad30f8e7SGabor Kovesdan 
276ad30f8e7SGabor Kovesdan 	ss = malloc(sizeof(*ss));
277ad30f8e7SGabor Kovesdan 	if (ss == NULL)
278ad30f8e7SGabor Kovesdan 		return (errno);
279ad30f8e7SGabor Kovesdan 
280ad30f8e7SGabor Kovesdan 	TAILQ_INIT(&ss->ss_dsts);
281ad30f8e7SGabor Kovesdan 
282ad30f8e7SGabor Kovesdan 	for (i = 0; i < dbsrc->db_num_charsets; i++) {
283ad30f8e7SGabor Kovesdan 		ret = open_dsts(&ss->ss_dsts, &dbsrc->db_charsets[i], dbdst);
284ad30f8e7SGabor Kovesdan 		if (ret)
285ad30f8e7SGabor Kovesdan 			goto err;
286ad30f8e7SGabor Kovesdan 		if (!TAILQ_EMPTY(&ss->ss_dsts)) {
287ad30f8e7SGabor Kovesdan 			ss->ss_csid = dbsrc->db_charsets[i].ec_csid;
288ad30f8e7SGabor Kovesdan 			TAILQ_INSERT_TAIL(sl, ss, ss_entry);
289ad30f8e7SGabor Kovesdan 			ss = malloc(sizeof(*ss));
290ad30f8e7SGabor Kovesdan 			if (ss == NULL) {
291ad30f8e7SGabor Kovesdan 				ret = errno;
292ad30f8e7SGabor Kovesdan 				goto err;
293ad30f8e7SGabor Kovesdan 			}
294ad30f8e7SGabor Kovesdan 			count++;
295ad30f8e7SGabor Kovesdan 			TAILQ_INIT(&ss->ss_dsts);
296ad30f8e7SGabor Kovesdan 		}
297ad30f8e7SGabor Kovesdan 	}
298ad30f8e7SGabor Kovesdan 	free(ss);
299ad30f8e7SGabor Kovesdan 
300ad30f8e7SGabor Kovesdan 	return (count ? 0 : ENOENT);
301ad30f8e7SGabor Kovesdan 
302ad30f8e7SGabor Kovesdan err:
303ad30f8e7SGabor Kovesdan 	free(ss);
304ad30f8e7SGabor Kovesdan 	close_srcs(sl);
305ad30f8e7SGabor Kovesdan 	return (ret);
306ad30f8e7SGabor Kovesdan }
307ad30f8e7SGabor Kovesdan 
308ad30f8e7SGabor Kovesdan /* do convert a character */
309ad30f8e7SGabor Kovesdan #define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */
310ad30f8e7SGabor Kovesdan static int
311ad30f8e7SGabor Kovesdan /*ARGSUSED*/
do_conv(const struct _citrus_iconv_std_shared * is,_csid_t * csid,_index_t * idx)312ad30f8e7SGabor Kovesdan do_conv(const struct _citrus_iconv_std_shared *is,
313ad30f8e7SGabor Kovesdan 	_csid_t *csid, _index_t *idx)
314ad30f8e7SGabor Kovesdan {
315ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_dst *sd;
316ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_src *ss;
317ad30f8e7SGabor Kovesdan 	_index_t tmpidx;
318ad30f8e7SGabor Kovesdan 	int ret;
319ad30f8e7SGabor Kovesdan 
320ad30f8e7SGabor Kovesdan 	TAILQ_FOREACH(ss, &is->is_srcs, ss_entry) {
321ad30f8e7SGabor Kovesdan 		if (ss->ss_csid == *csid) {
322ad30f8e7SGabor Kovesdan 			TAILQ_FOREACH(sd, &ss->ss_dsts, sd_entry) {
323ad30f8e7SGabor Kovesdan 				ret = _csmapper_convert(sd->sd_mapper,
324ad30f8e7SGabor Kovesdan 				    &tmpidx, *idx, NULL);
325ad30f8e7SGabor Kovesdan 				switch (ret) {
326ad30f8e7SGabor Kovesdan 				case _MAPPER_CONVERT_SUCCESS:
327ad30f8e7SGabor Kovesdan 					*csid = sd->sd_csid;
328ad30f8e7SGabor Kovesdan 					*idx = tmpidx;
329ad30f8e7SGabor Kovesdan 					return (0);
330ad30f8e7SGabor Kovesdan 				case _MAPPER_CONVERT_NONIDENTICAL:
331ad30f8e7SGabor Kovesdan 					break;
332ad30f8e7SGabor Kovesdan 				case _MAPPER_CONVERT_SRC_MORE:
333ad30f8e7SGabor Kovesdan 					/*FALLTHROUGH*/
334ad30f8e7SGabor Kovesdan 				case _MAPPER_CONVERT_DST_MORE:
335ad30f8e7SGabor Kovesdan 					/*FALLTHROUGH*/
336ad30f8e7SGabor Kovesdan 				case _MAPPER_CONVERT_ILSEQ:
337ad30f8e7SGabor Kovesdan 					return (EILSEQ);
338ad30f8e7SGabor Kovesdan 				case _MAPPER_CONVERT_FATAL:
339ad30f8e7SGabor Kovesdan 					return (EINVAL);
340ad30f8e7SGabor Kovesdan 				}
341ad30f8e7SGabor Kovesdan 			}
342ad30f8e7SGabor Kovesdan 			break;
343ad30f8e7SGabor Kovesdan 		}
344ad30f8e7SGabor Kovesdan 	}
345ad30f8e7SGabor Kovesdan 
346ad30f8e7SGabor Kovesdan 	return (E_NO_CORRESPONDING_CHAR);
347ad30f8e7SGabor Kovesdan }
348ad30f8e7SGabor Kovesdan /* ---------------------------------------------------------------------- */
349ad30f8e7SGabor Kovesdan 
350ad30f8e7SGabor Kovesdan static int
351ad30f8e7SGabor Kovesdan /*ARGSUSED*/
_citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared * ci,const char * __restrict src,const char * __restrict dst)352ad30f8e7SGabor Kovesdan _citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared *ci,
353ad30f8e7SGabor Kovesdan     const char * __restrict src, const char * __restrict dst)
354ad30f8e7SGabor Kovesdan {
355ad30f8e7SGabor Kovesdan 	struct _citrus_esdb esdbdst, esdbsrc;
356ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_shared *is;
357ad30f8e7SGabor Kovesdan 	int ret;
358ad30f8e7SGabor Kovesdan 
359ad30f8e7SGabor Kovesdan 	is = malloc(sizeof(*is));
360ad30f8e7SGabor Kovesdan 	if (is == NULL) {
361ad30f8e7SGabor Kovesdan 		ret = errno;
362ad30f8e7SGabor Kovesdan 		goto err0;
363ad30f8e7SGabor Kovesdan 	}
364ad30f8e7SGabor Kovesdan 	ret = _citrus_esdb_open(&esdbsrc, src);
365ad30f8e7SGabor Kovesdan 	if (ret)
366ad30f8e7SGabor Kovesdan 		goto err1;
367ad30f8e7SGabor Kovesdan 	ret = _citrus_esdb_open(&esdbdst, dst);
368ad30f8e7SGabor Kovesdan 	if (ret)
369ad30f8e7SGabor Kovesdan 		goto err2;
370ad30f8e7SGabor Kovesdan 	ret = _stdenc_open(&is->is_src_encoding, esdbsrc.db_encname,
371ad30f8e7SGabor Kovesdan 	    esdbsrc.db_variable, esdbsrc.db_len_variable);
372ad30f8e7SGabor Kovesdan 	if (ret)
373ad30f8e7SGabor Kovesdan 		goto err3;
374ad30f8e7SGabor Kovesdan 	ret = _stdenc_open(&is->is_dst_encoding, esdbdst.db_encname,
375ad30f8e7SGabor Kovesdan 	    esdbdst.db_variable, esdbdst.db_len_variable);
376ad30f8e7SGabor Kovesdan 	if (ret)
377ad30f8e7SGabor Kovesdan 		goto err4;
378ad30f8e7SGabor Kovesdan 	is->is_use_invalid = esdbdst.db_use_invalid;
379ad30f8e7SGabor Kovesdan 	is->is_invalid = esdbdst.db_invalid;
380ad30f8e7SGabor Kovesdan 
381ad30f8e7SGabor Kovesdan 	TAILQ_INIT(&is->is_srcs);
382ad30f8e7SGabor Kovesdan 	ret = open_srcs(&is->is_srcs, &esdbsrc, &esdbdst);
383ad30f8e7SGabor Kovesdan 	if (ret)
384ad30f8e7SGabor Kovesdan 		goto err5;
385ad30f8e7SGabor Kovesdan 
386ad30f8e7SGabor Kovesdan 	_esdb_close(&esdbsrc);
387ad30f8e7SGabor Kovesdan 	_esdb_close(&esdbdst);
388ad30f8e7SGabor Kovesdan 	ci->ci_closure = is;
389ad30f8e7SGabor Kovesdan 
390ad30f8e7SGabor Kovesdan 	return (0);
391ad30f8e7SGabor Kovesdan 
392ad30f8e7SGabor Kovesdan err5:
393ad30f8e7SGabor Kovesdan 	_stdenc_close(is->is_dst_encoding);
394ad30f8e7SGabor Kovesdan err4:
395ad30f8e7SGabor Kovesdan 	_stdenc_close(is->is_src_encoding);
396ad30f8e7SGabor Kovesdan err3:
397ad30f8e7SGabor Kovesdan 	_esdb_close(&esdbdst);
398ad30f8e7SGabor Kovesdan err2:
399ad30f8e7SGabor Kovesdan 	_esdb_close(&esdbsrc);
400ad30f8e7SGabor Kovesdan err1:
401ad30f8e7SGabor Kovesdan 	free(is);
402ad30f8e7SGabor Kovesdan err0:
403ad30f8e7SGabor Kovesdan 	return (ret);
404ad30f8e7SGabor Kovesdan }
405ad30f8e7SGabor Kovesdan 
406ad30f8e7SGabor Kovesdan static void
_citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared * ci)407ad30f8e7SGabor Kovesdan _citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared *ci)
408ad30f8e7SGabor Kovesdan {
409ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_shared *is = ci->ci_closure;
410ad30f8e7SGabor Kovesdan 
411ad30f8e7SGabor Kovesdan 	if (is == NULL)
412ad30f8e7SGabor Kovesdan 		return;
413ad30f8e7SGabor Kovesdan 
414ad30f8e7SGabor Kovesdan 	_stdenc_close(is->is_src_encoding);
415ad30f8e7SGabor Kovesdan 	_stdenc_close(is->is_dst_encoding);
416ad30f8e7SGabor Kovesdan 	close_srcs(&is->is_srcs);
417ad30f8e7SGabor Kovesdan 	free(is);
418ad30f8e7SGabor Kovesdan }
419ad30f8e7SGabor Kovesdan 
420ad30f8e7SGabor Kovesdan static int
_citrus_iconv_std_iconv_init_context(struct _citrus_iconv * cv)421ad30f8e7SGabor Kovesdan _citrus_iconv_std_iconv_init_context(struct _citrus_iconv *cv)
422ad30f8e7SGabor Kovesdan {
423ad30f8e7SGabor Kovesdan 	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
424ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_context *sc;
425ad30f8e7SGabor Kovesdan 	char *ptr;
426ad30f8e7SGabor Kovesdan 	size_t sz, szpsdst, szpssrc;
427ad30f8e7SGabor Kovesdan 
428ad30f8e7SGabor Kovesdan 	szpssrc = _stdenc_get_state_size(is->is_src_encoding);
429ad30f8e7SGabor Kovesdan 	szpsdst = _stdenc_get_state_size(is->is_dst_encoding);
430ad30f8e7SGabor Kovesdan 
431ad30f8e7SGabor Kovesdan 	sz = (szpssrc + szpsdst)*2 + sizeof(struct _citrus_iconv_std_context);
432ad30f8e7SGabor Kovesdan 	sc = malloc(sz);
433ad30f8e7SGabor Kovesdan 	if (sc == NULL)
434ad30f8e7SGabor Kovesdan 		return (errno);
435ad30f8e7SGabor Kovesdan 
436ad30f8e7SGabor Kovesdan 	ptr = (char *)&sc[1];
437ad30f8e7SGabor Kovesdan 	if (szpssrc > 0)
438ad30f8e7SGabor Kovesdan 		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
439ad30f8e7SGabor Kovesdan 		    ptr, ptr+szpssrc);
440ad30f8e7SGabor Kovesdan 	else
441ad30f8e7SGabor Kovesdan 		init_encoding(&sc->sc_src_encoding, is->is_src_encoding,
442ad30f8e7SGabor Kovesdan 		    NULL, NULL);
443ad30f8e7SGabor Kovesdan 	ptr += szpssrc*2;
444ad30f8e7SGabor Kovesdan 	if (szpsdst > 0)
445ad30f8e7SGabor Kovesdan 		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
446ad30f8e7SGabor Kovesdan 		    ptr, ptr+szpsdst);
447ad30f8e7SGabor Kovesdan 	else
448ad30f8e7SGabor Kovesdan 		init_encoding(&sc->sc_dst_encoding, is->is_dst_encoding,
449ad30f8e7SGabor Kovesdan 		    NULL, NULL);
450ad30f8e7SGabor Kovesdan 
451ad30f8e7SGabor Kovesdan 	cv->cv_closure = (void *)sc;
452ad30f8e7SGabor Kovesdan 
453ad30f8e7SGabor Kovesdan 	return (0);
454ad30f8e7SGabor Kovesdan }
455ad30f8e7SGabor Kovesdan 
456ad30f8e7SGabor Kovesdan static void
_citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv * cv)457ad30f8e7SGabor Kovesdan _citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv *cv)
458ad30f8e7SGabor Kovesdan {
459ad30f8e7SGabor Kovesdan 
460ad30f8e7SGabor Kovesdan 	free(cv->cv_closure);
461ad30f8e7SGabor Kovesdan }
462ad30f8e7SGabor Kovesdan 
463ad30f8e7SGabor Kovesdan 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)464ad30f8e7SGabor Kovesdan _citrus_iconv_std_iconv_convert(struct _citrus_iconv * __restrict cv,
4651243a98eSTijl Coosemans     char * __restrict * __restrict in, size_t * __restrict inbytes,
466ad30f8e7SGabor Kovesdan     char * __restrict * __restrict out, size_t * __restrict outbytes,
467ad30f8e7SGabor Kovesdan     uint32_t flags, size_t * __restrict invalids)
468ad30f8e7SGabor Kovesdan {
469ad30f8e7SGabor Kovesdan 	const struct _citrus_iconv_std_shared *is = cv->cv_shared->ci_closure;
470ad30f8e7SGabor Kovesdan 	struct _citrus_iconv_std_context *sc = cv->cv_closure;
471ad30f8e7SGabor Kovesdan 	_csid_t csid;
472ad30f8e7SGabor Kovesdan 	_index_t idx;
4731243a98eSTijl Coosemans 	char *tmpin;
474693f88c9SKyle Evans 	size_t inval, in_mb_cur_min, szrin, szrout;
475ad30f8e7SGabor Kovesdan 	int ret, state = 0;
476ad30f8e7SGabor Kovesdan 
477ad30f8e7SGabor Kovesdan 	inval = 0;
478ad30f8e7SGabor Kovesdan 	if (in == NULL || *in == NULL) {
479ad30f8e7SGabor Kovesdan 		/* special cases */
480ad30f8e7SGabor Kovesdan 		if (out != NULL && *out != NULL) {
481ad30f8e7SGabor Kovesdan 			/* init output state and store the shift sequence */
482ad30f8e7SGabor Kovesdan 			save_encoding_state(&sc->sc_src_encoding);
483ad30f8e7SGabor Kovesdan 			save_encoding_state(&sc->sc_dst_encoding);
484ad30f8e7SGabor Kovesdan 			szrout = 0;
485ad30f8e7SGabor Kovesdan 
486ad30f8e7SGabor Kovesdan 			ret = put_state_resetx(&sc->sc_dst_encoding,
487ad30f8e7SGabor Kovesdan 			    *out, *outbytes, &szrout);
488ad30f8e7SGabor Kovesdan 			if (ret)
489ad30f8e7SGabor Kovesdan 				goto err;
490ad30f8e7SGabor Kovesdan 
491ad30f8e7SGabor Kovesdan 			if (szrout == (size_t)-2) {
492ad30f8e7SGabor Kovesdan 				/* too small to store the character */
493ad30f8e7SGabor Kovesdan 				ret = EINVAL;
494ad30f8e7SGabor Kovesdan 				goto err;
495ad30f8e7SGabor Kovesdan 			}
496ad30f8e7SGabor Kovesdan 			*out += szrout;
497ad30f8e7SGabor Kovesdan 			*outbytes -= szrout;
498ad30f8e7SGabor Kovesdan 		} else
499ad30f8e7SGabor Kovesdan 			/* otherwise, discard the shift sequence */
500ad30f8e7SGabor Kovesdan 			init_encoding_state(&sc->sc_dst_encoding);
501ad30f8e7SGabor Kovesdan 		init_encoding_state(&sc->sc_src_encoding);
502ad30f8e7SGabor Kovesdan 		*invalids = 0;
503ad30f8e7SGabor Kovesdan 		return (0);
504ad30f8e7SGabor Kovesdan 	}
505ad30f8e7SGabor Kovesdan 
506693f88c9SKyle Evans 	in_mb_cur_min = _stdenc_get_mb_cur_min(is->is_src_encoding);
507693f88c9SKyle Evans 
508ad30f8e7SGabor Kovesdan 	/* normal case */
509ad30f8e7SGabor Kovesdan 	for (;;) {
510ad30f8e7SGabor Kovesdan 		if (*inbytes == 0) {
511ad30f8e7SGabor Kovesdan 			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
512ad30f8e7SGabor Kovesdan 			if (state == _STDENC_SDGEN_INITIAL ||
513ad30f8e7SGabor Kovesdan 			    state == _STDENC_SDGEN_STABLE)
514ad30f8e7SGabor Kovesdan 				break;
515ad30f8e7SGabor Kovesdan 		}
516ad30f8e7SGabor Kovesdan 
517ad30f8e7SGabor Kovesdan 		/* save the encoding states for the error recovery */
518ad30f8e7SGabor Kovesdan 		save_encoding_state(&sc->sc_src_encoding);
519ad30f8e7SGabor Kovesdan 		save_encoding_state(&sc->sc_dst_encoding);
520ad30f8e7SGabor Kovesdan 
521ad30f8e7SGabor Kovesdan 		/* mb -> csid/index */
522ad30f8e7SGabor Kovesdan 		tmpin = *in;
523ad30f8e7SGabor Kovesdan 		szrin = szrout = 0;
524ad30f8e7SGabor Kovesdan 		ret = mbtocsx(&sc->sc_src_encoding, &csid, &idx, &tmpin,
525ad30f8e7SGabor Kovesdan 		    *inbytes, &szrin, cv->cv_shared->ci_hooks);
526693f88c9SKyle Evans 		if (ret != 0 && (ret != EILSEQ ||
527693f88c9SKyle Evans 		    !cv->cv_shared->ci_discard_ilseq)) {
528ad30f8e7SGabor Kovesdan 			goto err;
529693f88c9SKyle Evans 		} else if (ret == EILSEQ) {
530693f88c9SKyle Evans 			/*
531693f88c9SKyle Evans 			 * If //IGNORE was specified, we'll just keep crunching
532693f88c9SKyle Evans 			 * through invalid characters.
533693f88c9SKyle Evans 			 */
534693f88c9SKyle Evans 			*in += in_mb_cur_min;
535693f88c9SKyle Evans 			*inbytes -= in_mb_cur_min;
536693f88c9SKyle Evans 			restore_encoding_state(&sc->sc_src_encoding);
537693f88c9SKyle Evans 			restore_encoding_state(&sc->sc_dst_encoding);
538693f88c9SKyle Evans 			continue;
539693f88c9SKyle Evans 		}
540ad30f8e7SGabor Kovesdan 
541ad30f8e7SGabor Kovesdan 		if (szrin == (size_t)-2) {
542ad30f8e7SGabor Kovesdan 			/* incompleted character */
543ad30f8e7SGabor Kovesdan 			ret = get_state_desc_gen(&sc->sc_src_encoding, &state);
544ad30f8e7SGabor Kovesdan 			if (ret) {
545ad30f8e7SGabor Kovesdan 				ret = EINVAL;
546ad30f8e7SGabor Kovesdan 				goto err;
547ad30f8e7SGabor Kovesdan 			}
548ad30f8e7SGabor Kovesdan 			switch (state) {
549ad30f8e7SGabor Kovesdan 			case _STDENC_SDGEN_INITIAL:
550ad30f8e7SGabor Kovesdan 			case _STDENC_SDGEN_STABLE:
551ad30f8e7SGabor Kovesdan 				/* fetch shift sequences only. */
552ad30f8e7SGabor Kovesdan 				goto next;
553ad30f8e7SGabor Kovesdan 			}
554ad30f8e7SGabor Kovesdan 			ret = EINVAL;
555ad30f8e7SGabor Kovesdan 			goto err;
556ad30f8e7SGabor Kovesdan 		}
557ad30f8e7SGabor Kovesdan 		/* convert the character */
558ad30f8e7SGabor Kovesdan 		ret = do_conv(is, &csid, &idx);
559ad30f8e7SGabor Kovesdan 		if (ret) {
560ad30f8e7SGabor Kovesdan 			if (ret == E_NO_CORRESPONDING_CHAR) {
5617c5b2311SHiroki Sato 				/*
5627c5b2311SHiroki Sato 				 * GNU iconv returns EILSEQ when no
5637c5b2311SHiroki Sato 				 * corresponding character in the output.
5647c5b2311SHiroki Sato 				 * Some software depends on this behavior
5657c5b2311SHiroki Sato 				 * though this is against POSIX specification.
5667c5b2311SHiroki Sato 				 */
5677c5b2311SHiroki Sato 				if (cv->cv_shared->ci_ilseq_invalid != 0) {
5687c5b2311SHiroki Sato 					ret = EILSEQ;
5697c5b2311SHiroki Sato 					goto err;
5707c5b2311SHiroki Sato 				}
571ad30f8e7SGabor Kovesdan 				inval++;
572ad30f8e7SGabor Kovesdan 				szrout = 0;
573ad30f8e7SGabor Kovesdan 				if ((((flags & _CITRUS_ICONV_F_HIDE_INVALID) == 0) &&
574ad30f8e7SGabor Kovesdan 				    !cv->cv_shared->ci_discard_ilseq) &&
575ad30f8e7SGabor Kovesdan 				    is->is_use_invalid) {
576ad30f8e7SGabor Kovesdan 					ret = wctombx(&sc->sc_dst_encoding,
577ad30f8e7SGabor Kovesdan 					    *out, *outbytes, is->is_invalid,
578ad30f8e7SGabor Kovesdan 					    &szrout, cv->cv_shared->ci_hooks);
579ad30f8e7SGabor Kovesdan 					if (ret)
580ad30f8e7SGabor Kovesdan 						goto err;
581ad30f8e7SGabor Kovesdan 				}
582ad30f8e7SGabor Kovesdan 				goto next;
583ad30f8e7SGabor Kovesdan 			} else
584ad30f8e7SGabor Kovesdan 				goto err;
585ad30f8e7SGabor Kovesdan 		}
586ad30f8e7SGabor Kovesdan 		/* csid/index -> mb */
587ad30f8e7SGabor Kovesdan 		ret = cstombx(&sc->sc_dst_encoding,
588ad30f8e7SGabor Kovesdan 		    *out, *outbytes, csid, idx, &szrout,
589ad30f8e7SGabor Kovesdan 		    cv->cv_shared->ci_hooks);
590ad30f8e7SGabor Kovesdan 		if (ret)
591ad30f8e7SGabor Kovesdan 			goto err;
592ad30f8e7SGabor Kovesdan next:
593ad30f8e7SGabor Kovesdan 		*inbytes -= tmpin-*in; /* szrin is insufficient on \0. */
594ad30f8e7SGabor Kovesdan 		*in = tmpin;
595ad30f8e7SGabor Kovesdan 		*outbytes -= szrout;
596ad30f8e7SGabor Kovesdan 		*out += szrout;
597ad30f8e7SGabor Kovesdan 	}
598ad30f8e7SGabor Kovesdan 	*invalids = inval;
599ad30f8e7SGabor Kovesdan 
600ad30f8e7SGabor Kovesdan 	return (0);
601ad30f8e7SGabor Kovesdan 
602ad30f8e7SGabor Kovesdan err:
603ad30f8e7SGabor Kovesdan 	restore_encoding_state(&sc->sc_src_encoding);
604ad30f8e7SGabor Kovesdan 	restore_encoding_state(&sc->sc_dst_encoding);
605ad30f8e7SGabor Kovesdan 	*invalids = inval;
606ad30f8e7SGabor Kovesdan 
607ad30f8e7SGabor Kovesdan 	return (ret);
608ad30f8e7SGabor Kovesdan }
609