1 /* $FreeBSD: head/lib/libiconv_modules/iconv_std/citrus_iconv_std.c 281550 2015-04-15 09:09:20Z tijl $ */
2 /* $NetBSD: citrus_iconv_std.c,v 1.16 2012/02/12 13:51:29 wiz Exp $ */
5 * Copyright (c)2003 Citrus Project,
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 #include <sys/endian.h>
32 #include <sys/queue.h>
42 #include "citrus_namespace.h"
43 #include "citrus_types.h"
44 #include "citrus_module.h"
45 #include "citrus_region.h"
46 #include "citrus_mmap.h"
47 #include "citrus_hash.h"
48 #include "citrus_iconv.h"
49 #include "citrus_stdenc.h"
50 #include "citrus_mapper.h"
51 #include "citrus_csmapper.h"
52 #include "citrus_memstream.h"
53 #include "citrus_iconv_std.h"
54 #include "citrus_esdb.h"
56 /* ---------------------------------------------------------------------- */
58 _CITRUS_ICONV_DECLS(iconv_std
);
59 _CITRUS_ICONV_DEF_OPS(iconv_std
);
62 /* ---------------------------------------------------------------------- */
65 _citrus_iconv_std_iconv_getops(struct _citrus_iconv_ops
*ops
)
68 memcpy(ops
, &_citrus_iconv_std_iconv_ops
,
69 sizeof(_citrus_iconv_std_iconv_ops
));
74 /* ---------------------------------------------------------------------- */
77 * convenience routines for stdenc.
80 save_encoding_state(struct _citrus_iconv_std_encoding
*se
)
84 memcpy(se
->se_pssaved
, se
->se_ps
,
85 _stdenc_get_state_size(se
->se_handle
));
89 restore_encoding_state(struct _citrus_iconv_std_encoding
*se
)
93 memcpy(se
->se_ps
, se
->se_pssaved
,
94 _stdenc_get_state_size(se
->se_handle
));
98 init_encoding_state(struct _citrus_iconv_std_encoding
*se
)
102 _stdenc_init_state(se
->se_handle
, se
->se_ps
);
106 mbtocsx(struct _citrus_iconv_std_encoding
*se
,
107 _csid_t
*csid
, _index_t
*idx
, char **s
, size_t n
, size_t *nresult
,
108 struct iconv_hooks
*hooks
)
111 return (_stdenc_mbtocs(se
->se_handle
, csid
, idx
, s
, n
, se
->se_ps
,
116 cstombx(struct _citrus_iconv_std_encoding
*se
,
117 char *s
, size_t n
, _csid_t csid
, _index_t idx
, size_t *nresult
,
118 struct iconv_hooks
*hooks
)
121 return (_stdenc_cstomb(se
->se_handle
, s
, n
, csid
, idx
, se
->se_ps
,
126 wctombx(struct _citrus_iconv_std_encoding
*se
,
127 char *s
, size_t n
, _wc_t wc
, size_t *nresult
,
128 struct iconv_hooks
*hooks
)
131 return (_stdenc_wctomb(se
->se_handle
, s
, n
, wc
, se
->se_ps
, nresult
,
136 put_state_resetx(struct _citrus_iconv_std_encoding
*se
, char *s
, size_t n
,
140 return (_stdenc_put_state_reset(se
->se_handle
, s
, n
, se
->se_ps
, nresult
));
144 get_state_desc_gen(struct _citrus_iconv_std_encoding
*se
, int *rstate
)
146 struct _stdenc_state_desc ssd
;
149 ret
= _stdenc_get_state_desc(se
->se_handle
, se
->se_ps
,
150 _STDENC_SDID_GENERIC
, &ssd
);
152 *rstate
= ssd
.u
.generic
.state
;
158 * init encoding context
161 init_encoding(struct _citrus_iconv_std_encoding
*se
, struct _stdenc
*cs
,
162 void *ps1
, void *ps2
)
168 se
->se_pssaved
= ps2
;
171 ret
= _stdenc_init_state(cs
, se
->se_ps
);
172 if (!ret
&& se
->se_pssaved
)
173 ret
= _stdenc_init_state(cs
, se
->se_pssaved
);
179 open_csmapper(struct _csmapper
**rcm
, const char *src
, const char *dst
,
180 unsigned long *rnorm
)
182 struct _csmapper
*cm
;
185 ret
= _csmapper_open(&cm
, src
, dst
, 0, rnorm
);
188 if (_csmapper_get_src_max(cm
) != 1 || _csmapper_get_dst_max(cm
) != 1 ||
189 _csmapper_get_state_size(cm
) != 0) {
200 close_dsts(struct _citrus_iconv_std_dst_list
*dl
)
202 struct _citrus_iconv_std_dst
*sd
;
204 while ((sd
= TAILQ_FIRST(dl
)) != NULL
) {
205 TAILQ_REMOVE(dl
, sd
, sd_entry
);
206 _csmapper_close(sd
->sd_mapper
);
212 open_dsts(struct _citrus_iconv_std_dst_list
*dl
,
213 const struct _esdb_charset
*ec
, const struct _esdb
*dbdst
)
215 struct _citrus_iconv_std_dst
*sd
, *sdtmp
;
219 sd
= malloc(sizeof(*sd
));
223 for (i
= 0; i
< dbdst
->db_num_charsets
; i
++) {
224 ret
= open_csmapper(&sd
->sd_mapper
, ec
->ec_csname
,
225 dbdst
->db_charsets
[i
].ec_csname
, &norm
);
227 sd
->sd_csid
= dbdst
->db_charsets
[i
].ec_csid
;
229 /* insert this mapper by sorted order. */
230 TAILQ_FOREACH(sdtmp
, dl
, sd_entry
) {
231 if (sdtmp
->sd_norm
> norm
) {
232 TAILQ_INSERT_BEFORE(sdtmp
, sd
,
239 TAILQ_INSERT_TAIL(dl
, sd
, sd_entry
);
240 sd
= malloc(sizeof(*sd
));
246 } else if (ret
!= ENOENT
) {
257 close_srcs(struct _citrus_iconv_std_src_list
*sl
)
259 struct _citrus_iconv_std_src
*ss
;
261 while ((ss
= TAILQ_FIRST(sl
)) != NULL
) {
262 TAILQ_REMOVE(sl
, ss
, ss_entry
);
263 close_dsts(&ss
->ss_dsts
);
269 open_srcs(struct _citrus_iconv_std_src_list
*sl
,
270 const struct _esdb
*dbsrc
, const struct _esdb
*dbdst
)
272 struct _citrus_iconv_std_src
*ss
;
273 int count
= 0, i
, ret
;
275 ss
= malloc(sizeof(*ss
));
279 TAILQ_INIT(&ss
->ss_dsts
);
281 for (i
= 0; i
< dbsrc
->db_num_charsets
; i
++) {
282 ret
= open_dsts(&ss
->ss_dsts
, &dbsrc
->db_charsets
[i
], dbdst
);
285 if (!TAILQ_EMPTY(&ss
->ss_dsts
)) {
286 ss
->ss_csid
= dbsrc
->db_charsets
[i
].ec_csid
;
287 TAILQ_INSERT_TAIL(sl
, ss
, ss_entry
);
288 ss
= malloc(sizeof(*ss
));
294 TAILQ_INIT(&ss
->ss_dsts
);
299 return (count
? 0 : ENOENT
);
307 /* do convert a character */
308 #define E_NO_CORRESPONDING_CHAR ENOENT /* XXX */
311 do_conv(const struct _citrus_iconv_std_shared
*is
,
312 _csid_t
*csid
, _index_t
*idx
)
314 struct _citrus_iconv_std_dst
*sd
;
315 struct _citrus_iconv_std_src
*ss
;
319 TAILQ_FOREACH(ss
, &is
->is_srcs
, ss_entry
) {
320 if (ss
->ss_csid
== *csid
) {
321 TAILQ_FOREACH(sd
, &ss
->ss_dsts
, sd_entry
) {
322 ret
= _csmapper_convert(sd
->sd_mapper
,
323 &tmpidx
, *idx
, NULL
);
325 case _MAPPER_CONVERT_SUCCESS
:
329 case _MAPPER_CONVERT_NONIDENTICAL
:
331 case _MAPPER_CONVERT_SRC_MORE
:
333 case _MAPPER_CONVERT_DST_MORE
:
335 case _MAPPER_CONVERT_ILSEQ
:
337 case _MAPPER_CONVERT_FATAL
:
345 return (E_NO_CORRESPONDING_CHAR
);
347 /* ---------------------------------------------------------------------- */
351 _citrus_iconv_std_iconv_init_shared(struct _citrus_iconv_shared
*ci
,
352 const char * __restrict src
, const char * __restrict dst
)
354 struct _citrus_esdb esdbdst
, esdbsrc
;
355 struct _citrus_iconv_std_shared
*is
;
358 is
= malloc(sizeof(*is
));
363 ret
= _citrus_esdb_open(&esdbsrc
, src
);
366 ret
= _citrus_esdb_open(&esdbdst
, dst
);
369 ret
= _stdenc_open(&is
->is_src_encoding
, esdbsrc
.db_encname
,
370 esdbsrc
.db_variable
, esdbsrc
.db_len_variable
);
373 ret
= _stdenc_open(&is
->is_dst_encoding
, esdbdst
.db_encname
,
374 esdbdst
.db_variable
, esdbdst
.db_len_variable
);
377 is
->is_use_invalid
= esdbdst
.db_use_invalid
;
378 is
->is_invalid
= esdbdst
.db_invalid
;
380 TAILQ_INIT(&is
->is_srcs
);
381 ret
= open_srcs(&is
->is_srcs
, &esdbsrc
, &esdbdst
);
385 _esdb_close(&esdbsrc
);
386 _esdb_close(&esdbdst
);
392 _stdenc_close(is
->is_dst_encoding
);
394 _stdenc_close(is
->is_src_encoding
);
396 _esdb_close(&esdbdst
);
398 _esdb_close(&esdbsrc
);
406 _citrus_iconv_std_iconv_uninit_shared(struct _citrus_iconv_shared
*ci
)
408 struct _citrus_iconv_std_shared
*is
= ci
->ci_closure
;
413 _stdenc_close(is
->is_src_encoding
);
414 _stdenc_close(is
->is_dst_encoding
);
415 close_srcs(&is
->is_srcs
);
420 _citrus_iconv_std_iconv_init_context(struct _citrus_iconv
*cv
)
422 const struct _citrus_iconv_std_shared
*is
= cv
->cv_shared
->ci_closure
;
423 struct _citrus_iconv_std_context
*sc
;
425 size_t sz
, szpsdst
, szpssrc
;
427 szpssrc
= _stdenc_get_state_size(is
->is_src_encoding
);
428 szpsdst
= _stdenc_get_state_size(is
->is_dst_encoding
);
430 sz
= (szpssrc
+ szpsdst
)*2 + sizeof(struct _citrus_iconv_std_context
);
435 ptr
= (char *)&sc
[1];
437 init_encoding(&sc
->sc_src_encoding
, is
->is_src_encoding
,
440 init_encoding(&sc
->sc_src_encoding
, is
->is_src_encoding
,
444 init_encoding(&sc
->sc_dst_encoding
, is
->is_dst_encoding
,
447 init_encoding(&sc
->sc_dst_encoding
, is
->is_dst_encoding
,
450 cv
->cv_closure
= (void *)sc
;
456 _citrus_iconv_std_iconv_uninit_context(struct _citrus_iconv
*cv
)
459 free(cv
->cv_closure
);
463 _citrus_iconv_std_iconv_convert(struct _citrus_iconv
* __restrict cv
,
464 char * __restrict
* __restrict in
, size_t * __restrict inbytes
,
465 char * __restrict
* __restrict out
, size_t * __restrict outbytes
,
466 uint32_t flags
, size_t * __restrict invalids
)
468 const struct _citrus_iconv_std_shared
*is
= cv
->cv_shared
->ci_closure
;
469 struct _citrus_iconv_std_context
*sc
= cv
->cv_closure
;
473 size_t inval
, szrin
, szrout
;
477 if (in
== NULL
|| *in
== NULL
) {
479 if (out
!= NULL
&& *out
!= NULL
) {
480 /* init output state and store the shift sequence */
481 save_encoding_state(&sc
->sc_src_encoding
);
482 save_encoding_state(&sc
->sc_dst_encoding
);
485 ret
= put_state_resetx(&sc
->sc_dst_encoding
,
486 *out
, *outbytes
, &szrout
);
490 if (szrout
== (size_t)-2) {
491 /* too small to store the character */
498 /* otherwise, discard the shift sequence */
499 init_encoding_state(&sc
->sc_dst_encoding
);
500 init_encoding_state(&sc
->sc_src_encoding
);
508 ret
= get_state_desc_gen(&sc
->sc_src_encoding
, &state
);
509 if (state
== _STDENC_SDGEN_INITIAL
||
510 state
== _STDENC_SDGEN_STABLE
)
514 /* save the encoding states for the error recovery */
515 save_encoding_state(&sc
->sc_src_encoding
);
516 save_encoding_state(&sc
->sc_dst_encoding
);
518 /* mb -> csid/index */
521 ret
= mbtocsx(&sc
->sc_src_encoding
, &csid
, &idx
, &tmpin
,
522 *inbytes
, &szrin
, cv
->cv_shared
->ci_hooks
);
526 if (szrin
== (size_t)-2) {
527 /* incompleted character */
528 ret
= get_state_desc_gen(&sc
->sc_src_encoding
, &state
);
534 case _STDENC_SDGEN_INITIAL
:
535 case _STDENC_SDGEN_STABLE
:
536 /* fetch shift sequences only. */
542 /* convert the character */
543 ret
= do_conv(is
, &csid
, &idx
);
545 if (ret
== E_NO_CORRESPONDING_CHAR
) {
547 * GNU iconv returns EILSEQ when no
548 * corresponding character in the output.
549 * Some software depends on this behavior
550 * though this is against POSIX specification.
552 if (cv
->cv_shared
->ci_ilseq_invalid
!= 0) {
558 if ((((flags
& _CITRUS_ICONV_F_HIDE_INVALID
) == 0) &&
559 !cv
->cv_shared
->ci_discard_ilseq
) &&
560 is
->is_use_invalid
) {
561 ret
= wctombx(&sc
->sc_dst_encoding
,
562 *out
, *outbytes
, is
->is_invalid
,
563 &szrout
, cv
->cv_shared
->ci_hooks
);
571 /* csid/index -> mb */
572 ret
= cstombx(&sc
->sc_dst_encoding
,
573 *out
, *outbytes
, csid
, idx
, &szrout
,
574 cv
->cv_shared
->ci_hooks
);
578 *inbytes
-= tmpin
-*in
; /* szrin is insufficient on \0. */
588 restore_encoding_state(&sc
->sc_src_encoding
);
589 restore_encoding_state(&sc
->sc_dst_encoding
);