Fireball patch for fixing RtlUnicodeStringToInteger and RtlCharToInteger
authorMagnus Olsen <magnus@greatlord.com>
Sat, 7 May 2005 22:17:03 +0000 (22:17 +0000)
committerMagnus Olsen <magnus@greatlord.com>
Sat, 7 May 2005 22:17:03 +0000 (22:17 +0000)
<Fireball> Imported WINE's RtlUnicodeStringToInteger and RtlCharToInteger - they were seriously broken in our revision. Thanks to GreatLord for his help pointing out this bug with me :)

svn path=/trunk/; revision=15092

reactos/lib/rtl/unicode.c

index ee485f6..483a579 100644 (file)
@@ -154,51 +154,96 @@ RtlAppendUnicodeStringToString(
 }
 
 
-/*
+/**************************************************************************
+ *      RtlCharToInteger   (NTDLL.@)
  * @implemented
+ * Converts a character string into its integer equivalent.
+ *
+ * RETURNS
+ *  Success: STATUS_SUCCESS. value contains the converted number
+ *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
+ *           STATUS_ACCESS_VIOLATION, if value is NULL.
+ *
+ * NOTES
+ *  For base 0 it uses 10 as base and the string should be in the format
+ *      "{whitespace} [+|-] [0[x|o|b]] {digits}".
+ *  For other bases the string should be in the format
+ *      "{whitespace} [+|-] {digits}".
+ *  No check is made for value overflow, only the lower 32 bits are assigned.
+ *  If str is NULL it crashes, as the native function does.
+ *
+ * DIFFERENCES
+ *  This function does not read garbage behind '\0' as the native version does.
  */
 NTSTATUS
 STDCALL
 RtlCharToInteger(
-   IN PCSZ String,
-   IN ULONG Base,
-   IN OUT PULONG Value)
-{
-   ULONG Val;
-
-   *Value = 0;
-
-   if (Base == 0)
-   {
-      Base = 10;
-      if (*String == '0')
-      {
-         Base = 8;
-         String++;
-         if ((*String == 'x') && isxdigit (String[1]))
-         {
-            String++;
-            Base = 16;
-         }
-      }
-   }
-
-   if (!isxdigit (*String))
-      return STATUS_INVALID_PARAMETER;
-
-   while (isxdigit (*String) &&
-          (Val = isdigit (*String) ? * String - '0' : (islower (*String)
-                 ? toupper (*String) : *String) - 'A' + 10) < Base)
-   {
-      *Value = *Value * Base + Val;
-      String++;
-   }
-
-   return STATUS_SUCCESS;
+    PCSZ str,      /* [I] '\0' terminated single-byte string containing a number */
+    ULONG base,    /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
+    PULONG value)  /* [O] Destination for the converted value */
+{
+    CHAR chCurrent;
+    int digit;
+    ULONG RunningTotal = 0;
+    char bMinus = 0;
+
+    while (*str != '\0' && *str <= ' ') {
+       str++;
+    } /* while */
+
+    if (*str == '+') {
+       str++;
+    } else if (*str == '-') {
+       bMinus = 1;
+       str++;
+    } /* if */
+
+    if (base == 0) {
+       base = 10;
+       if (str[0] == '0') {
+           if (str[1] == 'b') {
+               str += 2;
+               base = 2;
+           } else if (str[1] == 'o') {
+               str += 2;
+               base = 8;
+           } else if (str[1] == 'x') {
+               str += 2;
+               base = 16;
+           } /* if */
+       } /* if */
+    } else if (base != 2 && base != 8 && base != 10 && base != 16) {
+       return STATUS_INVALID_PARAMETER;
+    } /* if */
+
+    if (value == NULL) {
+       return STATUS_ACCESS_VIOLATION;
+    } /* if */
+
+    while (*str != '\0') {
+       chCurrent = *str;
+       if (chCurrent >= '0' && chCurrent <= '9') {
+           digit = chCurrent - '0';
+       } else if (chCurrent >= 'A' && chCurrent <= 'Z') {
+           digit = chCurrent - 'A' + 10;
+       } else if (chCurrent >= 'a' && chCurrent <= 'z') {
+           digit = chCurrent - 'a' + 10;
+       } else {
+           digit = -1;
+       } /* if */
+       if (digit < 0 || digit >= base) {
+           *value = bMinus ? -RunningTotal : RunningTotal;
+           return STATUS_SUCCESS;
+       } /* if */
+
+       RunningTotal = RunningTotal * base + digit;
+       str++;
+    } /* while */
+
+    *value = bMinus ? -RunningTotal : RunningTotal;
+    return STATUS_SUCCESS;
 }
 
-
-
 /*
  * @implemented
  */
@@ -807,113 +852,113 @@ RtlPrefixUnicodeString(
    return FALSE;
 }
 
