1 /*- 2 * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/queue.h> 31 32 #include <assert.h> 33 #include <errno.h> 34 #include <langinfo.h> 35 #include <limits.h> 36 #include <string.h> 37 #include <uchar.h> 38 39 #include "../iconv/citrus_hash.h" 40 #include "../iconv/citrus_module.h" 41 #include "../iconv/citrus_iconv.h" 42 #include "xlocale_private.h" 43 44 typedef struct { 45 bool initialized; 46 struct _citrus_iconv iconv; 47 char srcbuf[MB_LEN_MAX]; 48 size_t srcbuf_len; 49 union { 50 charXX_t widechar[DSTBUF_LEN]; 51 char bytes[sizeof(charXX_t) * DSTBUF_LEN]; 52 } dstbuf; 53 size_t dstbuf_len; 54 } _ConversionState; 55 _Static_assert(sizeof(_ConversionState) <= sizeof(mbstate_t), 56 "Size of _ConversionState must not exceed mbstate_t's size."); 57 58 size_t 59 mbrtocXX_l(charXX_t * __restrict pc, const char * __restrict s, size_t n, 60 mbstate_t * __restrict ps, locale_t locale) 61 { 62 _ConversionState *cs; 63 struct _citrus_iconv *handle; 64 size_t i, retval; 65 charXX_t retchar; 66 67 FIX_LOCALE(locale); 68 if (ps == NULL) 69 ps = &locale->mbrtocXX; 70 cs = (_ConversionState *)ps; 71 handle = &cs->iconv; 72 73 /* Reinitialize mbstate_t. */ 74 if (s == NULL || !cs->initialized) { 75 if (_citrus_iconv_open(&handle, 76 nl_langinfo_l(CODESET, locale), UTF_XX_INTERNAL) != 0) { 77 cs->initialized = false; 78 errno = EINVAL; 79 return (-1); 80 } 81 handle->cv_shared->ci_discard_ilseq = true; 82 handle->cv_shared->ci_hooks = NULL; 83 cs->srcbuf_len = cs->dstbuf_len = 0; 84 cs->initialized = true; 85 if (s == NULL) 86 return (0); 87 } 88 89 /* See if we still have characters left from the previous invocation. */ 90 if (cs->dstbuf_len > 0) { 91 retval = (size_t)-3; 92 goto return_char; 93 } 94 95 /* Fill up the read buffer as far as possible. */ 96 if (n > sizeof(cs->srcbuf) - cs->srcbuf_len) 97 n = sizeof(cs->srcbuf) - cs->srcbuf_len; 98 memcpy(cs->srcbuf + cs->srcbuf_len, s, n); 99 100 /* Convert as few characters to the dst buffer as possible. */ 101 for (i = 0; ; i++) { 102 char *src, *dst; 103 size_t srcleft, dstleft, invlen; 104 int err; 105 106 src = cs->srcbuf; 107 srcleft = cs->srcbuf_len + n; 108 dst = cs->dstbuf.bytes; 109 dstleft = i * sizeof(charXX_t); 110 assert(srcleft <= sizeof(cs->srcbuf) && 111 dstleft <= sizeof(cs->dstbuf.bytes)); 112 err = _citrus_iconv_convert(handle, &src, &srcleft, 113 &dst, &dstleft, 0, &invlen); 114 cs->dstbuf_len = (dst - cs->dstbuf.bytes) / sizeof(charXX_t); 115 116 /* Got new character(s). Return the first. */ 117 if (cs->dstbuf_len > 0) { 118 assert(src - cs->srcbuf > cs->srcbuf_len); 119 retval = src - cs->srcbuf - cs->srcbuf_len; 120 cs->srcbuf_len = 0; 121 goto return_char; 122 } 123 124 /* Increase dst buffer size, to obtain the surrogate pair. */ 125 if (err == E2BIG) 126 continue; 127 128 /* Illegal sequence. */ 129 if (invlen > 0) { 130 cs->srcbuf_len = 0; 131 errno = EILSEQ; 132 return ((size_t)-1); 133 } 134 135 /* Save unprocessed remainder for the next invocation. */ 136 memmove(cs->srcbuf, src, srcleft); 137 cs->srcbuf_len = srcleft; 138 return ((size_t)-2); 139 } 140 141 return_char: 142 retchar = cs->dstbuf.widechar[0]; 143 memmove(&cs->dstbuf.widechar[0], &cs->dstbuf.widechar[1], 144 --cs->dstbuf_len * sizeof(charXX_t)); 145 if (pc != NULL) 146 *pc = retchar; 147 if (retchar == 0) 148 return (0); 149 return (retval); 150 } 151 152 size_t 153 mbrtocXX(charXX_t * __restrict pc, const char * __restrict s, size_t n, 154 mbstate_t * __restrict ps) 155 { 156 157 return (mbrtocXX_l(pc, s, n, ps, __get_locale())); 158 } 159