am f0291ca6: Merge "Switch to a working UTF-8 mb/wc implementation."

* commit 'f0291ca6b7d0ef42a31ae76a18a99c81d6e6d345':
  Switch to a working UTF-8 mb/wc implementation.
This commit is contained in:
Elliott Hughes 2014-05-01 23:15:32 +00:00 committed by Android Git Automerger
commit 745b023b4d
6 changed files with 469 additions and 182 deletions

View File

@ -214,6 +214,7 @@ libc_bionic_src_files := \
bionic/utimes.cpp \
bionic/wait.cpp \
bionic/wchar.cpp \
bionic/wctype.cpp \
libc_upstream_freebsd_src_files := \
upstream-freebsd/lib/libc/gen/ldexp.c \
@ -356,6 +357,7 @@ libc_upstream_openbsd_src_files := \
upstream-openbsd/lib/libc/locale/wcstoumax.c \
upstream-openbsd/lib/libc/locale/wcsxfrm.c \
upstream-openbsd/lib/libc/locale/wctob.c \
upstream-openbsd/lib/libc/locale/wctomb.c \
upstream-openbsd/lib/libc/stdio/asprintf.c \
upstream-openbsd/lib/libc/stdio/clrerr.c \
upstream-openbsd/lib/libc/stdio/fdopen.c \

View File

