kernel32: Add UTF-7 support
Alex Henrie
alexhenrie24 at gmail.com
Thu Jul 26 00:19:47 CDT 2012
Fixes bug 27388.
This patch has been in the queue since May 21. I'm open to feedback.
---
dlls/kernel32/locale.c | 10 +-
dlls/kernel32/tests/codepage.c | 62 +++++
include/wine/unicode.h | 2 +
libs/wine/Makefile.in | 1 +
libs/wine/utf7.c | 518 ++++++++++++++++++++++++++++++++++++++++
libs/wine/wine.map | 2 +
6 files changed, 589 insertions(+), 6 deletions(-)
create mode 100644 libs/wine/utf7.c
diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index 0afe89d..a334cd9 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -1917,9 +1917,8 @@ INT WINAPI MultiByteToWideChar( UINT page, DWORD flags, LPCSTR src, INT srclen,
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
- FIXME("UTF-7 not supported\n");
- SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
- return 0;
+ ret = wine_utf7_mbstowcs( src, srclen, dst, dstlen );
+ break;
case CP_UNIXCP:
if (unix_cptable)
{
@@ -2025,9 +2024,8 @@ INT WINAPI WideCharToMultiByte( UINT page, DWORD flags, LPCWSTR src, INT srclen,
SetLastError( ERROR_INVALID_FLAGS );
return 0;
}
- FIXME("UTF-7 not supported\n");
- SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
- return 0;
+ ret = wine_utf7_wcstombs( src, srclen, dst, dstlen );
+ break;
case CP_UNIXCP:
if (unix_cptable)
{
diff --git a/dlls/kernel32/tests/codepage.c b/dlls/kernel32/tests/codepage.c
index c980348..234a81a 100644
--- a/dlls/kernel32/tests/codepage.c
+++ b/dlls/kernel32/tests/codepage.c
@@ -412,6 +412,66 @@ static void test_string_conversion(LPBOOL bUsedDefaultChar)
ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError());
}
+static void test_utf7_string_conversion(void)
+{
+ /* test which ASCII characters are base64-encoded and which are not */
+ WCHAR example_0_utf16[] = {'\t',' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.',
+ '/','0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
+ '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_','`','a',
+ 'b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r',
+ 's','t','u','v','w','x','y','z','{','|','}','~',0};
+ char example_0_utf7[] = "\t +ACEAIgAjACQAJQAm-'()+ACo-+-,-./0123456789:+ADsAPAA9AD4-?+AEA-ABCDEFGHIJKLMNOPQRSTUVWXYZ+AFsAXABdAF4AXwBg-abcdefghijklmnopqrstuvwxyz+AHsAfAB9AH4-";
+ int example_0_utf7_size = sizeof(example_0_utf7);
+
+ /* this string tests:
+ - a + before a non-base64 character
+ - a + between two non-directly-encodable characters
+ - a + before a base64 character */
+ WCHAR example_1_utf16[] = {'5',' ','+',' ','(',0x0391,'+',0x0392,'+','x',')',' ',0x2262,' ',0x0391,0x0392,0};
+ int example_1_utf16_size = sizeof(example_1_utf16) / sizeof(WCHAR);
+ char example_1_utf7[] = "5 +- (+A5E-+-+A5I-+-x) +ImI- +A5EDkg-";
+ int example_1_utf7_size = sizeof(example_1_utf7);
+
+ WCHAR example_2_utf16[] = {'?',0};
+ int example_2_utf16_size = sizeof(example_2_utf16) / sizeof(WCHAR);
+ char example_2_utf7[] = "+?";
+
+ WCHAR example_3_utf16[] = {'a',0,'b',0};
+ int example_3_utf16_size = sizeof(example_3_utf16) / sizeof(WCHAR);
+ char example_3_utf7[] = "a\0b";
+ int example_3_utf7_size = sizeof(example_3_utf7);
+
+ WCHAR example_4_utf16[] = {'h','e','l','l','o',0};
+ char example_4_utf7[] = "he";
+
+ char c_buffer[200] = {0};
+ WCHAR w_buffer[200] = {0};
+ int len;
+
+ len = WideCharToMultiByte(CP_UTF7, 0, example_0_utf16, -1, c_buffer, 200, NULL, NULL);
+ ok(len == example_0_utf7_size && strcmp(c_buffer, example_0_utf7) == 0, "len=%i dst=\"%s\"\n", len, c_buffer);
+
+ len = WideCharToMultiByte(CP_UTF7, 0, example_1_utf16, -1, c_buffer, 200, NULL, NULL);
+ ok(len == example_1_utf7_size && strcmp(c_buffer, example_1_utf7) == 0, "len=%i dst=\"%s\"\n", len, c_buffer);
+
+ len = MultiByteToWideChar(CP_UTF7, 0, example_1_utf7, -1, w_buffer, 200);
+ ok(len == example_1_utf16_size && winetest_strcmpW(w_buffer, example_1_utf16) == 0, "len=%i dst=%s\n", len, wine_dbgstr_w(w_buffer));
+
+ len = MultiByteToWideChar(CP_UTF7, 0, example_2_utf7, -1, w_buffer, 200);
+ ok(len == example_2_utf16_size && winetest_strcmpW(w_buffer, example_2_utf16) == 0, "len=%i dst=%s\n", len, wine_dbgstr_w(w_buffer));
+
+ len = WideCharToMultiByte(CP_UTF7, 0, example_3_utf16, example_3_utf16_size, c_buffer, 200, NULL, NULL);
+ ok(len == example_3_utf7_size && strcmp(c_buffer, example_3_utf7) == 0, "len=%i dst=\"%s\"\n", len, c_buffer);
+
+ len = MultiByteToWideChar(CP_UTF7, 0, example_3_utf7, example_3_utf7_size, w_buffer, 200);
+ ok(len == example_3_utf16_size && winetest_strcmpW(w_buffer, example_3_utf16) == 0, "len=%i dst=%s\n", len, wine_dbgstr_w(w_buffer));
+
+ memset(c_buffer, 0, 200);
+ len = WideCharToMultiByte(CP_UTF7, 0, example_4_utf16, -1, c_buffer, 2, NULL, NULL);
+ ok(len == 0 && strcmp(c_buffer, example_4_utf7) == 0, "len=%i dst=\"%s\"\n", len, c_buffer);
+}
+
static void test_undefined_byte_char(void)
{
static const struct tag_testset {
@@ -477,5 +537,7 @@ START_TEST(codepage)
test_string_conversion(NULL);
test_string_conversion(&bUsedDefaultChar);
+ test_utf7_string_conversion();
+
test_undefined_byte_char();
}
diff --git a/include/wine/unicode.h b/include/wine/unicode.h
index 35c6166..714d26b 100644
--- a/include/wine/unicode.h
+++ b/include/wine/unicode.h
@@ -94,6 +94,8 @@ extern int wine_cp_wcstombs( const union cptable *table, int flags,
char *dst, int dstlen, const char *defchar, int *used );
extern int wine_cpsymbol_mbstowcs( const char *src, int srclen, WCHAR *dst, int dstlen );
extern int wine_cpsymbol_wcstombs( const WCHAR *src, int srclen, char *dst, int dstlen );
+extern int wine_utf7_mbstowcs( const char *src, int srclen, WCHAR *dst, int dstlen );
+extern int wine_utf7_wcstombs( const WCHAR *src, int srclen, char *dst, int dstlen );
extern int wine_utf8_mbstowcs( int flags, const char *src, int srclen, WCHAR *dst, int dstlen );
extern int wine_utf8_wcstombs( int flags, const WCHAR *src, int srclen, char *dst, int dstlen );
diff --git a/libs/wine/Makefile.in b/libs/wine/Makefile.in
index ca93d26..431fafc 100644
--- a/libs/wine/Makefile.in
+++ b/libs/wine/Makefile.in
@@ -88,6 +88,7 @@ C_SRCS = \
port.c \
sortkey.c \
string.c \
+ utf7.c \
utf8.c \
wctomb.c \
wctype.c
diff --git a/libs/wine/utf7.c b/libs/wine/utf7.c
new file mode 100644
index 0000000..fb0b825
--- /dev/null
+++ b/libs/wine/utf7.c
@@ -0,0 +1,518 @@
+/*
+ * UTF-7 support routines
+ *
+ * Copyright 2012 Alex Henrie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "wine/unicode.h"
+#include <stdlib.h>
+
+/* returns -1 on error */
+static int write_to_w_string(WCHAR* string, int string_length, int* index, WCHAR character)
+{
+ if (*index >= string_length)
+ {
+ return -1;
+ }
+
+ string[*index] = character;
+ (*index)++;
+ return 0;
+}
+
+/* UTF-7 to UTF-16 string conversion */
+/* used by MultiByteToWideChar */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+int wine_utf7_mbstowcs(const char* src, int srclen, WCHAR* dst, int dstlen)
+{
+ static const WCHAR base64_decoding_table[] = {
+ /* \0 */ -1,
+ /* \x01 */ -1,
+ /* \x02 */ -1,
+ /* \x03 */ -1,
+ /* \x04 */ -1,
+ /* \x05 */ -1,
+ /* \x06 */ -1,
+ /* \a */ -1,
+ /* \b */ -1,
+ /* \t */ -1,
+ /* \n */ -1,
+ /* \v */ -1,
+ /* \f */ -1,
+ /* \r */ -1,
+ /* \x0E */ -1,
+ /* \x0F */ -1,
+ /* \x10 */ -1,
+ /* \x11 */ -1,
+ /* \x12 */ -1,
+ /* \x13 */ -1,
+ /* \x14 */ -1,
+ /* \x15 */ -1,
+ /* \x16 */ -1,
+ /* \x17 */ -1,
+ /* \x18 */ -1,
+ /* \x19 */ -1,
+ /* \x1A */ -1,
+ /* \e */ -1,
+ /* \x1C */ -1,
+ /* \x1D */ -1,
+ /* \x1E */ -1,
+ /* \x1F */ -1,
+ /* */ -1,
+ /* ! */ -1,
+ /* " */ -1,
+ /* # */ -1,
+ /* $ */ -1,
+ /* % */ -1,
+ /* & */ -1,
+ /* ' */ -1,
+ /* ( */ -1,
+ /* ) */ -1,
+ /* * */ -1,
+ /* + */ 62,
+ /* , */ -1,
+ /* - */ -1,
+ /* . */ -1,
+ /* / */ 63,
+ /* 0 */ 52,
+ /* 1 */ 53,
+ /* 2 */ 54,
+ /* 3 */ 55,
+ /* 4 */ 56,
+ /* 5 */ 57,
+ /* 6 */ 58,
+ /* 7 */ 59,
+ /* 8 */ 60,
+ /* 9 */ 61,
+ /* : */ -1,
+ /* ; */ -1,
+ /* < */ -1,
+ /* = */ -1,
+ /* > */ -1,
+ /* ? */ -1,
+ /* @ */ -1,
+ /* A */ 0,
+ /* B */ 1,
+ /* C */ 2,
+ /* D */ 3,
+ /* E */ 4,
+ /* F */ 5,
+ /* G */ 6,
+ /* H */ 7,
+ /* I */ 8,
+ /* J */ 9,
+ /* K */ 10,
+ /* L */ 11,
+ /* M */ 12,
+ /* N */ 13,
+ /* O */ 14,
+ /* P */ 15,
+ /* Q */ 16,
+ /* R */ 17,
+ /* S */ 18,
+ /* T */ 19,
+ /* U */ 20,
+ /* V */ 21,
+ /* W */ 22,
+ /* X */ 23,
+ /* Y */ 24,
+ /* Z */ 25,
+ /* [ */ -1,
+ /* \ */ -1,
+ /* ] */ -1,
+ /* ^ */ -1,
+ /* _ */ -1,
+ /* ` */ -1,
+ /* a */ 26,
+ /* b */ 27,
+ /* c */ 28,
+ /* d */ 29,
+ /* e */ 30,
+ /* f */ 31,
+ /* g */ 32,
+ /* h */ 33,
+ /* i */ 34,
+ /* j */ 35,
+ /* k */ 36,
+ /* l */ 37,
+ /* m */ 38,
+ /* n */ 39,
+ /* o */ 40,
+ /* p */ 41,
+ /* q */ 42,
+ /* r */ 43,
+ /* s */ 44,
+ /* t */ 45,
+ /* u */ 46,
+ /* v */ 47,
+ /* w */ 48,
+ /* x */ 49,
+ /* y */ 50,
+ /* z */ 51
+ };
+
+ int source_index = 0;
+ int dest_index = 0;
+ BOOL dst_was_null = FALSE;
+
+ if (dst == NULL)
+ {
+ dst = malloc(srclen * sizeof(WCHAR));
+ dstlen = srclen;
+ dst_was_null = TRUE;
+ }
+
+ do
+ {
+ if (src[source_index] == 0 && srclen == -1)
+ {
+ /* when srclen=-1, terminate at the first null character found */
+ if (write_to_w_string(dst, dstlen, &dest_index, 0)) return -1;
+ break;
+ }
+ else if (src[source_index] == '+')
+ {
+ WCHAR byte_pair = 0;
+ short offset = 0;
+
+ source_index++; /* skip the + sign */
+
+ if (src[source_index] == '-')
+ {
+ /* just a plus sign escaped as +- */
+ if (write_to_w_string(dst, dstlen, &dest_index, '+')) return -1;
+ source_index++;
+ continue;
+ }
+
+ for (;;)
+ {
+ WCHAR sextet = src[source_index];
+ if (sextet == '-')
+ {
+ /* skip over the - and signal end of base64 decoding */
+ source_index++;
+ sextet = -1;
+ }
+ else if (sextet <= 'z')
+ {
+ sextet = base64_decoding_table[sextet];
+ }
+ else
+ {
+ sextet = -1;
+ }
+
+ if (sextet == (WCHAR)-1)
+ {
+ /* -1 means that the next character of src is not part of a base64 sequence */
+ /* in other words, all sextets in this base64 sequence have been written */
+ if (byte_pair != 0)
+ {
+ if (write_to_w_string(dst, dstlen, &dest_index, byte_pair)) return -1;
+ }
+ break;
+ }
+
+ if (offset > 0)
+ {
+ byte_pair |= (sextet << 10) >> offset;
+ }
+ else
+ {
+ byte_pair |= sextet << (10 - offset);
+ }
+ offset += 6;
+ if (offset > 15)
+ {
+ /* this byte pair is done */
+ if (write_to_w_string(dst, dstlen, &dest_index, byte_pair)) return -1;
+ byte_pair = 0;
+ /* back up the offset to begin writing to the next byte pair,
+ including writing any part of the current sextet that didn't fit in the last byte pair */
+ offset -= 22;
+ }
+ else
+ {
+ /* this sextet is done */
+ source_index++;
+ }
+ }
+ }
+ else
+ {
+ if (write_to_w_string(dst, dstlen, &dest_index, src[source_index])) return -1;
+ source_index++;
+ }
+ } while (srclen == -1 || source_index < srclen);
+
+ if (dst_was_null)
+ {
+ free(dst);
+ }
+
+ return dest_index;
+}
+
+static BOOL can_directly_encode(WCHAR codepoint)
+{
+ static const BOOL directly_encodable_ascii[] = {
+ /* \0 */ TRUE, /* special case */
+ /* \x01 */ FALSE,
+ /* \x02 */ FALSE,
+ /* \x03 */ FALSE,
+ /* \x04 */ FALSE,
+ /* \x05 */ FALSE,
+ /* \x06 */ FALSE,
+ /* \a */ FALSE,
+ /* \b */ FALSE,
+ /* \t */ TRUE,
+ /* \n */ TRUE,
+ /* \v */ FALSE,
+ /* \f */ FALSE,
+ /* \r */ TRUE,
+ /* \x0E */ FALSE,
+ /* \x0F */ FALSE,
+ /* \x10 */ FALSE,
+ /* \x11 */ FALSE,
+ /* \x12 */ FALSE,
+ /* \x13 */ FALSE,
+ /* \x14 */ FALSE,
+ /* \x15 */ FALSE,
+ /* \x16 */ FALSE,
+ /* \x17 */ FALSE,
+ /* \x18 */ FALSE,
+ /* \x19 */ FALSE,
+ /* \x1A */ FALSE,
+ /* \e */ FALSE,
+ /* \x1C */ FALSE,
+ /* \x1D */ FALSE,
+ /* \x1E */ FALSE,
+ /* \x1F */ FALSE,
+ /* */ TRUE,
+ /* ! */ FALSE,
+ /* " */ FALSE,
+ /* # */ FALSE,
+ /* $ */ FALSE,
+ /* % */ FALSE,
+ /* & */ FALSE,
+ /* ' */ TRUE,
+ /* ( */ TRUE,
+ /* ) */ TRUE,
+ /* * */ FALSE,
+ /* + */ TRUE, /* special case */
+ /* , */ TRUE,
+ /* - */ TRUE,
+ /* . */ TRUE,
+ /* / */ TRUE,
+ /* 0 */ TRUE,
+ /* 1 */ TRUE,
+ /* 2 */ TRUE,
+ /* 3 */ TRUE,
+ /* 4 */ TRUE,
+ /* 5 */ TRUE,
+ /* 6 */ TRUE,
+ /* 7 */ TRUE,
+ /* 8 */ TRUE,
+ /* 9 */ TRUE,
+ /* : */ TRUE,
+ /* ; */ FALSE,
+ /* < */ FALSE,
+ /* = */ FALSE,
+ /* > */ FALSE,
+ /* ? */ TRUE,
+ /* @ */ FALSE,
+ /* A */ TRUE,
+ /* B */ TRUE,
+ /* C */ TRUE,
+ /* D */ TRUE,
+ /* E */ TRUE,
+ /* F */ TRUE,
+ /* G */ TRUE,
+ /* H */ TRUE,
+ /* I */ TRUE,
+ /* J */ TRUE,
+ /* K */ TRUE,
+ /* L */ TRUE,
+ /* M */ TRUE,
+ /* N */ TRUE,
+ /* O */ TRUE,
+ /* P */ TRUE,
+ /* Q */ TRUE,
+ /* R */ TRUE,
+ /* S */ TRUE,
+ /* T */ TRUE,
+ /* U */ TRUE,
+ /* V */ TRUE,
+ /* W */ TRUE,
+ /* X */ TRUE,
+ /* Y */ TRUE,
+ /* Z */ TRUE,
+ /* [ */ FALSE,
+ /* \ */ FALSE,
+ /* ] */ FALSE,
+ /* ^ */ FALSE,
+ /* _ */ FALSE,
+ /* ` */ FALSE,
+ /* a */ TRUE,
+ /* b */ TRUE,
+ /* c */ TRUE,
+ /* d */ TRUE,
+ /* e */ TRUE,
+ /* f */ TRUE,
+ /* g */ TRUE,
+ /* h */ TRUE,
+ /* i */ TRUE,
+ /* j */ TRUE,
+ /* k */ TRUE,
+ /* l */ TRUE,
+ /* m */ TRUE,
+ /* n */ TRUE,
+ /* o */ TRUE,
+ /* p */ TRUE,
+ /* q */ TRUE,
+ /* r */ TRUE,
+ /* s */ TRUE,
+ /* t */ TRUE,
+ /* u */ TRUE,
+ /* v */ TRUE,
+ /* w */ TRUE,
+ /* x */ TRUE,
+ /* y */ TRUE,
+ /* z */ TRUE,
+ /* { */ FALSE,
+ /* | */ FALSE,
+ /* } */ FALSE,
+ /* ~ */ FALSE
+ };
+
+ if (codepoint <= '~')
+ {
+ return directly_encodable_ascii[codepoint];
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* returns -1 on error */
+static int write_to_c_string(char* string, int string_length, int* index, char character)
+{
+ if (*index >= string_length)
+ {
+ return -1;
+ }
+
+ string[*index] = character;
+ (*index)++;
+ return 0;
+}
+
+/* UTF-16 to UTF-7 string conversion */
+/* used by WideCharToMultiByte */
+/* return -1 on dst buffer overflow, -2 on invalid input char */
+int wine_utf7_wcstombs(const WCHAR* src, int srclen, char* dst, int dstlen)
+{
+ static const char base64_encoding_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ int source_index = 0;
+ int dest_index = 0;
+ BOOL dst_was_null = FALSE;
+
+ if (dst == NULL)
+ {
+ dst = malloc((srclen / sizeof(WCHAR)) * 5);
+ dstlen = srclen * 5;
+ dst_was_null = TRUE;
+ }
+
+ do
+ {
+ if (src[source_index] == 0 && srclen == -1)
+ {
+ /* when srclen=-1, terminate at the first null character found */
+ if (write_to_c_string(dst, dstlen, &dest_index, 0)) return -1;
+ break;
+ }
+ else if (src[source_index] == '+')
+ {
+ if (write_to_c_string(dst, dstlen, &dest_index, '+')) return -1;
+ if (write_to_c_string(dst, dstlen, &dest_index, '-')) return -1;
+ source_index++;
+ }
+ else if (can_directly_encode(src[source_index]))
+ {
+ if (write_to_c_string(dst, dstlen, &dest_index, src[source_index])) return -1;
+ source_index++;
+ }
+ else
+ {
+ unsigned int offset;
+ WCHAR char1;
+ WCHAR char2;
+
+ offset = 16;
+ char2 = src[source_index];
+ source_index++;
+
+ if (write_to_c_string(dst, dstlen, &dest_index, '+')) return -1;
+
+ for (;;)
+ {
+ unsigned int chars_back_to_back;
+
+ if (offset > 15)
+ {
+ if (char2 == 0)
+ {
+ /* signal to end; the next character of src is directly encodable */
+ break;
+ }
+ char1 = char2;
+ if (can_directly_encode(src[source_index]))
+ {
+ /* do not include the next character of src in the base64 sequence */
+ /* pad the bits of the last character to be encoded with zeroes if needed */
+ char2 = 0;
+ }
+ else
+ {
+ /* claim the next character for inclusion in the base64 sequence */
+ char2 = src[source_index];
+ source_index++;
+ }
+ offset -= 16;
+ }
+
+ chars_back_to_back = ((unsigned int)char1 << 16) | (unsigned int)char2;
+ if (write_to_c_string(dst, dstlen, &dest_index, base64_encoding_table[(chars_back_to_back << offset) >> 26])) return -1;
+ offset += 6;
+ }
+
+ /* Windows always explicitly terminates the base64 sequence even though RFC 2152 (page 3, rule 2) does not require this */
+ if (write_to_c_string(dst, dstlen, &dest_index, '-')) return -1;
+ }
+ }
+ while (srclen == -1 || source_index < srclen);
+
+ if (dst_was_null)
+ {
+ free(dst);
+ }
+
+ return dest_index;
+}
diff --git a/libs/wine/wine.map b/libs/wine/wine.map
index 2159fac..3e0e6db 100644
--- a/libs/wine/wine.map
+++ b/libs/wine/wine.map
@@ -114,6 +114,8 @@ WINE_1.0
wine_set_fs;
wine_set_gs;
wine_switch_to_stack;
+ wine_utf7_mbstowcs;
+ wine_utf7_wcstombs;
wine_utf8_mbstowcs;
wine_utf8_wcstombs;
wine_wctype_table;
--
1.7.9.5
More information about the wine-patches
mailing list