Index: src/udcutils.pas
===================================================================
--- src/udcutils.pas	(revision 8402)
+++ src/udcutils.pas	(working copy)
@@ -189,9 +189,8 @@
 procedure InsertFirstItem(sLine: String; comboBox: TCustomComboBox);
 {en
    Compares two strings taking into account the numbers.
-   Strings must have tailing zeros (#0).
 }
-function StrFloatCmpW(str1, str2: PWideChar; CaseSensitivity: TCaseSensitivity): PtrInt;
+function StrFloatCmp(const str1, str2: String; CaseSensitivity: TCaseSensitivity): PtrInt;
 
 function EstimateRemainingTime(StartValue, CurrentValue, EndValue: Int64;
                                StartTime: TDateTime; CurrentTime: TDateTime;
@@ -807,7 +806,7 @@
 function CompareStrings(const s1, s2: String; Natural: Boolean; CaseSensitivity: TCaseSensitivity): PtrInt; inline;
 begin
   if Natural then
-    Result:= StrFloatCmpW(PWideChar(UTF8Decode(s1)), PWideChar(UTF8Decode(s2)), CaseSensitivity)
+    Result:= StrFloatCmp(s1, s2, CaseSensitivity)
   else
     begin
       case CaseSensitivity of
@@ -863,161 +862,157 @@
    Result := ord(pstr1[counter]) - ord(pstr2[counter]);
  end;
 
-function StrFloatCmpW(str1, str2: PWideChar; CaseSensitivity: TCaseSensitivity): PtrInt;
+const SORT_NATURAL = True;
+const SORT_SPECIAL = False;
+
+function StrFloatCmp(const str1, str2: String; CaseSensitivity: TCaseSensitivity): PtrInt;
+
+type
+  TCategory = (cNone, cNumber, cSpecial, cString);
+
+  TChunk = record
+    FullStr: String;
+    Str: String;
+    Category: TCategory;
+    PosStart: Integer;
+    PosEnd: Integer;
+  end;
+
 var
-  is_digit1, is_digit2: boolean;
-  string_result: ptrint = 0;
-  number_result: ptrint = 0;
-  number1_size: ptrint = 0;
-  number2_size: ptrint = 0;
-  str_cmp: function(const s1, s2: WideString): PtrInt;
+  Chunk1, Chunk2: TChunk;
 
-  function is_digit(c: widechar): boolean; inline;
+  function Categorize(c: Char): TCategory; inline;
   begin
-    result:= (c in ['0'..'9']);
+    if SORT_NATURAL and (c in ['0'..'9']) then
+      Result := cNumber
+    else if SORT_SPECIAL and (c in [' '..'/', ':'..'@', '['..'`', '{'..'~']) then
+      Result := cSpecial
+    else
+      Result := cString;
   end;
 
-  function is_point(c: widechar): boolean; inline;
+  procedure NextChunkInit(var Chunk: TChunk); inline;
   begin
-    result:= (c in [',', '.']);
+    Chunk.PosStart := Chunk.PosEnd;
+    if Chunk.PosStart > Length(Chunk.FullStr) then
+      Chunk.Category := cNone
+    else
+      Chunk.Category := Categorize(Chunk.FullStr[Chunk.PosStart]);
   end;
 
-begin
-  // Set up compare function
-  case CaseSensitivity of
-    cstNotSensitive: str_cmp:= @WideCompareText;
-    cstLocale:       str_cmp:= @WideCompareStr;
-    cstCharValue:    str_cmp:= @WideStrComp;
-    else
-      raise Exception.Create('Invalid CaseSensitivity parameter');
+  procedure FindChunk(var Chunk: TChunk); inline;
+  begin
+    Chunk.PosEnd := Chunk.PosStart;
+    repeat
+      inc(Chunk.PosEnd);
+    until (Chunk.PosEnd > Length(Chunk.FullStr)) or
+          (Categorize(Chunk.FullStr[Chunk.PosEnd]) <> Chunk.Category);
   end;
 
-  while (true) do
+  procedure FindSameCategoryChunks; inline;
   begin
-    // compare string part
-    while (true) do
-    begin
-      if str1^ = #0 then
-      begin
-        if str2^ <> #0 then
-          exit(-1)
-        else
-          exit(0);
-      end;
+    Chunk1.PosEnd := Chunk1.PosStart;
+    Chunk2.PosEnd := Chunk2.PosStart;
+    repeat
+      inc(Chunk1.PosEnd);
+      inc(Chunk2.PosEnd);
+    until (Chunk1.PosEnd > Length(Chunk1.FullStr)) or
+          (Chunk2.PosEnd > Length(Chunk2.FullStr)) or
+          (Categorize(Chunk1.FullStr[Chunk1.PosEnd]) <> Chunk1.Category) or
+          (Categorize(Chunk2.FullStr[Chunk2.PosEnd]) <> Chunk2.Category);
+  end;
 
-      if str2^ = #0 then
-      begin
-        if str1^ <> #0 then
-          exit(+1)
-        else
-          exit(0);
-      end;
+  procedure PrepareChunk(var Chunk: TChunk); inline;
+  begin
+    Chunk.Str := Copy(Chunk.FullStr, Chunk.PosStart, Chunk.PosEnd - Chunk.PosStart);
+  end;
 
-      is_digit1 := is_digit(str1^);
-      is_digit2 := is_digit(str2^);
+  procedure PrepareNumberChunk(var Chunk: TChunk); inline;
+  begin
+    while (Chunk.PosStart <= Length(Chunk.FullStr)) and
+          (Chunk.FullStr[Chunk.PosStart] = '0') do
+      inc(Chunk.PosStart);
+    PrepareChunk(Chunk);
+  end;
 
-      if (is_digit1 and is_digit2) then break;
+begin
+  Chunk1.FullStr := str1;
+  Chunk2.FullStr := str2;
+  Chunk1.PosEnd := 1;
+  Chunk2.PosEnd := 1;
 
-      string_result:= str_cmp(str1^, str2^);
+  NextChunkInit(Chunk1);
+  NextChunkInit(Chunk2);
 
-      if (string_result <> 0) then exit(string_result);
+  if (Chunk1.Category = cSpecial) and (Chunk2.Category = cSpecial) then
+    FindSameCategoryChunks
+  else
+  begin
+    FindChunk(Chunk1);
+    FindChunk(Chunk2);
+  end;
 
-      inc(str1);
-      inc(str2);
-    end;
+  if Chunk1.Category <> Chunk2.Category then
+    Chunk1.Category := cString;
 
-    // skip leading zeroes for number
-    while (str1^ = '0') do
-      inc(str1);
-    while (str2^ = '0') do
-      inc(str2);
+  while True do
+  begin
 
-    // compare number before decimal point
-    while (true) do
-    begin
-      is_digit1 := is_digit(str1^);
-      is_digit2 := is_digit(str2^);
-
-      if (not is_digit1 and not is_digit2) then
-        break;
-
-      if ((number_result = 0) and is_digit1 and is_digit2) then
-      begin
-        if (str1^ > str2^) then
-          number_result := +1
-        else if (str1^ < str2^) then
-          number_result := -1
-        else
-          number_result := 0;
-      end;
-
-      if (is_digit1) then
-      begin
-        inc(str1);
-        inc(number1_size);
-      end;
-
-      if (is_digit2) then
-      begin
-        inc(str2);
-        inc(number2_size);
-      end;
-    end;
-
-    if (number1_size <> number2_size) then
-      exit(number1_size - number2_size);
-
-    if (number_result <> 0) then
-      exit(number_result);
-
-    // if there is a decimal point, compare number after one
-    if (is_point(str1^) or is_point(str2^)) then
-    begin
-      if (is_point(str1^)) then
-        inc(str1);
-
-      if (is_point(str2^)) then
-        inc(str2);
-
-      while (true) do
-      begin
-        is_digit1 := is_digit(str1^);
-        is_digit2 := is_digit(str2^);
-
-        if (not is_digit1 and not is_digit2) then
-          break;
-
-        if (is_digit1 and not is_digit2) then
+    case Chunk1.Category of
+      cString:
         begin
-          while (str1^ = '0') do
-            inc(str1);
-
-          if (is_digit(str1^)) then
-            exit(+1)
-          else
-            break;
+          PrepareChunk(Chunk1);
+          PrepareChunk(Chunk2);
+          case CaseSensitivity of
+            cstNotSensitive: Result := WideCompareText(UTF8Decode(Chunk1.Str), UTF8Decode(Chunk2.Str));
+            cstLocale:       Result := WideCompareStr(UTF8Decode(Chunk1.Str), UTF8Decode(Chunk2.Str));
+            cstCharValue:    Result := WideStrComp(UTF8Decode(Chunk1.Str), UTF8Decode(Chunk2.Str));
+            else
+              raise Exception.Create('Invalid CaseSensitivity parameter');
+          end;
+          if Result <> 0 then
+            Exit;
         end;
-
-        if (is_digit2 and not is_digit1) then
+      cNumber:
         begin
-          while (str2^ = '0') do
-            inc(str2);
-
-          if (is_digit(str2^)) then
-            exit(-1)
-          else
-            break;
+          PrepareNumberChunk(Chunk1);
+          PrepareNumberChunk(Chunk2);
+          Result := Length(Chunk1.Str) - Length(Chunk2.Str);
+          if Result <> 0 then
+            Exit;
+          Result := CompareStr(Chunk1.Str, Chunk2.Str);
+          if Result <> 0 then
+            Exit;
         end;
+      cSpecial:
+        begin
+          PrepareChunk(Chunk1);
+          PrepareChunk(Chunk2);
+          Result := CompareStr(Chunk1.Str, Chunk2.Str);
+          if Result <> 0 then
+            Exit;
+        end;
+      cNone:
+        Exit(WideStrComp(UTF8Decode(str1), UTF8Decode(str2)));
+    end;
 
-        if (str1^ > str2^) then
-          exit(+1)
-        else if (str1^ < str2^) then
-          exit(-1);
+    NextChunkInit(Chunk1);
+    NextChunkInit(Chunk2);
 
-        inc(str1);
-        inc(str2);
-      end;
+    if Chunk1.Category <> Chunk2.Category then
+      if Chunk1.Category < Chunk2.Category then
+        Exit(-1)
+      else
+        Exit(1);
+
+    if Chunk1.Category = cSpecial then
+      FindSameCategoryChunks
+    else
+    begin
+      FindChunk(Chunk1);
+      FindChunk(Chunk2);
     end;
+
   end;
 end;
 
