kernel32: Add UTF-7 support

Alex Henrie alexhenrie24 at gmail.com
Mon May 21 01:24:00 CDT 2012


Fixes bug 27388.

MSDN says that Windows Vista and later check to make sure the Unicode read and
written is valid. My implementation does not do this, but could be extended to
do so in the future.

---
 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               |  516 ++++++++++++++++++++++++++++++++++++++++
 libs/wine/wine.map             |    2 +
 6 files changed, 587 insertions(+), 6 deletions(-)
 create mode 100644 libs/wine/utf7.c

diff --git a/dlls/kernel32/locale.c b/dlls/kernel32/locale.c
index 2970856..73a4d44 100644
--- a/dlls/kernel32/locale.c
+++ b/dlls/kernel32/locale.c
@@ -1902,9 +1902,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)
         {
@@ -2010,9 +2009,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..7f41a80
--- /dev/null
+++ b/libs/wine/utf7.c
@@ -0,0 +1,516 @@
+/*
+ * 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>
+
+static BOOL write_to_w_string(WCHAR* string, int string_length, int* index, WCHAR character)
+{
+    if (*index >= string_length)
+    {
+        return TRUE;
+    }
+
+    string[*index] = character;
+    (*index)++;
+    return FALSE;
+}
+
+/* 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;
+    }
+}
+
+static BOOL write_to_c_string(char* string, int string_length, int* index, char character)
+{
+    if (*index >= string_length)
+    {
+        return TRUE;
+    }
+
+    string[*index] = character;
+    (*index)++;
+    return FALSE;
+}
+
+/* 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.5.4



More information about the wine-patches mailing list