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