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