Search code examples
linuxdelphiglibckylix

kylix AnsiCompareStr bug?


The following program tries to use AnsiCompareStr/Text:

program Project5;
{$APPTYPE CONSOLE}
uses StrUtils, SysUtils;

begin
  Writeln('Ord(''-'')=', Ord('-'));
  Writeln('Ord(''_'')=', Ord('_'));
  Writeln('AnsiCompareStr(''-'', ''_'')=', AnsiCompareStr('-', '_'));
  Writeln('AnsiCompareText(''-'', ''_'')=', AnsiCompareText('-', '_'));
  Writeln('WideCompareStr(''-'', ''_'')=', WideCompareStr('-', '_'));
  Writeln('WideCompareText(''-'', ''_'')=', WideCompareText('-', '_'));
  Writeln('CompareStr(''-'', ''_'')=', CompareStr('-', '_'));
  Writeln('CompareText(''-'', ''_'')=', CompareText('-', '_'));
  Readln;
end.

When compiled with Kylix, it outputs wrong results on CentOS 5/6 x64:

Ord('-')=45
Ord('_')=95
AnsiCompareStr('-', '_')=3   <--- should be negative
AnsiCompareText('-', '_')=3  <--- should be negative
WideCompareStr('-', '_')=3   <--- should be negative
WideCompareText('-', '_')=3  <--- should be negative
CompareStr('-', '_')=-50
CompareText('-', '_')=-50

I wonder why AnsiCompareStr/Text gives wrong results ?

PS: CompareStr is considered obsolete according to http://www.delphibasics.co.uk/RTL.asp?Name=CompareStr . AnsiCompareStr is considered to be the modern, Locale safe form of CompareStr according to http://www.delphibasics.co.uk/RTL.asp?Name=AnsiCompareStr .

PS: The locale is en_US.iso885915 and/or en_US.UTF8

PS: Kylix 3 (as Delphi 7 with CrossKylix)

function AnsiCompareStr(const S1, S2: string): Integer;
begin
{$IFDEF MSWINDOWS}
  Result := CompareString(LOCALE_USER_DEFAULT, 0, PChar(S1), Length(S1),
    PChar(S2), Length(S2)) - 2;
{$ENDIF}
{$IFDEF LINUX}
  // glibc 2.1.2 / 2.1.3 implementations of strcoll() and strxfrm()
  // have severe capacity limits.  Comparing two 100k strings may
  // exhaust the stack and kill the process.
  // Fixed in glibc 2.1.91 and later.
  Result := strcoll(PChar(S1), PChar(S2));
{$ENDIF}
end;

function AnsiCompareText(const S1, S2: string): Integer;
begin
{$IFDEF MSWINDOWS}
  Result := CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, PChar(S1),
    Length(S1), PChar(S2), Length(S2)) - 2;
{$ENDIF}
{$IFDEF LINUX}
  Result := WideCompareText(S1, S2);
{$ENDIF}
end;

Solution

  • AnsiCompareStr/Text are giving correct results. They reflect the ordering defined by the prevailing locale. For instance AnsiCompareStr is implemented with a simple call to strcoll from the glibc standard library.

    The problem is in your expectations. I'm not sure why you expect the results you do, but it's quite reasonable to expect that glibc implements collation correctly. The order implied by strcoll is the collation order for the prevailing locale, whatever that happens to be. You didn't state that in the question.

    Perhaps the clue is that you use the phrase locale safe. Well, CompareXXX are independent of locale, AnsiCompareXXX depend upon the locale. If you are looking for locale invariant ordering then AnsiCompareXXX are the wrong choice.