[KERNEL32] Implement MB_USEGLYPHCHARS flag for MultiByteToWideChar. Add checks for...
[reactos.git] / reactos / dll / win32 / kernel32 / winnls / string / nls.c
index 8bfc515..8a44bf8 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
- * FILE:            dll/win32/kernel32/misc/nls.c
+ * FILE:            dll/win32/kernel32/winnls/string/nls.c
  * PURPOSE:         National Language Support
  * PROGRAMMER:      Filip Navara
  *                  Hartmut Birr
@@ -14,6 +14,7 @@
 /* INCLUDES *******************************************************************/
 
 #include <k32.h>
+
 #define NDEBUG
 #include <debug.h>
 
@@ -88,21 +89,22 @@ NlsInit(VOID)
     }
 
     /* Setup ANSI code page. */
-    AnsiCodePage.CodePage = CP_ACP;
     AnsiCodePage.SectionHandle = NULL;
     AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
 
     RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
                          &AnsiCodePage.CodePageTable);
+    AnsiCodePage.CodePage = AnsiCodePage.CodePageTable.CodePage;
+    
     InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
 
     /* Setup OEM code page. */
-    OemCodePage.CodePage = CP_OEMCP;
     OemCodePage.SectionHandle = NULL;
     OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
 
     RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
                          &OemCodePage.CodePageTable);
+    OemCodePage.CodePage = OemCodePage.CodePageTable.CodePage;
     InsertTailList(&CodePageListHead, &OemCodePage.Entry);
 
     return TRUE;
@@ -198,8 +200,15 @@ IntGetCodePageEntry(UINT CodePage)
     WCHAR FileName[MAX_PATH + 1];
     UINT FileNamePos;
     PCODEPAGE_ENTRY CodePageEntry;
