diff --git a/libc/bionic/wctype.cpp b/libc/bionic/wctype.cpp index f2d7861c2..05e9c907c 100644 --- a/libc/bionic/wctype.cpp +++ b/libc/bionic/wctype.cpp @@ -26,11 +26,13 @@ * SUCH DAMAGE. */ +#include + #include +#include #include #include #include -#include // TODO: these only work for the ASCII range; rewrite to dlsym icu4c? http://b/14499654 @@ -85,8 +87,8 @@ int iswctype_l(wint_t wc, wctype_t char_class, locale_t) { wint_t towlower(wint_t wc) { return tolower(wc); } wint_t towupper(wint_t wc) { return toupper(wc); } -int towupper_l(int c, locale_t) { return towupper(c); } -int towlower_l(int c, locale_t) { return towlower(c); } +wint_t towupper_l(int c, locale_t) { return towupper(c); } +wint_t towlower_l(int c, locale_t) { return towlower(c); } wctype_t wctype(const char* property) { static const char* const properties[WC_TYPE_MAX] = { @@ -109,3 +111,27 @@ wctype_t wctype_l(const char* property, locale_t) { int wcwidth(wchar_t wc) { return (wc > 0); } + +static wctrans_t wctrans_tolower = wctrans_t(1); +static wctrans_t wctrans_toupper = wctrans_t(2); + +wctrans_t wctrans(const char* name) { + if (strcmp(name, "tolower") == 0) return wctrans_tolower; + if (strcmp(name, "toupper") == 0) return wctrans_toupper; + return 0; +} + +wctrans_t wctrans_l(const char* name, locale_t) { + return wctrans(name); +} + +wint_t towctrans(wint_t c, wctrans_t t) { + if (t == wctrans_tolower) return towlower(c); + if (t == wctrans_toupper) return towupper(c); + errno = EINVAL; + return 0; +} + +wint_t towctrans_l(wint_t c, wctrans_t t, locale_t) { + return towctrans(c, t); +} diff --git a/libc/include/bits/wctype.h b/libc/include/bits/wctype.h new file mode 100644 index 000000000..8f32aba61 --- /dev/null +++ b/libc/include/bits/wctype.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _BITS_WCTYPE_H_ +#define _BITS_WCTYPE_H_ + +#include + +__BEGIN_DECLS + +typedef __WINT_TYPE__ wint_t; + +int iswalnum(wint_t); +int iswalpha(wint_t); +int iswblank(wint_t); +int iswcntrl(wint_t); +int iswdigit(wint_t); +int iswgraph(wint_t); +int iswlower(wint_t); +int iswprint(wint_t); +int iswpunct(wint_t); +int iswspace(wint_t); +int iswupper(wint_t); +int iswxdigit(wint_t); + +wint_t towlower(wint_t); +wint_t towupper(wint_t); + +typedef long wctype_t; +wctype_t wctype(const char*); +int iswctype(wint_t, wctype_t); + +typedef const void* wctrans_t; +wint_t towctrans(wint_t, wctrans_t); +wctrans_t wctrans(const char*); + +__END_DECLS + +#endif diff --git a/libc/include/wchar.h b/libc/include/wchar.h index f65c9881a..625097e45 100644 --- a/libc/include/wchar.h +++ b/libc/include/wchar.h @@ -37,10 +37,10 @@ #include #include +#include __BEGIN_DECLS -typedef __WINT_TYPE__ wint_t; typedef struct { uint8_t __seq[4]; #ifdef __LP64__ @@ -65,26 +65,11 @@ enum { WC_TYPE_MAX }; -typedef long wctype_t; - #define WEOF ((wint_t)(-1)) extern wint_t btowc(int); extern int fwprintf(FILE *, const wchar_t *, ...); extern int fwscanf(FILE *, const wchar_t *, ...); -extern int iswalnum(wint_t); -extern int iswalpha(wint_t); -extern int iswblank(wint_t); -extern int iswcntrl(wint_t); -extern int iswdigit(wint_t); -extern int iswgraph(wint_t); -extern int iswlower(wint_t); -extern int iswprint(wint_t); -extern int iswpunct(wint_t); -extern int iswspace(wint_t); -extern int iswupper(wint_t); -extern int iswxdigit(wint_t); -extern int iswctype(wint_t, wctype_t); extern wint_t fgetwc(FILE *); extern wchar_t *fgetws(wchar_t *, int, FILE *); extern wint_t fputwc(wchar_t, FILE *); @@ -101,8 +86,6 @@ extern wint_t putwc(wchar_t, FILE *); extern wint_t putwchar(wchar_t); extern int swprintf(wchar_t *, size_t, const wchar_t *, ...); extern int swscanf(const wchar_t *, const wchar_t *, ...); -extern wint_t towlower(wint_t); -extern wint_t towupper(wint_t); extern wint_t ungetwc(wint_t, FILE *); extern int vfwprintf(FILE*, const wchar_t*, va_list); extern int vfwscanf(FILE*, const wchar_t*, va_list); @@ -145,7 +128,6 @@ extern unsigned long long wcstoull(const wchar_t*, wchar_t**, int); extern int wcswidth(const wchar_t *, size_t); extern size_t wcsxfrm(wchar_t *, const wchar_t *, size_t); extern int wctob(wint_t); -extern wctype_t wctype(const char *); extern int wcwidth(wchar_t); extern wchar_t *wmemchr(const wchar_t *, wchar_t, size_t); extern int wmemcmp(const wchar_t *, const wchar_t *, size_t); @@ -168,10 +150,6 @@ extern size_t wcsxfrm_l(wchar_t *, const wchar_t *, size_t, locale_t); extern size_t wcslcat(wchar_t*, const wchar_t*, size_t); extern size_t wcslcpy(wchar_t*, const wchar_t*, size_t); -typedef void *wctrans_t; -extern wint_t towctrans(wint_t, wctrans_t) __UNAVAILABLE; -extern wctrans_t wctrans(const char*) __UNAVAILABLE; - #if __POSIX_VISIBLE >= 200809 FILE* open_wmemstream(wchar_t**, size_t*); wchar_t* wcsdup(const wchar_t*); diff --git a/libc/include/wctype.h b/libc/include/wctype.h index 1a4a05e56..12ef7cd3a 100644 --- a/libc/include/wctype.h +++ b/libc/include/wctype.h @@ -31,25 +31,31 @@ #include +#include + __BEGIN_DECLS -extern int iswalnum_l(wint_t, locale_t); -extern int iswalpha_l(wint_t, locale_t); -extern int iswblank_l(wint_t, locale_t); -extern int iswcntrl_l(wint_t, locale_t); -extern int iswdigit_l(wint_t, locale_t); -extern int iswgraph_l(wint_t, locale_t); -extern int iswlower_l(wint_t, locale_t); -extern int iswprint_l(wint_t, locale_t); -extern int iswpunct_l(wint_t, locale_t); -extern int iswspace_l(wint_t, locale_t); -extern int iswupper_l(wint_t, locale_t); -extern int iswxdigit_l(wint_t, locale_t); -extern int towlower_l(int, locale_t); -extern int towupper_l(int, locale_t); +int iswalnum_l(wint_t, locale_t); +int iswalpha_l(wint_t, locale_t); +int iswblank_l(wint_t, locale_t); +int iswcntrl_l(wint_t, locale_t); +int iswdigit_l(wint_t, locale_t); +int iswgraph_l(wint_t, locale_t); +int iswlower_l(wint_t, locale_t); +int iswprint_l(wint_t, locale_t); +int iswpunct_l(wint_t, locale_t); +int iswspace_l(wint_t, locale_t); +int iswupper_l(wint_t, locale_t); +int iswxdigit_l(wint_t, locale_t); -extern int iswctype_l(wint_t, wctype_t, locale_t); -extern wctype_t wctype_l(const char*, locale_t); +wint_t towlower_l(int, locale_t); +wint_t towupper_l(int, locale_t); + +wint_t towctrans_l(wint_t, wctrans_t, locale_t); +wctrans_t wctrans_l(const char*, locale_t); + +wctype_t wctype_l(const char*, locale_t); +int iswctype_l(wint_t, wctype_t, locale_t); __END_DECLS diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map index a1373d899..6998cab21 100644 --- a/libc/libc.arm.brillo.map +++ b/libc/libc.arm.brillo.map @@ -1291,6 +1291,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.arm.map b/libc/libc.arm.map index 4e69bf112..8628ed166 100644 --- a/libc/libc.arm.map +++ b/libc/libc.arm.map @@ -1291,6 +1291,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map index 7ea74a672..c853f73c2 100644 --- a/libc/libc.arm64.map +++ b/libc/libc.arm64.map @@ -1213,6 +1213,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.map.txt b/libc/libc.map.txt index 8d1a1f9d0..1ed29774d 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1316,6 +1316,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map index aac1d8ced..f42f5aacd 100644 --- a/libc/libc.mips.brillo.map +++ b/libc/libc.mips.brillo.map @@ -1275,6 +1275,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.mips.map b/libc/libc.mips.map index e514aa135..439c85509 100644 --- a/libc/libc.mips.map +++ b/libc/libc.mips.map @@ -1275,6 +1275,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map index 7ea74a672..c853f73c2 100644 --- a/libc/libc.mips64.map +++ b/libc/libc.mips64.map @@ -1213,6 +1213,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map index f14861d30..b1ba9885b 100644 --- a/libc/libc.x86.brillo.map +++ b/libc/libc.x86.brillo.map @@ -1273,6 +1273,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.x86.map b/libc/libc.x86.map index f6b6ea1ed..bab1737c7 100644 --- a/libc/libc.x86.map +++ b/libc/libc.x86.map @@ -1273,6 +1273,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map index 7ea74a672..c853f73c2 100644 --- a/libc/libc.x86_64.map +++ b/libc/libc.x86_64.map @@ -1213,6 +1213,10 @@ LIBC_O { sigrelse; sigset; sync_file_range; + towctrans; + towctrans_l; + wctrans; + wctrans_l; } LIBC_N; LIBC_PRIVATE { diff --git a/tests/Android.mk b/tests/Android.mk index 8e190ea0d..2db1cda6f 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -129,6 +129,7 @@ libBionicStandardTests_src_files := \ unistd_test.cpp \ utmp_test.cpp \ wchar_test.cpp \ + wctype_test.cpp \ libBionicStandardTests_cflags := \ $(test_cflags) \ diff --git a/tests/wctype_test.cpp b/tests/wctype_test.cpp new file mode 100644 index 000000000..fe2e374cb --- /dev/null +++ b/tests/wctype_test.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +class UtfLocale { + public: + UtfLocale() : l(newlocale(LC_ALL, "C.UTF-8", 0)) {} + ~UtfLocale() { freelocale(l); } + locale_t l; +}; + +static void TestIsWideFn(int fn(wint_t), + int fn_l(wint_t, locale_t), + const wchar_t* trues, + const wchar_t* falses) { + UtfLocale l; + for (const wchar_t* p = trues; *p; ++p) { + EXPECT_TRUE(fn(*p)) << *p; + EXPECT_TRUE(fn_l(*p, l.l)) << *p; + } + for (const wchar_t* p = falses; *p; ++p) { + EXPECT_FALSE(fn(*p)) << *p; + EXPECT_FALSE(fn_l(*p, l.l)) << *p; + } +} + +TEST(wctype, iswalnum) { + TestIsWideFn(iswalnum, iswalnum_l, L"1aA", L"! \b"); +} + +TEST(wctype, iswalpha) { + TestIsWideFn(iswalpha, iswalpha_l, L"aA", L"1! \b"); +} + +TEST(wctype, iswblank) { + TestIsWideFn(iswblank, iswblank_l, L" \t", L"1aA!\b"); +} + +TEST(wctype, iswcntrl) { + TestIsWideFn(iswcntrl, iswcntrl_l, L"\b", L"1aA! "); +} + +TEST(wctype, iswdigit) { + TestIsWideFn(iswdigit, iswdigit_l, L"1", L"aA! \b"); +} + +TEST(wctype, iswgraph) { + TestIsWideFn(iswgraph, iswgraph_l, L"1aA!", L" \b"); +} + +TEST(wctype, iswlower) { + TestIsWideFn(iswlower, iswlower_l, L"a", L"1A! \b"); +} + +TEST(wctype, iswprint) { + TestIsWideFn(iswprint, iswprint_l, L"1aA! ", L"\b"); +} + +TEST(wctype, iswpunct) { + TestIsWideFn(iswpunct, iswpunct_l, L"!", L"1aA \b"); +} + +TEST(wctype, iswspace) { + TestIsWideFn(iswspace, iswspace_l, L" \f\t", L"1aA!\b"); +} + +TEST(wctype, iswupper) { + TestIsWideFn(iswupper, iswupper_l, L"A", L"1a! \b"); +} + +TEST(wctype, iswxdigit) { + TestIsWideFn(iswxdigit, iswxdigit_l, L"01aA", L"xg! \b"); +} + +TEST(wctype, towlower) { + EXPECT_EQ(wint_t('!'), towlower(L'!')); + EXPECT_EQ(wint_t('a'), towlower(L'a')); + EXPECT_EQ(wint_t('a'), towlower(L'A')); +} + +TEST(wctype, towlower_l) { + UtfLocale l; + EXPECT_EQ(wint_t('!'), towlower_l(L'!', l.l)); + EXPECT_EQ(wint_t('a'), towlower_l(L'a', l.l)); + EXPECT_EQ(wint_t('a'), towlower_l(L'A', l.l)); +} + +TEST(wctype, towupper) { + EXPECT_EQ(wint_t('!'), towupper(L'!')); + EXPECT_EQ(wint_t('A'), towupper(L'a')); + EXPECT_EQ(wint_t('A'), towupper(L'A')); +} + +TEST(wctype, towupper_l) { + UtfLocale l; + EXPECT_EQ(wint_t('!'), towupper_l(L'!', l.l)); + EXPECT_EQ(wint_t('A'), towupper_l(L'a', l.l)); + EXPECT_EQ(wint_t('A'), towupper_l(L'A', l.l)); +} + +TEST(wctype, wctype) { + EXPECT_TRUE(wctype("alnum") != 0); + EXPECT_TRUE(wctype("alpha") != 0); + EXPECT_TRUE(wctype("blank") != 0); + EXPECT_TRUE(wctype("cntrl") != 0); + EXPECT_TRUE(wctype("digit") != 0); + EXPECT_TRUE(wctype("graph") != 0); + EXPECT_TRUE(wctype("lower") != 0); + EXPECT_TRUE(wctype("print") != 0); + EXPECT_TRUE(wctype("punct") != 0); + EXPECT_TRUE(wctype("space") != 0); + EXPECT_TRUE(wctype("upper") != 0); + EXPECT_TRUE(wctype("xdigit") != 0); + + EXPECT_TRUE(wctype("monkeys") == 0); +} + +TEST(wctype, wctype_l) { + UtfLocale l; + EXPECT_TRUE(wctype_l("alnum", l.l) != 0); + EXPECT_TRUE(wctype_l("alpha", l.l) != 0); + EXPECT_TRUE(wctype_l("blank", l.l) != 0); + EXPECT_TRUE(wctype_l("cntrl", l.l) != 0); + EXPECT_TRUE(wctype_l("digit", l.l) != 0); + EXPECT_TRUE(wctype_l("graph", l.l) != 0); + EXPECT_TRUE(wctype_l("lower", l.l) != 0); + EXPECT_TRUE(wctype_l("print", l.l) != 0); + EXPECT_TRUE(wctype_l("punct", l.l) != 0); + EXPECT_TRUE(wctype_l("space", l.l) != 0); + EXPECT_TRUE(wctype_l("upper", l.l) != 0); + EXPECT_TRUE(wctype_l("xdigit", l.l) != 0); + + EXPECT_TRUE(wctype_l("monkeys", l.l) == 0); +} + +TEST(wctype, iswctype) { + EXPECT_TRUE(iswctype(L'a', wctype("alnum"))); + EXPECT_TRUE(iswctype(L'1', wctype("alnum"))); + EXPECT_FALSE(iswctype(L' ', wctype("alnum"))); + + EXPECT_EQ(0, iswctype(WEOF, wctype("alnum"))); +} + +TEST(wctype, iswctype_l) { + UtfLocale l; + EXPECT_TRUE(iswctype_l(L'a', wctype_l("alnum", l.l), l.l)); + EXPECT_TRUE(iswctype_l(L'1', wctype_l("alnum", l.l), l.l)); + EXPECT_FALSE(iswctype_l(L' ', wctype_l("alnum", l.l), l.l)); + + EXPECT_EQ(0, iswctype_l(WEOF, wctype_l("alnum", l.l), l.l)); +} + +TEST(wctype, towctrans) { + EXPECT_TRUE(wctrans("tolower") != 0); + EXPECT_TRUE(wctrans("toupper") != 0); + + EXPECT_TRUE(wctrans("monkeys") == 0); +} + +TEST(wctype, towctrans_l) { + UtfLocale l; + EXPECT_TRUE(wctrans_l("tolower", l.l) != 0); + EXPECT_TRUE(wctrans_l("toupper", l.l) != 0); + + EXPECT_TRUE(wctrans_l("monkeys", l.l) == 0); +} + +TEST(wctype, wctrans) { + EXPECT_EQ(wint_t('a'), towctrans(L'A', wctrans("tolower"))); + EXPECT_EQ(WEOF, towctrans(WEOF, wctrans("tolower"))); + + EXPECT_EQ(wint_t('A'), towctrans(L'a', wctrans("toupper"))); + EXPECT_EQ(WEOF, towctrans(WEOF, wctrans("toupper"))); +} + +TEST(wctype, wctrans_l) { + UtfLocale l; + EXPECT_EQ(wint_t('a'), towctrans_l(L'A', wctrans_l("tolower", l.l), l.l)); + EXPECT_EQ(WEOF, towctrans_l(WEOF, wctrans_l("tolower", l.l), l.l)); + + EXPECT_EQ(wint_t('A'), towctrans_l(L'a', wctrans_l("toupper", l.l), l.l)); + EXPECT_EQ(WEOF, towctrans_l(WEOF, wctrans_l("toupper", l.l), l.l)); +}