X-Git-Url: https://git.saurik.com/apple/libc.git/blobdiff_plain/9385eb3d10ebe5eb398c52040ec3dbfba9b0cdcf..15de9d6b4ab2de27ae24b13b7b6c4d55fffe4aef:/locale/FreeBSD/utf2.c diff --git a/locale/FreeBSD/utf2.c b/locale/FreeBSD/utf2.c index 22a6ba4..006b32f 100644 --- a/locale/FreeBSD/utf2.c +++ b/locale/FreeBSD/utf2.c @@ -1,9 +1,6 @@ /*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Paul Borman at Krystal Technologies. + * Copyright (c) 2002-2004 Tim J. Robbins + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -13,18 +10,11 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) @@ -34,116 +24,388 @@ * SUCH DAMAGE. */ -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)utf2.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD: src/lib/libc/locale/utf2.c,v 1.7 2002/03/22 21:52:18 obrien Exp $"); +// MWW: Generated by applying utf2.c.patch to utf8.c in the FreeBSD patch sets. + +#include +__FBSDID("$FreeBSD: src/lib/libc/locale/utf8.c,v 1.16 2007/10/15 09:51:30 ache Exp $"); + +#include "xlocale_private.h" -#include -#include -#include +#include +#include +#include #include +#include +#include +#include "mblocal.h" -rune_t _UTF2_sgetrune(const char *, size_t, char const **); -int _UTF2_sputrune(rune_t, char *, size_t, char **); +#define UTF2_MB_CUR_MAX 3 -static int _utf_count[16] = { - 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 2, 2, 3, 0, -}; +static size_t _UTF2_mbrtowc(wchar_t * __restrict, const char * __restrict, + size_t, mbstate_t * __restrict, locale_t); +static int _UTF2_mbsinit(const mbstate_t *, locale_t); +static size_t _UTF2_mbsnrtowcs(wchar_t * __restrict, + const char ** __restrict, size_t, size_t, + mbstate_t * __restrict, locale_t); +static size_t _UTF2_wcrtomb(char * __restrict, wchar_t, + mbstate_t * __restrict, locale_t); +static size_t _UTF2_wcsnrtombs(char * __restrict, const wchar_t ** __restrict, + size_t, size_t, mbstate_t * __restrict, locale_t); + +typedef struct { + wchar_t ch; + int want; + wchar_t lbound; +} _UTF2State; int -_UTF2_init(rl) - _RuneLocale *rl; +_UTF2_init(struct __xlocale_st_runelocale *xrl) { - rl->sgetrune = _UTF2_sgetrune; - rl->sputrune = _UTF2_sputrune; - _CurrentRuneLocale = rl; - __mb_cur_max = 3; + + xrl->__mbrtowc = _UTF2_mbrtowc; + xrl->__wcrtomb = _UTF2_wcrtomb; + xrl->__mbsinit = _UTF2_mbsinit; + xrl->__mbsnrtowcs = _UTF2_mbsnrtowcs; + xrl->__wcsnrtombs = _UTF2_wcsnrtombs; + xrl->__mb_cur_max = UTF2_MB_CUR_MAX; + /* + * UCS-4 encoding used as the internal representation, so + * slots 0x0080-0x00FF are occuped and must be excluded + * from the single byte ctype by setting the limit. + */ + xrl->__mb_sb_limit = 128; + return (0); } -rune_t -_UTF2_sgetrune(string, n, result) - const char *string; - size_t n; - char const **result; +static int +_UTF2_mbsinit(const mbstate_t *ps, locale_t loc) { - int c; - - if (n < 1 || (c = _utf_count[(*string >> 4) & 0xf]) > n) { - if (result) - *result = string; - return (_INVALID_RUNE); - } - switch (c) { - case 1: - if (result) - *result = string + 1; - return (*string & 0xff); - case 2: - if ((string[1] & 0xC0) != 0x80) - goto encoding_error; - if (result) - *result = string + 2; - return (((string[0] & 0x1F) << 6) | (string[1] & 0x3F)); - case 3: - if ((string[1] & 0xC0) != 0x80 || (string[2] & 0xC0) != 0x80) - goto encoding_error; - if (result) - *result = string + 3; - return (((string[0] & 0x1F) << 12) | ((string[1] & 0x3F) << 6) - | (string[2] & 0x3F)); - default: -encoding_error: if (result) - *result = string + 1; - return (_INVALID_RUNE); + + return (ps == NULL || ((const _UTF2State *)ps)->want == 0); +} + +static size_t +_UTF2_mbrtowc(wchar_t * __restrict pwc, const char * __restrict s, size_t n, + mbstate_t * __restrict ps, locale_t loc) +{ + _UTF2State *us; + int ch, i, mask, want; + wchar_t lbound, wch; + + us = (_UTF2State *)ps; + + if (us->want < 0 || us->want > 6) { + errno = EINVAL; + return ((size_t)-1); + } + + if (s == NULL) { + s = ""; + n = 1; + pwc = NULL; + } + + if (n == 0) + /* Incomplete multibyte sequence */ + return ((size_t)-2); + + if (us->want == 0 && ((ch = (unsigned char)*s) & ~0x7f) == 0) { + /* Fast path for plain ASCII characters. */ + if (pwc != NULL) + *pwc = ch; + return (ch != '\0' ? 1 : 0); } + + if (us->want == 0) { + /* + * Determine the number of octets that make up this character + * from the first octet, and a mask that extracts the + * interesting bits of the first octet. We already know + * the character is at least two bytes long. + * + * We also specify a lower bound for the character code to + * detect redundant, non-"shortest form" encodings. For + * example, the sequence C0 80 is _not_ a legal representation + * of the null character. This enforces a 1-to-1 mapping + * between character codes and their multibyte representations. + */ + ch = (unsigned char)*s; + if ((ch & 0x80) == 0) { + mask = 0x7f; + want = 1; + lbound = 0; + } else if ((ch & 0xe0) == 0xc0) { + mask = 0x1f; + want = 2; + lbound = 0x80; + } else if ((ch & 0xf0) == 0xe0) { + mask = 0x0f; + want = 3; + lbound = 0x800; + } else { + /* + * Malformed input; input is not UTF2. + */ + errno = EILSEQ; + return ((size_t)-1); + } + } else { + want = us->want; + lbound = us->lbound; + } + + /* + * Decode the octet sequence representing the character in chunks + * of 6 bits, most significant first. + */ + if (us->want == 0) + wch = (unsigned char)*s++ & mask; + else + wch = us->ch; + for (i = (us->want == 0) ? 1 : 0; i < MIN(want, n); i++) { + if ((*s & 0xc0) != 0x80) { + /* + * Malformed input; bad characters in the middle + * of a character. + */ + errno = EILSEQ; + return ((size_t)-1); + } + wch <<= 6; + wch |= *s++ & 0x3f; + } + if (i < want) { + /* Incomplete multibyte sequence. */ + us->want = want - i; + us->lbound = lbound; + us->ch = wch; + return ((size_t)-2); + } + if (wch < lbound) { + /* + * Malformed input; redundant encoding. + */ + errno = EILSEQ; + return ((size_t)-1); + } + if (pwc != NULL) + *pwc = wch; + us->want = 0; + return (wch == L'\0' ? 0 : want); } -int -_UTF2_sputrune(c, string, n, result) - rune_t c; - char *string, **result; - size_t n; +static size_t +_UTF2_mbsnrtowcs(wchar_t * __restrict dst, const char ** __restrict src, + size_t nms, size_t len, mbstate_t * __restrict ps, locale_t loc) { - if (c & 0xF800) { - if (n >= 3) { - if (string) { - string[0] = 0xE0 | ((c >> 12) & 0x0F); - string[1] = 0x80 | ((c >> 6) & 0x3F); - string[2] = 0x80 | ((c) & 0x3F); + _UTF2State *us; + const char *s; + size_t nchr; + wchar_t wc; + size_t nb; + + us = (_UTF2State *)ps; + + s = *src; + nchr = 0; + + if (dst == NULL) { + /* + * The fast path in the loop below is not safe if an ASCII + * character appears as anything but the first byte of a + * multibyte sequence. Check now to avoid doing it in the loop. + */ + if (nms > 0 && us->want > 0 && (signed char)*s > 0) { + errno = EILSEQ; + return ((size_t)-1); + } + for (;;) { + if (nms > 0 && (signed char)*s > 0) + /* + * Fast path for plain ASCII characters + * excluding NUL. + */ + nb = 1; + else if ((nb = _UTF2_mbrtowc(&wc, s, nms, ps, loc)) == + (size_t)-1) + /* Invalid sequence - mbrtowc() sets errno. */ + return ((size_t)-1); + else if (nb == 0 || nb == (size_t)-2) + return (nchr); + s += nb; + nms -= nb; + nchr++; + } + /*NOTREACHED*/ + } + + /* + * The fast path in the loop below is not safe if an ASCII + * character appears as anything but the first byte of a + * multibyte sequence. Check now to avoid doing it in the loop. + */ + if (nms > 0 && len > 0 && us->want > 0 && (signed char)*s > 0) { + errno = EILSEQ; + return ((size_t)-1); + } + while (len-- > 0) { + if (nms > 0 && (signed char)*s > 0) { + /* + * Fast path for plain ASCII characters + * excluding NUL. + */ + *dst = (wchar_t)*s; + nb = 1; + } else if ((nb = _UTF2_mbrtowc(dst, s, nms, ps, loc)) == + (size_t)-1) { + *src = s; + return ((size_t)-1); + } else if (nb == (size_t)-2) { + *src = s + nms; + return (nchr); + } else if (nb == 0) { + *src = NULL; + return (nchr); + } + s += nb; + nms -= nb; + nchr++; + dst++; + } + *src = s; + return (nchr); +} + +static size_t +_UTF2_wcrtomb(char * __restrict s, wchar_t wc, mbstate_t * __restrict ps, locale_t loc) +{ + _UTF2State *us; + unsigned char lead; + int i, len; + + us = (_UTF2State *)ps; + + if (us->want != 0) { + errno = EINVAL; + return ((size_t)-1); + } + + if (s == NULL) + /* Reset to initial shift state (no-op) */ + return (1); + + if ((wc & ~0x7f) == 0) { + /* Fast path for plain ASCII characters. */ + *s = (char)wc; + return (1); + } + + /* + * Determine the number of octets needed to represent this character. + * We always output the shortest sequence possible. Also specify the + * first few bits of the first octet, which contains the information + * about the sequence length. + */ + if ((wc & ~0x7f) == 0) { + lead = 0; + len = 1; + } else if ((wc & ~0x7ff) == 0) { + lead = 0xc0; + len = 2; + } else if ((wc & ~0xffff) == 0) { + lead = 0xe0; + len = 3; + } else { + errno = EILSEQ; + return ((size_t)-1); + } + + /* + * Output the octets representing the character in chunks + * of 6 bits, least significant last. The first octet is + * a special case because it contains the sequence length + * information. + */ + for (i = len - 1; i > 0; i--) { + s[i] = (wc & 0x3f) | 0x80; + wc >>= 6; + } + *s = (wc & 0xff) | lead; + + return (len); +} + +static size_t +_UTF2_wcsnrtombs(char * __restrict dst, const wchar_t ** __restrict src, + size_t nwc, size_t len, mbstate_t * __restrict ps, locale_t loc) +{ + _UTF2State *us; + char buf[MB_LEN_MAX]; + const wchar_t *s; + size_t nbytes; + size_t nb; + + us = (_UTF2State *)ps; + + if (us->want != 0) { + errno = EINVAL; + return ((size_t)-1); + } + + s = *src; + nbytes = 0; + + if (dst == NULL) { + while (nwc-- > 0) { + if (0 <= *s && *s < 0x80) + /* Fast path for plain ASCII characters. */ + nb = 1; + else if ((nb = _UTF2_wcrtomb(buf, *s, ps, loc)) == + (size_t)-1) + /* Invalid character - wcrtomb() sets errno. */ + return ((size_t)-1); + if (*s == L'\0') + return (nbytes + nb - 1); + s++; + nbytes += nb; + } + return (nbytes); + } + + while (len > 0 && nwc-- > 0) { + if (0 <= *s && *s < 0x80) { + /* Fast path for plain ASCII characters. */ + nb = 1; + *dst = *s; + } else if (len > (size_t)UTF2_MB_CUR_MAX) { + /* Enough space to translate in-place. */ + if ((nb = _UTF2_wcrtomb(dst, *s, ps, loc)) == (size_t)-1) { + *src = s; + return ((size_t)-1); } - if (result) - *result = string + 3; - } else - if (result) - *result = NULL; - - return (3); - } else - if (c & 0x0780) { - if (n >= 2) { - if (string) { - string[0] = 0xC0 | ((c >> 6) & 0x1F); - string[1] = 0x80 | ((c) & 0x3F); - } - if (result) - *result = string + 2; - } else - if (result) - *result = NULL; - return (2); } else { - if (n >= 1) { - if (string) - string[0] = c; - if (result) - *result = string + 1; - } else - if (result) - *result = NULL; - return (1); + /* + * May not be enough space; use temp. buffer. + */ + if ((nb = _UTF2_wcrtomb(buf, *s, ps, loc)) == (size_t)-1) { + *src = s; + return ((size_t)-1); + } + if (nb > (int)len) + /* MB sequence for character won't fit. */ + break; + memcpy(dst, buf, nb); } + if (*s == L'\0') { + *src = NULL; + return (nbytes + nb - 1); + } + s++; + dst += nb; + len -= nb; + nbytes += nb; + } + *src = s; + return (nbytes); }