-
-    if (CodePage == CP_THREAD_ACP)
+    if (CodePage == CP_ACP)
+    {
+        return &AnsiCodePage;
+    }
+    else if (CodePage == CP_OEMCP)
+    {
+        return &OemCodePage;
+    }
+    else if (CodePage == CP_THREAD_ACP)
     {
         if (!GetLocaleInfoW(GetThreadLocale(),
                             LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
@@ -209,6 +218,8 @@ IntGetCodePageEntry(UINT CodePage)
             /* Last error is set by GetLocaleInfoW. */
             return NULL;
         }
+        if (CodePage == 0)
+            return &AnsiCodePage;
     }
     else if (CodePage == CP_MACCP)
     {
@@ -425,6 +436,7 @@ IntMultiByteToWideCharCP(UINT CodePage,
 {
     PCODEPAGE_ENTRY CodePageEntry;
     PCPTABLEINFO CodePageTable;
+    PUSHORT MultiByteTable;
     LPCSTR TempString;
     INT TempLength;
 
@@ -435,18 +447,34 @@ IntMultiByteToWideCharCP(UINT CodePage,
         SetLastError(ERROR_INVALID_PARAMETER);
         return 0;
     }
+
     CodePageTable = &CodePageEntry->CodePageTable;
 
-    /* Different handling for DBCS code pages. */
-    if (CodePageTable->MaximumCharacterSize > 1)
+    /* If MB_USEGLYPHCHARS flag present and glyph table present */
+    if ((Flags & MB_USEGLYPHCHARS) && CodePageTable->MultiByteTable[256])
+    {
+        /* Use glyph table */
+        MultiByteTable = CodePageTable->MultiByteTable + 256 + 1;
+    }
+    else
     {
-        /* FIXME */
+        MultiByteTable = CodePageTable->MultiByteTable;
+    }
 
+    /* Different handling for DBCS code pages. */
+    if (CodePageTable->DBCSCodePage)
+    {
         UCHAR Char;
         USHORT DBCSOffset;
         LPCSTR MbsEnd = MultiByteString + MultiByteCount;
         INT Count;
 
+        if (Flags & MB_ERR_INVALID_CHARS)
+        {
+            /* FIXME */
+            DPRINT1("IntMultiByteToWideCharCP: MB_ERR_INVALID_CHARS case not implemented!\n");
+        }
+        
         /* Does caller query for output buffer size? */
         if (WideCharCount == 0)
         {
@@ -483,7 +511,7 @@ IntMultiByteToWideCharCP(UINT CodePage,
 
             if (!DBCSOffset)
             {
-                *WideCharString++ = CodePageTable->MultiByteTable[Char];
+                *WideCharString++ = MultiByteTable[Char];
                 continue;
             }
 
@@ -508,9 +536,12 @@ IntMultiByteToWideCharCP(UINT CodePage,
                  TempLength > 0;
                  TempString++, TempLength--)
             {
-                if (CodePageTable->MultiByteTable[(UCHAR)*TempString] ==
-                    CodePageTable->UniDefaultChar &&
-                    *TempString != CodePageEntry->CodePageTable.DefaultChar)
+                USHORT WideChar = MultiByteTable[(UCHAR)*TempString];
+
+                if ((WideChar == CodePageTable->UniDefaultChar &&
+                    *TempString != CodePageEntry->CodePageTable.TransUniDefaultChar) ||
+                    /* "Private Use" characters */
+                    (WideChar >= 0xE000 && WideChar <= 0xF8FF))
                 {
                     SetLastError(ERROR_NO_UNICODE_TRANSLATION);
                     return 0;
@@ -527,7 +558,7 @@ IntMultiByteToWideCharCP(UINT CodePage,
             TempLength > 0;
             MultiByteString++, TempLength--)
         {
-            *WideCharString++ = CodePageTable->MultiByteTable[(UCHAR)*MultiByteString];
+            *WideCharString++ = MultiByteTable[(UCHAR)*MultiByteString];
         }
 
         /* Adjust buffer size. Wine trick ;-) */
@@ -551,10 +582,10 @@ IntMultiByteToWideCharCP(UINT CodePage,
 
 static
 INT
-WINAPI 
-IntMultiByteToWideCharSYMBOL(DWORD Flags, 
+WINAPI
+IntMultiByteToWideCharSYMBOL(DWORD Flags,
                              LPCSTR MultiByteString,
-                             INT MultiByteCount, 
+                             INT MultiByteCount,
                              LPWSTR WideCharString,
                              INT WideCharCount)
 {
@@ -569,7 +600,7 @@ IntMultiByteToWideCharSYMBOL(DWORD Flags,
         return 0;
     }
 
-    if (WideCharCount == 0) 
+    if (WideCharCount == 0)
     {
         return MultiByteCount;
     }
@@ -588,7 +619,7 @@ IntMultiByteToWideCharSYMBOL(DWORD Flags,
             WideCharString[Count] = Char + 0xf000;
         }
     }
-    if (MultiByteCount > WideCharMaxLen) 
+    if (MultiByteCount > WideCharMaxLen)
     {
         SetLastError(ERROR_INSUFFICIENT_BUFFER);
         return 0;
@@ -624,7 +655,7 @@ IntWideCharToMultiByteSYMBOL(DWORD Flags,
     }
 
 
-    if (MultiByteCount == 0) 
+    if (MultiByteCount == 0)
     {
         return WideCharCount;
     }
@@ -635,11 +666,11 @@ IntWideCharToMultiByteSYMBOL(DWORD Flags,
         Char = WideCharString[Count];
         if (Char < 0x20)
         {
-            MultiByteString[Count] = Char;
+            MultiByteString[Count] = (CHAR)Char;
         }
-        else 
+        else
         {
-            if ((Char>=0xf020)&&(Char<0xf100))
+            if ((Char >= 0xf020) && (Char < 0xf100))
             {
                 MultiByteString[Count] = Char - 0xf000;
             }
@@ -650,7 +681,8 @@ IntWideCharToMultiByteSYMBOL(DWORD Flags,
             }
         }
     }
-    if (WideCharCount > MaxLen) 
+
+    if (WideCharCount > MaxLen)
     {
         SetLastError(ERROR_INSUFFICIENT_BUFFER);
         return 0;
@@ -1013,7 +1045,7 @@ IntWideCharToMultiByteCP(UINT CodePage,
             if (DefaultChar)
                 DefChar = *DefaultChar;
             else
-                DefChar = CodePageTable->TransDefaultChar;
+                DefChar = (CHAR)CodePageTable->TransDefaultChar;
 
             /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
             for (TempLength = MultiByteCount;
@@ -1259,168 +1291,121 @@ IsValidCodePage(UINT CodePage)
     return GetCPFileNameFromRegistry(CodePage, NULL, 0);
 }
 
-static const signed char 
-base64inv[] = 
+static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
 {
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 
-    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 
-    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
-    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 
-    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
-};
-
-static VOID Utf7Base64Decode(BYTE *pbDest, LPCSTR pszSrc, INT cchSrc)
-{
-    INT i, j, n;
-    BYTE b;
-
-    for(i = 0; i < cchSrc / 4 * 4; i += 4)
+    if (dstlen > 0)
     {
-        for(j = n = 0; j < 4; )
-        {
-            b = (BYTE) base64inv[(BYTE) *pszSrc++];
-            n |= (((INT) b) << ((3 - j) * 6));
-            j++;
-        }
-        for(j = 0; j < 3; j++)
-            *pbDest++ = (BYTE) ((n >> (8 * (2 - j))) & 0xFF);
-    }
-    for(j = n = 0; j < cchSrc % 4; )
-    {
-        b = (BYTE) base64inv[(BYTE) *pszSrc++];
-        n |= (((INT) b) << ((3 - j) * 6));
-        j++;
-    }
-    for(j = 0; j < ((cchSrc % 4) * 6 / 8); j++)
-        *pbDest++ = (BYTE) ((n >> (8 * (2 - j))) & 0xFF);
-}
+        if (*index >= dstlen)
+            return FALSE;
 
-static VOID myswab(LPVOID pv, INT cw)
-{
-    LPBYTE pb = (LPBYTE) pv;
-    BYTE b;
-    while(cw > 0)
-    {
-        b = *pb;
-        *pb = pb[1];
-        pb[1] = b;
-        pb += 2;
-        cw--;
+        dst[*index] = character;
     }
+
+    (*index)++;
+
+    return TRUE;
 }
 
-static INT Utf7ToWideCharSize(LPCSTR pszUtf7, INT cchUtf7)
+static INT Utf7ToWideChar(const char *src, int srclen, WCHAR *dst, int dstlen)
 {
-    INT n, c, cch;
-    CHAR ch;
-    LPCSTR pch;
-
-    c = 0;
-    while(cchUtf7 > 0)
+    static const signed char base64_decoding_table[] =
+    {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
+        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
+        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
+        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
+        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
+        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70-0x7F */
+    };
+
+    const char *source_end = src + srclen;
+    int dest_index = 0;
+
+    DWORD byte_pair = 0;
+    short offset = 0;
+
+    while (src < source_end)
     {
-        ch = *pszUtf7++;
-        if (ch == '+')
+        if (*src == '+')
         {
-            ch = *pszUtf7;
-            if (ch == '-')
+            src++;
+            if (src >= source_end)
+                break;
+
+            if (*src == '-')
             {
-                c++;
-                pszUtf7++;
-                cchUtf7 -= 2;
+                /* just a plus sign escaped as +- */
+                if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
+                {
+                    SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                    return 0;
+                }
+                src++;
                 continue;
             }
-            cchUtf7--;
-            pch = pszUtf7;
-            while(cchUtf7 > 0 && (BYTE) *pszUtf7 < 0x80 && 
-                  base64inv[*pszUtf7] >= 0)
-            {
-                cchUtf7--;
-                pszUtf7++;
-            }
-            cch = pszUtf7 - pch;
-            n = (cch * 3) / 8;
-            c += n;
-            if (cchUtf7 > 0 && *pszUtf7 == '-')
-            {
-                pszUtf7++;
-                cchUtf7--;
-            }
-        }
-        else
-        {
-            c++;
-            cchUtf7--;
-        }
-    }
 
-    return c;
-}
+            do
+            {
+                signed char sextet = *src;
+                if (sextet == '-')
+                {
+                    /* skip over the dash and end base64 decoding
+                     * the current, unfinished byte pair is discarded */
+                    src++;
+                    offset = 0;
+                    break;
+                }
+                if (sextet < 0)
+                {
+                    /* the next character of src is < 0 and therefore not part of a base64 sequence
+                     * the current, unfinished byte pair is NOT discarded in this case
+                     * this is probably a bug in Windows */
+                    break;
+                }
 
-static INT Utf7ToWideChar(LPCSTR pszUtf7, INT cchUtf7, LPWSTR pszWide, INT cchWide)
-{
-    INT n, c, cch;
-    CHAR ch;
-    LPCSTR pch;
-    WORD *pwsz;
+                sextet = base64_decoding_table[sextet];
+                if (sextet == -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 processed
+                     * the current, unfinished byte pair is discarded */
+                    offset = 0;
+                    break;
+                }
 
-    c = Utf7ToWideCharSize(pszUtf7, cchUtf7);
-    if (cchWide == 0)
-        return c;
+                byte_pair = (byte_pair << 6) | sextet;
+                offset += 6;
 
-    if (cchWide < c)
-    {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return 0;
-    }
+                if (offset >= 16)
+                {
+                    /* this byte pair is done */
+                    if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
+                    {
+                        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                        return 0;
+                    }
+                    offset -= 16;
+                }
 
-    while(cchUtf7 > 0)
-    {
-        ch = *pszUtf7++;
-        if (ch == '+')
-        {
-            if (*pszUtf7 == '-')
-            {
-                *pszWide++ = L'+';
-                pszUtf7++;
-                cchUtf7 -= 2;
-                continue;
-            }
-            cchUtf7--;
-            pch = pszUtf7;
-            while(cchUtf7 > 0 && (BYTE) *pszUtf7 < 0x80 && 
-                  base64inv[*pszUtf7] >= 0)
-            {
-                cchUtf7--;
-                pszUtf7++;
-            }
-            cch = pszUtf7 - pch;
-            n = (cch * 3) / 8;
-            pwsz = (WORD *) HeapAlloc(GetProcessHeap(), 0, (n + 1) * sizeof(WORD));
-            if (pwsz == NULL)
-                return 0;
-            ZeroMemory(pwsz, n * sizeof(WORD));
-            Utf7Base64Decode((BYTE *) pwsz, pch, cch);
-            myswab(pwsz, n);
-            CopyMemory(pszWide, pwsz, n * sizeof(WORD));
-            HeapFree(GetProcessHeap(), 0, pwsz);
-            pszWide += n;
-            if (cchUtf7 > 0 && *pszUtf7 == '-')
-            {
-                pszUtf7++;
-                cchUtf7--;
+                src++;
             }
+            while (src < source_end);
         }
         else
         {
-            *pszWide++ = (WCHAR) ch;
-            cchUtf7--;
+            /* we have to convert to unsigned char in case *src < 0 */
+            if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
+            {
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return 0;
+            }
+            src++;
         }
     }
 
-    return c;
+    return dest_index;
 }
 
 /**
@@ -1465,8 +1450,9 @@ MultiByteToWideChar(UINT CodePage,
 {
     /* Check the parameters. */
     if (MultiByteString == NULL ||
-        (WideCharString == NULL && WideCharCount > 0) ||
-        (PVOID)MultiByteString == (PVOID)WideCharString)
+        MultiByteCount == 0 || WideCharCount < 0 ||
+        (WideCharCount && (WideCharString == NULL ||
+        (PVOID)MultiByteString == (PVOID)WideCharString)))
     {
         SetLastError(ERROR_INVALID_PARAMETER);
         return 0;
@@ -1512,164 +1498,131 @@ MultiByteToWideChar(UINT CodePage,
     }
 }
 
-static const char mustshift[] =
+static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
 {
-    0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
-    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
-    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1
-};
-
-static const char base64[] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    static const BOOL directly_encodable_table[] =
+    {
+        1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
+        1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1                 /* 0x70 - 0x7A */
+    };
+
+    return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
+}
 
-static INT WideCharToUtf7Size(LPCWSTR pszWide, INT cchWide)
+static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
 {
-    WCHAR wch;
-    INT c = 0;
-    BOOL fShift = FALSE;
-
-    while(cchWide > 0)
+    if (dstlen > 0)
     {
-        wch = *pszWide;
-        if (wch < 0x80 && !mustshift[wch])
-        {
-            c++;
-            cchWide--;
-            pszWide++;
-        }
-        else
-        {
-            if (wch == L'+')
-            {
-                c++;
-                c++;
-                cchWide--;
-                pszWide++;
-                continue;
-            }
-            if (!fShift)
-            {
-                c++;
-                fShift = TRUE;
-            }
-            pszWide++;
-            cchWide--;
-            c += 3;
-            if (cchWide > 0 && (*pszWide >= 0x80 || mustshift[*pszWide]))
-            {
-                pszWide++;
-                cchWide--;
-                c += 3;
-                if (cchWide > 0 && (*pszWide >= 0x80 || mustshift[*pszWide]))
-                {
-                    pszWide++;
-                    cchWide--;
-                    c += 2;
-                }
-            }
-            if (cchWide > 0 && *pszWide < 0x80 && !mustshift[*pszWide])
-            {
-                c++;
-                fShift = FALSE;
-            }
-        }
+        if (*index >= dstlen)
+            return FALSE;
+
+        dst[*index] = character;
     }
-    if (fShift)
-        c++;
 
-    return c;
+    (*index)++;
+
+    return TRUE;
 }
 
-static INT WideCharToUtf7(LPCWSTR pszWide, INT cchWide, LPSTR pszUtf7, INT cchUtf7)
+static INT WideCharToUtf7(const WCHAR *src, int srclen, char *dst, int dstlen)
 {
-    WCHAR wch;
-    INT c, n;
-    WCHAR wsz[3];
-    BOOL fShift = FALSE;
+    static const char base64_encoding_table[] =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-    c = WideCharToUtf7Size(pszWide, cchWide);
-    if (cchUtf7 == 0)
-        return c;
-
-    if (cchUtf7 < c)
-    {
-        SetLastError(ERROR_INSUFFICIENT_BUFFER);
-        return 0;
-    }
+    const WCHAR *source_end = src + srclen;
+    int dest_index = 0;
 
-    while(cchWide > 0)
+    while (src < source_end)
     {
-        wch = *pszWide;
-        if (wch < 0x80 && !mustshift[wch])
+        if (*src == '+')
         {
-            *pszUtf7++ = (CHAR) wch;
-            cchWide--;
-            pszWide++;
+            if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
+            {
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return 0;
+            }
+            if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
+            {
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return 0;
+            }
+            src++;
         }
-        else
+        else if (utf7_can_directly_encode(*src))
         {
-            if (wch == L'+')
+            if (!utf7_write_c(dst, dstlen, &dest_index, *src))
             {
-                *pszUtf7++ = '+';
-                *pszUtf7++ = '-';
-                cchWide--;
-                pszWide++;
-                continue;
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return 0;
             }
-            if (!fShift)
+            src++;
+        }
+        else
+        {
+            unsigned int offset = 0;
+            DWORD byte_pair = 0;
+
+            if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
             {
-                *pszUtf7++ = '+';
-                fShift = TRUE;
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return 0;
             }
-            wsz[0] = *pszWide++;
-            cchWide--;
-            n = 1;
-            if (cchWide > 0 && (*pszWide >= 0x80 || mustshift[*pszWide]))
+
+            while (src < source_end && !utf7_can_directly_encode(*src))
             {
-                wsz[1] = *pszWide++;
-                cchWide--;
-                n++;
-                if (cchWide > 0 && (*pszWide >= 0x80 || mustshift[*pszWide]))
+                byte_pair = (byte_pair << 16) | *src;
+                offset += 16;
+                while (offset >= 6)
                 {
-                    wsz[2] = *pszWide++;
-                    cchWide--;
-                    n++;
+                    if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
+                    {
+                        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                        return 0;
+                    }
+                    offset -= 6;
                 }
+                src++;
             }
-            *pszUtf7++ = base64[wsz[0] >> 10];
-            *pszUtf7++ = base64[(wsz[0] >> 4) & 0x3F];
-            *pszUtf7++ = base64[(wsz[0] << 2 | wsz[1] >> 14) & 0x3F];
-            if (n >= 2)
+
+            if (offset)
             {
-                *pszUtf7++ = base64[(wsz[1] >> 8) & 0x3F];
-                *pszUtf7++ = base64[(wsz[1] >> 2) & 0x3F];
-                *pszUtf7++ = base64[(wsz[1] << 4 | wsz[2] >> 12) & 0x3F];
-                if (n >= 3)
+                /* Windows won't create a padded base64 character if there's no room for the - sign
+                 * as well ; this is probably a bug in Windows */
+                if (dstlen > 0 && dest_index + 1 >= dstlen)
+                {
+                    SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                    return 0;
+                }
+
+                byte_pair <<= (6 - offset);
+                if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
                 {
-                    *pszUtf7++ = base64[(wsz[2] >> 6) & 0x3F];
-                    *pszUtf7++ = base64[wsz[2] & 0x3F];
+                    SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                    return 0;
                 }
             }
-            if (cchWide > 0 && *pszWide < 0x80 && !mustshift[*pszWide])
+
+            /* Windows always explicitly terminates the base64 sequence
+               even though RFC 2152 (page 3, rule 2) does not require this */
+            if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
             {
-                *pszUtf7++ = '-';
-                fShift = FALSE;
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return 0;
             }
         }
     }
-    if (fShift)
-        *pszUtf7 = '-';
 
-    return c;
+    return dest_index;
 }
 
-static BOOL
-GetLocalisedText(DWORD dwResId, WCHAR *lpszDest)
+DWORD
+GetLocalisedText(DWORD dwResId, WCHAR *lpszDest, DWORD dwDestSize)
 {
     HRSRC hrsrc;
     LCID lcid;
@@ -1693,6 +1646,16 @@ GetLocalisedText(DWORD dwResId, WCHAR *lpszDest)
                             (LPWSTR)RT_STRING,
                             MAKEINTRESOURCEW((dwId >> 4) + 1),
                             langId);
+
+    /* english fallback */
+    if(!hrsrc)
+    {
+        hrsrc = FindResourceExW(hCurrentModule,
+                            (LPWSTR)RT_STRING,
+                            MAKEINTRESOURCEW((dwId >> 4) + 1),
+                            MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
+    }
+
     if (hrsrc)
     {
         HGLOBAL hmem = LoadResource(hCurrentModule, hrsrc);
@@ -1701,18 +1664,32 @@ GetLocalisedText(DWORD dwResId, WCHAR *lpszDest)
         {
             const WCHAR *p;
             unsigned int i;
+            unsigned int len;
 
             p = LockResource(hmem);
+
             for (i = 0; i < (dwId & 0x0f); i++) p += *p + 1;
 
-            memcpy(lpszDest, p + 1, *p * sizeof(WCHAR));
+            if(dwDestSize == 0)
+                return *p + 1;
+
+            len = *p * sizeof(WCHAR);
+
+            if(len + sizeof(WCHAR) > dwDestSize)
+            {
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                return FALSE;
+            }
+
+            memcpy(lpszDest, p + 1, len);
             lpszDest[*p] = '\0';
 
             return TRUE;
         }
     }
 
-    DPRINT1("Could not get codepage name. dwResId = %ld\n", dwResId);
+    DPRINT1("Resource not found: dwResId = %lu\n", dwResId);
+    SetLastError(ERROR_INVALID_PARAMETER);
     return FALSE;
 }
 
@@ -1746,6 +1723,7 @@ GetCPInfo(UINT CodePage,
                 return TRUE;
         }
 
+        DPRINT1("Invalid CP!: %lx\n", CodePage);
         SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
@@ -1787,7 +1765,7 @@ GetCPInfoExW(UINT CodePage,
         {
             lpCPInfoEx->CodePage = CP_UTF7;
             lpCPInfoEx->UnicodeDefaultChar = 0x3f;
-            return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName);
+            return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName)) != 0;
         }
         break;
 
@@ -1795,7 +1773,7 @@ GetCPInfoExW(UINT CodePage,
         {
             lpCPInfoEx->CodePage = CP_UTF8;
             lpCPInfoEx->UnicodeDefaultChar = 0x3f;
-            return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName);
+            return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName)) != 0;
         }
 
         default:
@@ -1812,7 +1790,7 @@ GetCPInfoExW(UINT CodePage,
 
             lpCPInfoEx->CodePage = CodePageEntry->CodePageTable.CodePage;
             lpCPInfoEx->UnicodeDefaultChar = CodePageEntry->CodePageTable.UniDefaultChar;
-            return GetLocalisedText((DWORD)CodePage, lpCPInfoEx->CodePageName);
+            return GetLocalisedText(CodePageEntry->CodePageTable.CodePage, lpCPInfoEx->CodePageName, sizeof(lpCPInfoEx->CodePageName)) != 0;
         }
         break;
     }
@@ -1899,6 +1877,7 @@ WideCharToMultiByte(UINT CodePage,
 {
     /* Check the parameters. */
     if (WideCharString == NULL ||
+        WideCharCount == 0 ||
         (MultiByteString == NULL && MultiByteCount > 0) ||
         (PVOID)WideCharString == (PVOID)MultiByteString ||
         MultiByteCount < 0)
@@ -1916,6 +1895,11 @@ WideCharToMultiByte(UINT CodePage,
     switch (CodePage)
     {
         case CP_UTF8:
+            if (DefaultChar != NULL || UsedDefaultChar != NULL)
+            {
+                SetLastError(ERROR_INVALID_PARAMETER);
+                return 0;
+            }
             return IntWideCharToMultiByteUTF8(CodePage,
                                               Flags,
                                               WideCharString,
@@ -2158,5 +2142,5 @@ GetNLSVersion(IN NLS_FUNCTION Function,
     STUB;
     return TRUE;
 }
-    
+
 /* EOF */