-
-
-/*
+/**************************************************************************
+ *      RtlUnicodeStringToInteger (NTDLL.@)
  * @implemented
+ * Converts an unicode string into its integer equivalent.
  *
- * Note that regardless of success or failure status, we should leave the
- * partial value in Value.  An error is never returned based on the chars
- * in the string.
+ * RETURNS
+ *  Success: STATUS_SUCCESS. value contains the converted number
+ *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
+ *           STATUS_ACCESS_VIOLATION, if value is NULL.
+ *
+ * NOTES
+ *  For base 0 it uses 10 as base and the string should be in the format
+ *      "{whitespace} [+|-] [0[x|o|b]] {digits}".
+ *  For other bases the string should be in the format
+ *      "{whitespace} [+|-] {digits}".
+ *  No check is made for value overflow, only the lower 32 bits are assigned.
+ *  If str is NULL it crashes, as the native function does.
  *
- * This function does check the base.  Only 2, 8, 10, 16 are permitted,
- * else STATUS_INVALID_PARAMETER is returned.
+ *  Note that regardless of success or failure status, we should leave the
+ *  partial value in Value.  An error is never returned based on the chars
+ *  in the string.
+ *
+ * DIFFERENCES
+ *  This function does not read garbage on string length 0 as the native
+ *  version does.
  */
 NTSTATUS
 STDCALL
 RtlUnicodeStringToInteger(
-   IN PUNICODE_STRING String,
-   IN ULONG  Base,
-   OUT PULONG  Value)
-{
-   PWCHAR Str;
-   ULONG lenmin = 0;
-   ULONG i;
-   ULONG Val;
-   BOOLEAN addneg = FALSE;
-   NTSTATUS Status = STATUS_SUCCESS;
-
-   *Value = 0;
-   Str = String->Buffer;
-
-   if( Base && Base != 2 && Base != 8 && Base != 10 && Base != 16 )
-       return STATUS_INVALID_PARAMETER;
-
-   for (i = 0; i < String->Length / sizeof(WCHAR); i++)
-   {
-      if (*Str == L'b')
-      {
-         Base = 2;
-         lenmin++;
-      }
-      else if (*Str == L'o')
-      {
-         Base = 8;
-         lenmin++;
-      }
-      else if (*Str == L'd')
-      {
-         Base = 10;
-         lenmin++;
-      }
-      else if (*Str == L'x')
-      {
-         Base = 16;
-         lenmin++;
-      }
-      else if (*Str == L'+')
-      {
-         lenmin++;
-      }
-      else if (*Str == L'-')
-      {
-         addneg = TRUE;
-         lenmin++;
-      }
-      else if ((*Str > L'1') && (Base == 2))
-      {
-         break;
-      }
-      else if (((*Str > L'7') || (*Str < L'0')) && (Base == 8))
-      {
-         break;
-      }
-      else if (((*Str > L'9') || (*Str < L'0')) && (Base == 10))
-      {
-         break;
-      }
-      else if (  ((*Str > L'9') || (*Str < L'0')) &&
-                 ((towupper (*Str) > L'F') || (towupper (*Str) < L'A')) &&
-                 (Base == 16))
-      {
-         break;
-      }
-      Str++;
-   }
-
-   Str = String->Buffer + lenmin;
-
-   if (Base == 0)
-      Base = 10;
-
-   while (iswxdigit (*Str) &&
-          (Val = 
-          iswdigit (*Str) ? 
-          *Str - L'0' : 
-          (towupper (*Str) - L'A' + 10)) < Base)
-   {
-      *Value = *Value * Base + Val;
-      Str++;
-   }
-
-   if (addneg == TRUE)
-      *Value *= -1;
-
-   return Status;
+    PUNICODE_STRING str, /* [I] Unicode string to be converted */
+    ULONG base,                /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
+    PULONG value)              /* [O] Destination for the converted value */
+{
+    LPWSTR lpwstr = str->Buffer;
+    USHORT CharsRemaining = str->Length / sizeof(WCHAR);
+    WCHAR wchCurrent;
+    int digit;
+    ULONG RunningTotal = 0;
+    char bMinus = 0;
+
+    while (CharsRemaining >= 1 && *lpwstr <= ' ') {
+       lpwstr++;
+       CharsRemaining--;
+    } /* while */
+
+    if (CharsRemaining >= 1) {
+       if (*lpwstr == '+') {
+           lpwstr++;
+           CharsRemaining--;
+       } else if (*lpwstr == '-') {
+           bMinus = 1;
+           lpwstr++;
+           CharsRemaining--;
+       } /* if */
+    } /* if */
+
+    if (base == 0) {
+       base = 10;
+       if (CharsRemaining >= 2 && lpwstr[0] == '0') {
+           if (lpwstr[1] == 'b') {
+               lpwstr += 2;
+               CharsRemaining -= 2;
+               base = 2;
+           } else if (lpwstr[1] == 'o') {
+               lpwstr += 2;
+               CharsRemaining -= 2;
+               base = 8;
+           } else if (lpwstr[1] == 'x') {
+               lpwstr += 2;
+               CharsRemaining -= 2;
+               base = 16;
+           } /* if */
+       } /* if */
+    } else if (base != 2 && base != 8 && base != 10 && base != 16) {
+       return STATUS_INVALID_PARAMETER;
+    } /* if */
+
+    if (value == NULL) {
+       return STATUS_ACCESS_VIOLATION;
+    } /* if */
+
+    while (CharsRemaining >= 1) {
+       wchCurrent = *lpwstr;
+       if (wchCurrent >= '0' && wchCurrent <= '9') {
+           digit = wchCurrent - '0';
+       } else if (wchCurrent >= 'A' && wchCurrent <= 'Z') {
+           digit = wchCurrent - 'A' + 10;
+       } else if (wchCurrent >= 'a' && wchCurrent <= 'z') {
+           digit = wchCurrent - 'a' + 10;
+       } else {
+           digit = -1;
+       } /* if */
+       if (digit < 0 || digit >= base) {
+           *value = bMinus ? -RunningTotal : RunningTotal;
+           return STATUS_SUCCESS;
+       } /* if */
+
+       RunningTotal = RunningTotal * base + digit;
+       lpwstr++;
+       CharsRemaining--;
+    } /* while */
+
+    *value = bMinus ? -RunningTotal : RunningTotal;
+    return STATUS_SUCCESS;
 }
 
 
-
 /*
  * @implemented
  *