@ -75,8 +75,12 @@ static void __locale_init() {
gLocale.int_n_sign_posn = CHAR_MAX;
}
static bool __bionic_current_locale_is_utf8 = false;
static bool __is_supported_locale(const char* locale) {
return (strcmp(locale, "") == 0 || strcmp(locale, "C") == 0 || strcmp(locale, "POSIX") == 0);
return (strcmp(locale, "") == 0 ||
strcmp(locale, "C") == 0 || strcmp(locale, "C.UTF-8") == 0 ||
strcmp(locale, "POSIX") == 0);
}
static locale_t __new_locale() {
@ -115,26 +119,24 @@ locale_t newlocale(int category_mask, const char* locale_name, locale_t /*base*/
return __new_locale();
}
char* setlocale(int category, char const* locale_name) {
char* setlocale(int category, const char* locale_name) {
// Is 'category' valid?
if (category < LC_CTYPE || category > LC_IDENTIFICATION) {
errno = EINVAL;
return NULL;
}
// Caller just wants to query the current locale?
if (locale_name == NULL) {
return const_cast<char*>("C");
// Caller wants to set the locale rather than just query?
if (locale_name != NULL) {
if (!__is_supported_locale(locale_name)) {
// We don't support this locale.
errno = ENOENT;
return NULL;
}
__bionic_current_locale_is_utf8 = (strstr(locale_name, "UTF-8") != NULL);
}
// Caller wants one of the mandatory POSIX locales?
if (__is_supported_locale(locale_name)) {
return const_cast<char*>("C");
}
// We don't support any other locales.
errno = ENOENT;
return NULL;
return const_cast<char*>(__bionic_current_locale_is_utf8 ? "C.UTF-8" : "C");
}
locale_t uselocale(locale_t new_locale) {

View File

@ -1,215 +1,307 @@
/*
* Copyright (C) 2008 The Android Open Source Project
/* $OpenBSD: citrus_utf8.c,v 1.6 2012/12/05 23:19:59 deraadt Exp $ */
/*-
* 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
* are met:
* * Redistributions of source code must retain the above copyright
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
* COPYRIGHT OWNER 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) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* 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 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)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <wchar.h>
/* stubs for wide-char functions */
//
// This file is basically OpenBSD's citrus_utf8.c but rewritten to not require a 12-byte mbstate_t
// so we're backwards-compatible with our LP32 ABI where mbstate_t was only 4 bytes. An additional
// advantage of this is that callers who don't supply their own mbstate_t won't be accessing shared
// state.
//
// We also implement the POSIX interface directly rather than being accessed via function pointers.
//
int iswalnum(wint_t wc) { return isalnum(wc); }
int iswalpha(wint_t wc) { return isalpha(wc); }
int iswblank(wint_t wc) { return isblank(wc); }
int iswcntrl(wint_t wc) { return iscntrl(wc); }
int iswdigit(wint_t wc) { return isdigit(wc); }
int iswgraph(wint_t wc) { return isgraph(wc); }
int iswlower(wint_t wc) { return islower(wc); }
int iswprint(wint_t wc) { return isprint(wc); }
int iswpunct(wint_t wc) { return ispunct(wc); }
int iswspace(wint_t wc) { return isspace(wc); }
int iswupper(wint_t wc) { return isupper(wc); }
int iswxdigit(wint_t wc) { return isxdigit(wc); }
#define ERR_ILLEGAL_SEQUENCE static_cast<size_t>(-1)
#define ERR_INCOMPLETE_SEQUENCE static_cast<size_t>(-2)
int iswctype(wint_t wc, wctype_t char_class) {
switch (char_class) {
case WC_TYPE_ALNUM: return isalnum(wc);
case WC_TYPE_ALPHA: return isalpha(wc);
case WC_TYPE_BLANK: return isblank(wc);
case WC_TYPE_CNTRL: return iscntrl(wc);
case WC_TYPE_DIGIT: return isdigit(wc);
case WC_TYPE_GRAPH: return isgraph(wc);
case WC_TYPE_LOWER: return islower(wc);
case WC_TYPE_PRINT: return isprint(wc);
case WC_TYPE_PUNCT: return ispunct(wc);
case WC_TYPE_SPACE: return isspace(wc);
case WC_TYPE_UPPER: return isupper(wc);
case WC_TYPE_XDIGIT: return isxdigit(wc);
default: return 0;
}
}
int mbsinit(const mbstate_t* /*ps*/) {
int mbsinit(const mbstate_t*) {
// We have no state, so we're always in the initial state.
return 1;
}
size_t mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t* /*ps*/) {
size_t mbrtowc(wchar_t* pwc, const char* s, size_t n, mbstate_t*) {
if (s == NULL) {
return 0;
s = "";
n = 1;
pwc = NULL;
}
if (n == 0) {
return 0;
}
if (pwc != NULL) {
*pwc = *s;
}
return (*s != 0);
}
size_t mbsnrtowcs(wchar_t* dst, const char** src, size_t n, size_t dst_size, mbstate_t* /*ps*/) {
size_t i = 0; // Number of input bytes read.
size_t o = 0; // Number of output characters written.
for (; i < n && (*src)[i] != 0; ++i) {
// TODO: UTF-8 support.
if (static_cast<uint8_t>((*src)[i]) > 0x7f) {
errno = EILSEQ;
if (dst != NULL) {
*src = &(*src)[i];
}
return static_cast<size_t>(-1);
}
if (dst != NULL) {
if (o + 1 > dst_size) {
break;
}
dst[o++] = static_cast<wchar_t>((*src)[i]);
} else {
++o;
int ch;
if (((ch = static_cast<uint8_t>(*s)) & ~0x7f) == 0) {
// Fast path for plain ASCII characters.
if (pwc != NULL) {
*pwc = ch;
}
return (ch != '\0' ? 1 : 0);
}
// If we consumed all the input, terminate the output.
if (dst != NULL && o < dst_size) {
dst[o] = 0;
}
// If we were actually consuming input, record how far we got.
if (dst != NULL) {
if ((*src)[i] != 0) {
*src = &(*src)[i]; // This is where the next call should pick up.
} else {
*src = NULL; // We consumed everything.
}
}
return o;
}
size_t mbsrtowcs(wchar_t* dst, const char** src, size_t dst_size, mbstate_t* ps) {
return mbsnrtowcs(dst, src, SIZE_MAX, dst_size, ps);
}
// 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.
int length;
int mask;
wint_t towlower(wint_t wc) {
return tolower(wc);
}
// 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.
wchar_t lower_bound;
wint_t towupper(wint_t wc) {
return toupper(wc);
}
int wctomb(char* s, wchar_t wc) {
if (s == NULL) {
return 0;
}
if (wc <= 0xff) {
*s = static_cast<char>(wc);
ch = static_cast<uint8_t>(*s);
if ((ch & 0x80) == 0) {
mask = 0x7f;
length = 1;
lower_bound = 0;
} else if ((ch & 0xe0) == 0xc0) {
mask = 0x1f;
length = 2;
lower_bound = 0x80;
} else if ((ch & 0xf0) == 0xe0) {
mask = 0x0f;
length = 3;
lower_bound = 0x800;
} else if ((ch & 0xf8) == 0xf0) {
mask = 0x07;
length = 4;
lower_bound = 0x10000;
} else {
*s = '?';
// Malformed input; input is not UTF-8. See RFC 3629.
errno = EILSEQ;
return ERR_ILLEGAL_SEQUENCE;
}
return 1;
}
size_t wcrtomb(char* s, wchar_t wc, mbstate_t* /*ps*/) {
if (s == NULL) {
char buf[MB_LEN_MAX];
return wctomb(buf, L'\0');
}
return wctomb(s, wc);
}
size_t wcsftime(wchar_t* wcs, size_t maxsize, const wchar_t* format, const struct tm* timptr) {
return strftime(reinterpret_cast<char*>(wcs), maxsize, reinterpret_cast<const char*>(format), timptr);
}
size_t wcsnrtombs(char* dst, const wchar_t** src, size_t n, size_t dst_size, mbstate_t* /*ps*/) {
size_t i = 0; // Number of input characters read.
size_t o = 0; // Number of output bytes written.
for (; i < n && (*src)[i] != 0; ++i) {
// TODO: UTF-8 support.
if ((*src)[i] > 0x7f) {
// Decode the octet sequence representing the character in chunks
// of 6 bits, most significant first.
wchar_t wch = static_cast<uint8_t>(*s++) & mask;
int i;
for (i = 1; i < MIN(length, n); i++) {
if ((*s & 0xc0) != 0x80) {
// Malformed input; bad characters in the middle of a character.
errno = EILSEQ;
if (dst != NULL) {
*src = &(*src)[i];
return ERR_ILLEGAL_SEQUENCE;
}
wch <<= 6;
wch |= *s++ & 0x3f;
}
if (i < length) {
return ERR_INCOMPLETE_SEQUENCE;
}
if (wch < lower_bound) {
// Malformed input; redundant encoding.
errno = EILSEQ;
return ERR_ILLEGAL_SEQUENCE;
}
if ((wch >= 0xd800 && wch <= 0xdfff) || wch == 0xfffe || wch == 0xffff) {
// Malformed input; invalid code points.
errno = EILSEQ;
return ERR_ILLEGAL_SEQUENCE;
}
if (pwc != NULL) {
*pwc = wch;
}
return (wch == L'\0' ? 0 : length);
}
size_t mbsnrtowcs(wchar_t* dst, const char** src, size_t nmc, size_t len, mbstate_t* ps) {
size_t i, o, r;
if (dst == NULL) {
for (i = o = 0; i < nmc; i += r, o++) {
if (static_cast<uint8_t>((*src)[i]) < 0x80) {
// Fast path for plain ASCII characters.
if ((*src)[i] == '\0') {
return o;
}
r = 1;
} else {
r = mbrtowc(NULL, *src + i, nmc - i, ps);
if (r == ERR_ILLEGAL_SEQUENCE) {
return r;
}
if (r == ERR_INCOMPLETE_SEQUENCE) {
return o;
}
if (r == 0) {
return o;
}
}
return static_cast<size_t>(-1);
}
if (dst != NULL) {
if (o + 1 > dst_size) {
break;
return o;
}
for (i = o = 0; i < nmc && o < len; i += r, o++) {
if (static_cast<uint8_t>((*src)[i]) < 0x80) {
// Fast path for plain ASCII characters.
dst[o] = (*src)[i];
if ((*src)[i] == '\0') {
*src = NULL;
return o;
}
dst[o++] = static_cast<char>((*src)[i]);
r = 1;
} else {
++o;
}
}
// If we consumed all the input, terminate the output.
if (dst != NULL && o < dst_size) {
dst[o] = 0;
}
// If we were actually consuming input, record how far we got.
if (dst != NULL) {
if ((*src)[i] != 0) {
*src = &(*src)[i]; // This is where the next call should pick up.
} else {
*src = NULL; // We consumed everything.
r = mbrtowc(dst + o, *src + i, nmc - i, ps);
if (r == ERR_ILLEGAL_SEQUENCE) {
*src += i;
return r;
}
if (r == ERR_INCOMPLETE_SEQUENCE) {
*src += nmc;
return o;
}
if (r == 0) {
*src = NULL;
return o;
}
}
}
*src += i;
return o;
}
size_t wcsrtombs(char* dst, const wchar_t** src, size_t dst_size, mbstate_t* ps) {
return wcsnrtombs(dst, src, SIZE_MAX, dst_size, ps);
size_t mbsrtowcs(wchar_t* dst, const char** src, size_t len, mbstate_t* ps) {
return mbsnrtowcs(dst, src, SIZE_MAX, len, ps);
}
wctype_t wctype(const char* property) {
static const char* const properties[WC_TYPE_MAX] = {
"<invalid>",
"alnum", "alpha", "blank", "cntrl", "digit", "graph",
"lower", "print", "punct", "space", "upper", "xdigit"
};
for (size_t i = 0; i < WC_TYPE_MAX; ++i) {
if (!strcmp(properties[i], property)) {
return static_cast<wctype_t>(i);
size_t wcrtomb(char* s, wchar_t wc, mbstate_t*) {
unsigned char lead;
int i, len;
if (s == NULL) {
// Reset to initial shift state (no-op).
return 1;
}
if ((wc & ~0x7f) == 0) {
// Fast path for plain ASCII characters.
*s = 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 if ((wc & ~0x1fffff) == 0) {
lead = 0xf0;
len = 4;
} else {
errno = EILSEQ;
return ERR_ILLEGAL_SEQUENCE;
}
// 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;
}
size_t wcsnrtombs(char* dst, const wchar_t** src, size_t nwc, size_t len, mbstate_t* ps) {
char buf[MB_LEN_MAX];
size_t i, o, r;
if (dst == NULL) {
for (i = o = 0; i < nwc; i++, o += r) {
wchar_t wc = (*src)[i];
if (wc < 0x80) {
// Fast path for plain ASCII characters.
if (wc == 0) {
return o;
}
r = 1;
} else {
r = wcrtomb(buf, wc, ps);
if (r == ERR_ILLEGAL_SEQUENCE) {
return r;
}
}
}
return o;
}
for (i = o = 0; i < nwc && o < len; i++, o += r) {
wchar_t wc = (*src)[i];
if (wc < 0x80) {
// Fast path for plain ASCII characters.
dst[o] = wc;
if (wc == 0) {
*src = NULL;
return o;
}
r = 1;
} else if (len - o >= sizeof(buf)) {
// Enough space to translate in-place.
r = wcrtomb(dst + o, wc, ps);
if (r == ERR_ILLEGAL_SEQUENCE) {
*src += i;
return r;
}
} else {
// May not be enough space; use temp buffer.
r = wcrtomb(buf, wc, ps);
if (r == ERR_ILLEGAL_SEQUENCE) {
*src += i;
return r;
}
if (r > len - o) {
break;
}
memcpy(dst + o, buf, r);
}
}
return static_cast<wctype_t>(0);
*src += i;
return o;
}
int wcwidth(wchar_t wc) {
return (wc > 0);
size_t wcsrtombs(char* dst, const wchar_t** src, size_t len, mbstate_t* ps) {
return wcsnrtombs(dst, src, SIZE_MAX, len, ps);
}

91
libc/bionic/wctype.cpp Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
* COPYRIGHT OWNER 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) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
// TODO: these only work for the ASCII range; rewrite to dlsym icu4c?
int iswalnum(wint_t wc) { return isalnum(wc); }
int iswalpha(wint_t wc) { return isalpha(wc); }
int iswblank(wint_t wc) { return isblank(wc); }
int iswcntrl(wint_t wc) { return iscntrl(wc); }
int iswdigit(wint_t wc) { return isdigit(wc); }
int iswgraph(wint_t wc) { return isgraph(wc); }
int iswlower(wint_t wc) { return islower(wc); }
int iswprint(wint_t wc) { return isprint(wc); }
int iswpunct(wint_t wc) { return ispunct(wc); }
int iswspace(wint_t wc) { return isspace(wc); }
int iswupper(wint_t wc) { return isupper(wc); }
int iswxdigit(wint_t wc) { return isxdigit(wc); }
int iswctype(wint_t wc, wctype_t char_class) {
switch (char_class) {
case WC_TYPE_ALNUM: return isalnum(wc);
case WC_TYPE_ALPHA: return isalpha(wc);
case WC_TYPE_BLANK: return isblank(wc);
case WC_TYPE_CNTRL: return iscntrl(wc);
case WC_TYPE_DIGIT: return isdigit(wc);
case WC_TYPE_GRAPH: return isgraph(wc);
case WC_TYPE_LOWER: return islower(wc);
case WC_TYPE_PRINT: return isprint(wc);
case WC_TYPE_PUNCT: return ispunct(wc);
case WC_TYPE_SPACE: return isspace(wc);
case WC_TYPE_UPPER: return isupper(wc);
case WC_TYPE_XDIGIT: return isxdigit(wc);
default: return 0;
}
}
wint_t towlower(wint_t wc) { return tolower(wc); }
wint_t towupper(wint_t wc) { return toupper(wc); }
wctype_t wctype(const char* property) {
static const char* const properties[WC_TYPE_MAX] = {
"<invalid>",
"alnum", "alpha", "blank", "cntrl", "digit", "graph",
"lower", "print", "punct", "space", "upper", "xdigit"
};
for (size_t i = 0; i < WC_TYPE_MAX; ++i) {
if (!strcmp(properties[i], property)) {
return static_cast<wctype_t>(i);
}
}
return static_cast<wctype_t>(0);
}
int wcwidth(wchar_t wc) {
return (wc > 0);
}
// TODO: implement wcsftime.
size_t wcsftime(wchar_t* wcs, size_t maxsize, const wchar_t* format, const struct tm* timptr) {
abort();
}

View File

@ -0,0 +1,47 @@
/* $OpenBSD: wctomb.c,v 1.2 2012/12/05 23:20:00 deraadt Exp $ */
/*-
* 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
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 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.
*
* 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 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)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
int
wctomb(char *s, wchar_t wchar)
{
static mbstate_t mbs;
size_t rval;
if (s == NULL) {
/* No support for state dependent encodings. */
memset(&mbs, 0, sizeof(mbs));
return (0);
}
if ((rval = wcrtomb(s, wchar, &mbs)) == (size_t)-1)
return (-1);
return ((int)rval);
}

View File

@ -18,6 +18,7 @@
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <stdint.h>
#include <wchar.h>
@ -49,18 +50,46 @@ TEST(wchar, wctomb_wcrtomb) {
EXPECT_EQ(1U, wcrtomb(bytes, L'\0', NULL));
// ...and for regular characters.
bytes[0] = 'x';
memset(bytes, 0, sizeof(bytes));
EXPECT_EQ(1, wctomb(bytes, L'h'));
EXPECT_EQ('h', bytes[0]);
bytes[0] = 'x';
memset(bytes, 0, sizeof(bytes));
EXPECT_EQ(1U, wcrtomb(bytes, L'h', NULL));
EXPECT_EQ('h', bytes[0]);
ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
uselocale(LC_GLOBAL_LOCALE);
// 1-byte UTF-8.
memset(bytes, 0, sizeof(bytes));
EXPECT_EQ(1U, wcrtomb(bytes, L'h', NULL));
EXPECT_EQ('h', bytes[0]);
// 2-byte UTF-8.
memset(bytes, 0, sizeof(bytes));
EXPECT_EQ(2U, wcrtomb(bytes, 0x00a2, NULL));
EXPECT_EQ('\xc2', bytes[0]);
EXPECT_EQ('\xa2', bytes[1]);
// 3-byte UTF-8.
memset(bytes, 0, sizeof(bytes));
EXPECT_EQ(3U, wcrtomb(bytes, 0x20ac, NULL));
EXPECT_EQ('\xe2', bytes[0]);
EXPECT_EQ('\x82', bytes[1]);
EXPECT_EQ('\xac', bytes[2]);
// 4-byte UTF-8.
memset(bytes, 0, sizeof(bytes));
EXPECT_EQ(4U, wcrtomb(bytes, 0x24b62, NULL));
EXPECT_EQ('\xf0', bytes[0]);
EXPECT_EQ('\xa4', bytes[1]);
EXPECT_EQ('\xad', bytes[2]);
EXPECT_EQ('\xa2', bytes[3]);
// Invalid code point.
EXPECT_EQ(static_cast<size_t>(-1), wcrtomb(bytes, 0xffffffff, NULL));
EXPECT_EQ(EILSEQ, errno);
}
TEST(wchar, wcstombs_wcrtombs) {
const wchar_t chars[] = { L'h', L'e', L'l', L'l', L'o', 0 };
const wchar_t bad_chars[] = { L'h', L'i', 666, 0 };
const wchar_t bad_chars[] = { L'h', L'i', 0xffffffff, 0 };
const wchar_t* src;
char bytes[BUFSIZ];
@ -212,6 +241,30 @@ TEST(wchar, mbrtowc) {
ASSERT_EQ(1U, mbrtowc(NULL, "hello", 1, NULL));
ASSERT_EQ(0U, mbrtowc(NULL, NULL, 0, NULL));
ASSERT_STREQ("C.UTF-8", setlocale(LC_CTYPE, "C.UTF-8"));
uselocale(LC_GLOBAL_LOCALE);
// 1-byte UTF-8.
ASSERT_EQ(1U, mbrtowc(out, "abcdef", 6, NULL));
ASSERT_EQ(L'a', out[0]);
// 2-byte UTF-8.
ASSERT_EQ(2U, mbrtowc(out, "\xc2\xa2" "cdef", 6, NULL));
ASSERT_EQ(0x00a2, out[0]);
// 3-byte UTF-8.
ASSERT_EQ(3U, mbrtowc(out, "\xe2\x82\xac" "def", 6, NULL));
ASSERT_EQ(0x20ac, out[0]);
// 4-byte UTF-8.
ASSERT_EQ(4U, mbrtowc(out, "\xf0\xa4\xad\xa2" "ef", 6, NULL));
ASSERT_EQ(0x24b62, out[0]);
#if __BIONIC__ // glibc allows this.
// Illegal 5-byte UTF-8.
ASSERT_EQ(static_cast<size_t>(-1), mbrtowc(out, "\xf8\xa1\xa2\xa3\xa4" "f", 6, NULL));
ASSERT_EQ(EILSEQ, errno);
#endif
// Illegal over-long sequence.
ASSERT_EQ(static_cast<size_t>(-1), mbrtowc(out, "\xf0\x82\x82\xac" "ef", 6, NULL));
ASSERT_EQ(EILSEQ, errno);
}
TEST(wchar, wcstod) {