1 /* 2 * Copyright 2011 Nexenta Systems, Inc. All rights reserved. 3 * Copyright (c) 2002-2004 Tim J. Robbins 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include "lint.h" 29 #include <errno.h> 30 #include <limits.h> 31 #include "runetype.h" 32 #include <stdlib.h> 33 #include <string.h> 34 #include <wchar.h> 35 #include "mblocal.h" 36 37 static size_t _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD, 38 const char *_RESTRICT_KYWD, 39 size_t, mbstate_t *_RESTRICT_KYWD); 40 static int _UTF8_mbsinit(const mbstate_t *); 41 static size_t _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD, 42 const char **_RESTRICT_KYWD, size_t, size_t, 43 mbstate_t *_RESTRICT_KYWD); 44 static size_t _UTF8_wcrtomb(char *_RESTRICT_KYWD, wchar_t, 45 mbstate_t *_RESTRICT_KYWD); 46 static size_t _UTF8_wcsnrtombs(char *_RESTRICT_KYWD, 47 const wchar_t **_RESTRICT_KYWD, 48 size_t, size_t, mbstate_t *_RESTRICT_KYWD); 49 50 typedef struct { 51 wchar_t ch; 52 int want; 53 wchar_t lbound; 54 } _UTF8State; 55 56 int 57 _UTF8_init(_RuneLocale *rl) 58 { 59 __mbrtowc = _UTF8_mbrtowc; 60 __wcrtomb = _UTF8_wcrtomb; 61 __mbsinit = _UTF8_mbsinit; 62 __mbsnrtowcs = _UTF8_mbsnrtowcs; 63 __wcsnrtombs = _UTF8_wcsnrtombs; 64 _CurrentRuneLocale = rl; 65 66 charset_is_ascii = 0; 67 68 /* 69 * In theory up to 6 bytes can be used for the encoding, 70 * but only encodings with more than 4 bytes are illegal. 71 */ 72 __ctype[520] = 4; 73 /* 74 * Note that the other CSWIDTH members are nonsensical for this 75 * this coding. They only are valid with EUC codings. 76 */ 77 78 return (0); 79 } 80 81 static int 82 _UTF8_mbsinit(const mbstate_t *ps) 83 { 84 85 return (ps == NULL || ((const _UTF8State *)ps)->want == 0); 86 } 87 88 static size_t 89 _UTF8_mbrtowc(wchar_t *_RESTRICT_KYWD pwc, const char *_RESTRICT_KYWD s, 90 size_t n, mbstate_t *_RESTRICT_KYWD ps) 91 { 92 _UTF8State *us; 93 int ch, i, mask, want; 94 wchar_t lbound, wch; 95 96 us = (_UTF8State *)ps; 97 98 if (us->want < 0 || us->want > 6) { 99 errno = EINVAL; 100 return ((size_t)-1); 101 } 102 103 if (s == NULL) { 104 s = ""; 105 n = 1; 106 pwc = NULL; 107 } 108 109 if (n == 0) 110 /* Incomplete multibyte sequence */ 111 return ((size_t)-2); 112 113 if (us->want == 0) { 114 /* 115 * Determine the number of octets that make up this character 116 * from the first octet, and a mask that extracts the 117 * interesting bits of the first octet. We already know 118 * the character is at least two bytes long. 119 * 120 * We also specify a lower bound for the character code to 121 * detect redundant, non-"shortest form" encodings. For 122 * example, the sequence C0 80 is _not_ a legal representation 123 * of the null character. This enforces a 1-to-1 mapping 124 * between character codes and their multibyte representations. 125 */ 126 ch = (unsigned char)*s; 127 if ((ch & 0x80) == 0) { 128 /* Fast path for plain ASCII characters. */ 129 if (pwc != NULL) 130 *pwc = ch; 131 return (ch != '\0' ? 1 : 0); 132 } 133 if ((ch & 0xe0) == 0xc0) { 134 mask = 0x1f; 135 want = 2; 136 lbound = 0x80; 137 } else if ((ch & 0xf0) == 0xe0) { 138 mask = 0x0f; 139 want = 3; 140 lbound = 0x800; 141 } else if ((ch & 0xf8) == 0xf0) { 142 mask = 0x07; 143 want = 4; 144 lbound = 0x10000; 145 #if 0 146 /* These would be illegal in the UTF-8 space */ 147 148 } else if ((ch & 0xfc) == 0xf8) { 149 mask = 0x03; 150 want = 5; 151 lbound = 0x200000; 152 } else if ((ch & 0xfe) == 0xfc) { 153 mask = 0x01; 154 want = 6; 155 lbound = 0x4000000; 156 #endif 157 } else { 158 /* 159 * Malformed input; input is not UTF-8. 160 */ 161 errno = EILSEQ; 162 return ((size_t)-1); 163 } 164 } else { 165 want = us->want; 166 lbound = us->lbound; 167 } 168 169 /* 170 * Decode the octet sequence representing the character in chunks 171 * of 6 bits, most significant first. 172 */ 173 if (us->want == 0) 174 wch = (unsigned char)*s++ & mask; 175 else 176 wch = us->ch; 177 178 for (i = (us->want == 0) ? 1 : 0; i < MIN(want, n); i++) { 179 if ((*s & 0xc0) != 0x80) { 180 /* 181 * Malformed input; bad characters in the middle 182 * of a character. 183 */ 184 errno = EILSEQ; 185 return ((size_t)-1); 186 } 187 wch <<= 6; 188 wch |= *s++ & 0x3f; 189 } 190 if (i < want) { 191 /* Incomplete multibyte sequence. */ 192 us->want = want - i; 193 us->lbound = lbound; 194 us->ch = wch; 195 return ((size_t)-2); 196 } 197 if (wch < lbound) { 198 /* 199 * Malformed input; redundant encoding. 200 */ 201 errno = EILSEQ; 202 return ((size_t)-1); 203 } 204 if (pwc != NULL) 205 *pwc = wch; 206 us->want = 0; 207 return (wch == L'\0' ? 0 : want); 208 } 209 210 static size_t 211 _UTF8_mbsnrtowcs(wchar_t *_RESTRICT_KYWD dst, const char **_RESTRICT_KYWD src, 212 size_t nms, size_t len, mbstate_t *_RESTRICT_KYWD ps) 213 { 214 _UTF8State *us; 215 const char *s; 216 size_t nchr; 217 wchar_t wc; 218 size_t nb; 219 220 us = (_UTF8State *)ps; 221 222 s = *src; 223 nchr = 0; 224 225 if (dst == NULL) { 226 /* 227 * The fast path in the loop below is not safe if an ASCII 228 * character appears as anything but the first byte of a 229 * multibyte sequence. Check now to avoid doing it in the loop. 230 */ 231 if (nms > 0 && us->want > 0 && (signed char)*s > 0) { 232 errno = EILSEQ; 233 return ((size_t)-1); 234 } 235 for (;;) { 236 if (nms > 0 && (signed char)*s > 0) 237 /* 238 * Fast path for plain ASCII characters 239 * excluding NUL. 240 */ 241 nb = 1; 242 else if ((nb = _UTF8_mbrtowc(&wc, s, nms, ps)) == 243 (size_t)-1) 244 /* Invalid sequence - mbrtowc() sets errno. */ 245 return ((size_t)-1); 246 else if (nb == 0 || nb == (size_t)-2) 247 return (nchr); 248 s += nb; 249 nms -= nb; 250 nchr++; 251 } 252 /*NOTREACHED*/ 253 } 254 255 /* 256 * The fast path in the loop below is not safe if an ASCII 257 * character appears as anything but the first byte of a 258 * multibyte sequence. Check now to avoid doing it in the loop. 259 */ 260 if (nms > 0 && len > 0 && us->want > 0 && (signed char)*s > 0) { 261 errno = EILSEQ; 262 return ((size_t)-1); 263 } 264 while (len-- > 0) { 265 if (nms > 0 && (signed char)*s > 0) { 266 /* 267 * Fast path for plain ASCII characters 268 * excluding NUL. 269 */ 270 *dst = (wchar_t)*s; 271 nb = 1; 272 } else if ((nb = _UTF8_mbrtowc(dst, s, nms, ps)) == 273 (size_t)-1) { 274 *src = s; 275 return ((size_t)-1); 276 } else if (nb == (size_t)-2) { 277 *src = s + nms; 278 return (nchr); 279 } else if (nb == 0) { 280 *src = NULL; 281 return (nchr); 282 } 283 s += nb; 284 nms -= nb; 285 nchr++; 286 dst++; 287 } 288 *src = s; 289 return (nchr); 290 } 291 292 static size_t 293 _UTF8_wcrtomb(char *_RESTRICT_KYWD s, wchar_t wc, mbstate_t *_RESTRICT_KYWD ps) 294 { 295 _UTF8State *us; 296 unsigned char lead; 297 int i, len; 298 299 us = (_UTF8State *)ps; 300 301 if (us->want != 0) { 302 errno = EINVAL; 303 return ((size_t)-1); 304 } 305 306 if (s == NULL) 307 /* Reset to initial shift state (no-op) */ 308 return (1); 309 310 /* 311 * Determine the number of octets needed to represent this character. 312 * We always output the shortest sequence possible. Also specify the 313 * first few bits of the first octet, which contains the information 314 * about the sequence length. 315 */ 316 if ((wc & ~0x7f) == 0) { 317 /* Fast path for plain ASCII characters. */ 318 *s = (char)wc; 319 return (1); 320 } else if ((wc & ~0x7ff) == 0) { 321 lead = 0xc0; 322 len = 2; 323 } else if ((wc & ~0xffff) == 0) { 324 lead = 0xe0; 325 len = 3; 326 } else if ((wc & ~0x1fffff) == 0) { 327 lead = 0xf0; 328 len = 4; 329 #if 0 330 /* Again, 5 and 6 byte encodings are simply not permitted */ 331 } else if ((wc & ~0x3ffffff) == 0) { 332 lead = 0xf8; 333 len = 5; 334 } else if ((wc & ~0x7fffffff) == 0) { 335 lead = 0xfc; 336 len = 6; 337 #endif 338 } else { 339 errno = EILSEQ; 340 return ((size_t)-1); 341 } 342 343 /* 344 * Output the octets representing the character in chunks 345 * of 6 bits, least significant last. The first octet is 346 * a special case because it contains the sequence length 347 * information. 348 */ 349 for (i = len - 1; i > 0; i--) { 350 s[i] = (wc & 0x3f) | 0x80; 351 wc >>= 6; 352 } 353 *s = (wc & 0xff) | lead; 354 355 return (len); 356 } 357 358 static size_t 359 _UTF8_wcsnrtombs(char *_RESTRICT_KYWD dst, const wchar_t **_RESTRICT_KYWD src, 360 size_t nwc, size_t len, mbstate_t *_RESTRICT_KYWD ps) 361 { 362 _UTF8State *us; 363 char buf[MB_LEN_MAX]; 364 const wchar_t *s; 365 size_t nbytes; 366 size_t nb; 367 368 us = (_UTF8State *)ps; 369 370 if (us->want != 0) { 371 errno = EINVAL; 372 return ((size_t)-1); 373 } 374 375 s = *src; 376 nbytes = 0; 377 378 if (dst == NULL) { 379 while (nwc-- > 0) { 380 if (0 <= *s && *s < 0x80) 381 /* Fast path for plain ASCII characters. */ 382 nb = 1; 383 else if ((nb = _UTF8_wcrtomb(buf, *s, ps)) == 384 (size_t)-1) 385 /* Invalid character - wcrtomb() sets errno. */ 386 return ((size_t)-1); 387 if (*s == L'\0') 388 return (nbytes + nb - 1); 389 s++; 390 nbytes += nb; 391 } 392 return (nbytes); 393 } 394 395 while (len > 0 && nwc-- > 0) { 396 if (0 <= *s && *s < 0x80) { 397 /* Fast path for plain ASCII characters. */ 398 nb = 1; 399 *dst = *s; 400 } else if (len > (size_t)MB_CUR_MAX) { 401 /* Enough space to translate in-place. */ 402 if ((nb = _UTF8_wcrtomb(dst, *s, ps)) == (size_t)-1) { 403 *src = s; 404 return ((size_t)-1); 405 } 406 } else { 407 /* 408 * May not be enough space; use temp. buffer. 409 */ 410 if ((nb = _UTF8_wcrtomb(buf, *s, ps)) == (size_t)-1) { 411 *src = s; 412 return ((size_t)-1); 413 } 414 if (nb > (int)len) 415 /* MB sequence for character won't fit. */ 416 break; 417 (void) memcpy(dst, buf, nb); 418 } 419 if (*s == L'\0') { 420 *src = NULL; 421 return (nbytes + nb - 1); 422 } 423 s++; 424 dst += nb; 425 len -= nb; 426 nbytes += nb; 427 } 428 *src = s; 429 return (nbytes); 430 }