[rtl]
[reactos.git] / reactos / lib / rtl / unicode.c
index 07fb58c..bafe8c5 100644 (file)
 #define NDEBUG
 #include <debug.h>
 
+#include <wine/unicode.h>
+
 /* GLOBALS *******************************************************************/
 
 extern BOOLEAN NlsMbCodePageTag;
 extern BOOLEAN NlsMbOemCodePageTag;
 extern PUSHORT NlsLeadByteInfo;
 
+extern USHORT NlsOemDefaultChar;
+extern USHORT NlsUnicodeDefaultChar;
+
 /* FUNCTIONS *****************************************************************/
 
 /*
@@ -393,6 +398,45 @@ RtlFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
     }
 }
 
+
+/*
+ * @implemented
+ * 
+ * NOTES
+ *  Check the oem-string to match the uincoded-string.
+ *
+ *  Functions who convert unicode strings to oem strings will set a DefaultChar from 
+ *  the OemCodepage when the character are unknown. So check it against the unicode string
+ *  and return false when the unicode string not contain an TransDefaultChar.
+ */
+BOOLEAN
+NTAPI
+RtlpDidUnicodeToOemWork(IN PCUNICODE_STRING UnicodeString,
+                        IN POEM_STRING OemString)
+{
+   ULONG i = 0;
+
+   /* Go through all characters of a string */
+   while (i < OemString->Length)
+   {
+       /* Check if it got translated into '?', but source char
+          wasn't '?' equivalent */
+       if ((OemString->Buffer[i] != 0) &&
+           (OemString->Buffer[i] == NlsOemDefaultChar) &&
+           (UnicodeString->Buffer[i] != NlsUnicodeDefaultChar))
+       {
+           /* Yes, it means unmappable characters were found */
+           return FALSE;
+       }
+
+       /* Move to the next char */
+       i++;
+   }
+
+   /* All chars were translated successfuly */
+   return TRUE;
+}
+
 /*
 * @unimplemented
 */
