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:
commit
745b023b4d
@ -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 \
|
||||
|
@ -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) {
|
||||
|
@ -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
91
libc/bionic/wctype.cpp
Normal 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();
|
||||
}
|
47
libc/upstream-openbsd/lib/libc/locale/wctomb.c
Normal file
47
libc/upstream-openbsd/lib/libc/locale/wctomb.c
Normal 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);
|
||||
}
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user