@@ -1057,12 +1101,14 @@ RtlUnicodeStringToOemString(
  */
 BOOLEAN
 NTAPI
-RtlIsTextUnicode( LPCVOID buf, INT len, INT *pf )
+RtlIsTextUnicode( PVOID buf, INT len, INT *pf )
 {
+    static const WCHAR std_control_chars[] = {'\r','\n','\t',' ',0x3000,0};
+    static const WCHAR byterev_control_chars[] = {0x0d00,0x0a00,0x0900,0x2000,0};
     const WCHAR *s = buf;
     int i;
-    unsigned int flags = ~0U, out_flags = 0;
-    
+    unsigned int flags = MAXULONG, out_flags = 0;
+
     if (len < sizeof(WCHAR))
     {
         /* FIXME: MSDN documents IS_TEXT_UNICODE_BUFFER_TOO_SMALL but there is no such thing... */
@@ -1077,21 +1123,21 @@ RtlIsTextUnicode( LPCVOID buf, INT len, INT *pf )
      * the output flags. But some of the tests are mutually
      * exclusive, so I don't see how you could pass all tests ...
      */
-    
+
     /* Check for an odd length ... pass if even. */
     if (len & 1) out_flags |= IS_TEXT_UNICODE_ODD_LENGTH;
-    
+
     if (((char *)buf)[len - 1] == 0)
         len--;  /* Windows seems to do something like that to avoid e.g. false IS_TEXT_UNICODE_NULL_BYTES  */
-    
+
     len /= sizeof(WCHAR);
     /* Windows only checks the first 256 characters */
     if (len > 256) len = 256;
-    
+
     /* Check for the special byte order unicode marks. */
     if (*s == 0xFEFF) out_flags |= IS_TEXT_UNICODE_SIGNATURE;
     if (*s == 0xFFFE) out_flags |= IS_TEXT_UNICODE_REVERSE_SIGNATURE;
-    
+
     /* apply some statistical analysis */
     if (flags & IS_TEXT_UNICODE_STATISTICS)
     {
@@ -1104,7 +1150,7 @@ RtlIsTextUnicode( LPCVOID buf, INT len, INT *pf )
         if (stats > len / 2)
             out_flags |= IS_TEXT_UNICODE_STATISTICS;
     }
-    
+
     /* Check for unicode NULL chars */
     if (flags & IS_TEXT_UNICODE_NULL_BYTES)
     {
@@ -1117,7 +1163,31 @@ RtlIsTextUnicode( LPCVOID buf, INT len, INT *pf )
             }
         }
     }
-    
+
+    if (flags & IS_TEXT_UNICODE_CONTROLS)
+    {
+        for (i = 0; i < len; i++)
+        {
+            if (strchrW(std_control_chars, s[i]))
+            {
+                out_flags |= IS_TEXT_UNICODE_CONTROLS;
+                break;
+            }
+        }
+    }
+
+    if (flags & IS_TEXT_UNICODE_REVERSE_CONTROLS)
+    {
+        for (i = 0; i < len; i++)
+        {
+            if (strchrW(byterev_control_chars, s[i]))
+            {
+                out_flags |= IS_TEXT_UNICODE_REVERSE_CONTROLS;
+                break;
+            }
+        }
+    }
+
     if (pf)
     {
         out_flags &= *pf;
@@ -1154,29 +1224,35 @@ RtlOemStringToCountedUnicodeString(
 
     PAGED_CODE_RTL();
 
+    /* Calculate size of the string */
     Length = RtlOemStringToCountedUnicodeSize(OemSource);
 
+    /* If it's 0 then zero out dest string and return */
     if (!Length)
     {
         RtlZeroMemory(UniDest, sizeof(UNICODE_STRING));
         return STATUS_SUCCESS;
     }
 
+    /* Check if length is a sane value */
     if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;
 
+    /* Store it in dest string */
     UniDest->Length = (USHORT)Length;
 
+    /* If we're asked to alloc the string - do so */
     if (AllocateDestinationString)
     {
         UniDest->Buffer = RtlpAllocateStringMemory(Length, TAG_USTR);
         UniDest->MaximumLength = Length;
         if (!UniDest->Buffer) return STATUS_NO_MEMORY;
     }
-    else if (UniDest->Length >= UniDest->MaximumLength)
+    else if (UniDest->Length > UniDest->MaximumLength)
     {
         return STATUS_BUFFER_OVERFLOW;
     }
 
+    /* Do the conversion */
     Status = RtlOemToUnicodeN(UniDest->Buffer,
                               UniDest->Length,
                               &Index,
@@ -1185,6 +1261,7 @@ RtlOemStringToCountedUnicodeString(
 
     if (!NT_SUCCESS(Status) && AllocateDestinationString)
     {
+        /* Conversion failed, free dest string and return status code */
         RtlpFreeStringMemory(UniDest->Buffer, TAG_USTR);
         UniDest->Buffer = NULL;
         return Status;
@@ -1464,38 +1541,48 @@ RtlUnicodeStringToCountedOemString(
 
     PAGED_CODE_RTL();
 
+    /* Calculate size of the string */
     Length = RtlUnicodeStringToCountedOemSize(UniSource);
 
+    /* If it's 0 then zero out dest string and return */
     if (!Length)
     {
         RtlZeroMemory(OemDest, sizeof(OEM_STRING));
+        return STATUS_SUCCESS;
     }
 
+    /* Check if length is a sane value */
     if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;
 
+    /* Store it in dest string */
     OemDest->Length = (USHORT)Length;
 
+    /* If we're asked to alloc the string - do so */
     if (AllocateDestinationString)
     {
         OemDest->Buffer = RtlpAllocateStringMemory(Length, TAG_OSTR);
         OemDest->MaximumLength = Length;
         if (!OemDest->Buffer) return STATUS_NO_MEMORY;
     }
-    else if (OemDest->Length >= OemDest->MaximumLength)
+    else if (OemDest->Length > OemDest->MaximumLength)
     {
         return STATUS_BUFFER_OVERFLOW;
     }
 
+    /* Do the conversion */
     Status = RtlUnicodeToOemN(OemDest->Buffer,
                               OemDest->Length,
                               &Index,
                               UniSource->Buffer,
                               UniSource->Length);
 
-    /* FIXME: Special check needed and return STATUS_UNMAPPABLE_CHARACTER */
+    /* Check for unmapped character */
+    if (NT_SUCCESS(Status) && !RtlpDidUnicodeToOemWork(UniSource, OemDest))
+        Status = STATUS_UNMAPPABLE_CHARACTER;
 
     if (!NT_SUCCESS(Status) && AllocateDestinationString)
     {
+        /* Conversion failed, free dest string and return status code */
         RtlpFreeStringMemory(OemDest->Buffer, TAG_OSTR);
         OemDest->Buffer = NULL;
         return Status;
@@ -1515,56 +1602,67 @@ RtlLargeIntegerToChar(
    IN ULONG  Length,
    IN OUT PCHAR  String)
 {
-   NTSTATUS Status = STATUS_SUCCESS;
-   ULONG Radix;
-   CHAR  temp[65];
-   ULONGLONG v = Value->QuadPart;
-   ULONG i;
-   PCHAR tp;
-   PCHAR sp;
+    ULONGLONG Val = Value->QuadPart;
+    NTSTATUS Status = STATUS_SUCCESS;
+    CHAR Buffer[65];
+    CHAR Digit;
+    ULONG Len;
+    PCHAR Pos;
 
-   Radix = Base;
-   if (Radix == 0)
-      Radix = 10;
+    if (Base == 0) Base = 10;
 
-   if ((Radix != 2) && (Radix != 8) &&
-         (Radix != 10) && (Radix != 16))
-      return STATUS_INVALID_PARAMETER;
+    if ((Base != 2) && (Base != 8) &&
+        (Base != 10) && (Base != 16))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
 
-   tp = temp;
-   while (v || tp == temp)
-   {
-      i = v % Radix;
-      v = v / Radix;
-      if (i < 10)
-         *tp = i + '0';
-      else
-         *tp = i + 'A' - 10;
-      tp++;
-   }
+    Pos = &Buffer[64];
+    *Pos = '\0';
 
-   if ((ULONG)((ULONG_PTR)tp - (ULONG_PTR)temp) > Length)
-      return STATUS_BUFFER_OVERFLOW;
+    do
+    {
+        Pos--;
+        Digit = Val % Base;
+        Val = Val / Base;
+        if (Digit < 10)
+            *Pos = '0' + Digit;
+        else
+            *Pos = 'A' + Digit - 10;
+    }
+    while (Val != 0L);
 
-   //_SEH_TRY
-   {
-      sp = String;
-      while (tp > temp)
-         *sp++ = *--tp;
+    Len = &Buffer[64] - Pos;
+
+    if (Len > Length)
+        return STATUS_BUFFER_OVERFLOW;
+
+#if 1 /* It needs to be removed, when will probably use SEH in rtl */
+    if (String == NULL)
+    {
+        return STATUS_ACCESS_VIOLATION;
+    }
+#endif
 
-      if((ULONG)((ULONG_PTR)sp - (ULONG_PTR)String) < Length)
-         *sp = 0;
-   }
 #if 0
-   _SEH_HANDLE
-   {
-      /* Get the error code */
-      Status = _SEH_GetExceptionCode();
-   }
-   _SEH_END;
+    _SEH2_TRY
+    {
+#endif
+        if (Len == Length)
+            RtlCopyMemory(String, Pos, Len);
+        else
+            RtlCopyMemory(String, Pos, Len + 1);
+#if 0
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Get the error code */
+        Status = _SEH2_GetExceptionCode();
+    }
+    _SEH2_END;
 #endif
 
-   return Status;
+    return Status;
 }
 
 /*
@@ -1708,7 +1806,9 @@ RtlUpcaseUnicodeStringToCountedOemString(
                                     UniSource->Buffer,
                                     UniSource->Length);
 
-    /* FIXME: Special check needed and return STATUS_UNMAPPABLE_CHARACTER */
+    /* Check for unmapped characters */
+    if (NT_SUCCESS(Status) && !RtlpDidUnicodeToOemWork(UniSource, OemDest))
+        Status = STATUS_UNMAPPABLE_CHARACTER;
 
     if (!NT_SUCCESS(Status) && AllocateDestinationString)
     {
@@ -1761,7 +1861,9 @@ RtlUpcaseUnicodeStringToOemString (
                                     UniSource->Buffer,
                                     UniSource->Length);
 
-    /* FIXME: Special check needed and return STATUS_UNMAPPABLE_CHARACTER */
+    /* Check for unmapped characters */
+    if (NT_SUCCESS(Status) && !RtlpDidUnicodeToOemWork(UniSource, OemDest))
+        Status = STATUS_UNMAPPABLE_CHARACTER;
 
     if (!NT_SUCCESS(Status) && AllocateDestinationString)
     {